1import * as Schema from "effect/Schema";
2
3import { NonNegativeInt, PositiveInt, TrimmedNonEmptyString } from "./baseSchemas.ts";
4import { HostPowerSnapshot } from "./background.ts";
5import { DesktopUpdateStateSchema } from "./ipc.ts";
6
7export const RESOURCE_MONITOR_PROTOCOL_VERSION = 3 as const;
8
9/** Whole-host capacity, independent of T3's process diagnostics. */
10export const HostResourcesSnapshot = Schema.Struct({
11 sampledAt: NonNegativeInt,
12 cpuUtilization: Schema.NullOr(Schema.Number.check(Schema.isBetween({ minimum: 0, maximum: 1 }))),
13 cpuCount: NonNegativeInt,
14 availableMemoryBytes: NonNegativeInt,
15 totalMemoryBytes: NonNegativeInt,
16});
17export type HostResourcesSnapshot = typeof HostResourcesSnapshot.Type;
18
19export const ResourceTelemetryIoSemantics = Schema.Literals([
20 "storage",
21 "logical",
22 "all-io",
23 "unavailable",
24]);
25export type ResourceTelemetryIoSemantics = typeof ResourceTelemetryIoSemantics.Type;
26
27export const ResourceTelemetryProcessCategory = Schema.Literals([
28 "server",
29 "server-child",
30 "provider-root",
31 "terminal-root",
32 "electron-main",
33 "electron-renderer",
34 "electron-gpu",
35 "electron-utility",
36 "resource-monitor",
37 "unknown-t3",
38]);
39export type ResourceTelemetryProcessCategory = typeof ResourceTelemetryProcessCategory.Type;
40
41export const ResourceTelemetrySourceStatus = Schema.Literals([
42 "starting",
43 "healthy",
44 "degraded",
45 "unavailable",
46 "stopped",
47]);
48export type ResourceTelemetrySourceStatus = typeof ResourceTelemetrySourceStatus.Type;
49
50export const ResourceTelemetryProcessIdentity = Schema.Struct({
51 pid: PositiveInt,
52 startTimeMs: NonNegativeInt,
53});
54export type ResourceTelemetryProcessIdentity = typeof ResourceTelemetryProcessIdentity.Type;
55
56export const ResourceMonitorExternalProcess = Schema.Struct({
57 pid: PositiveInt,
58 startTimeMs: Schema.optionalKey(NonNegativeInt),
59});
60export type ResourceMonitorExternalProcess = typeof ResourceMonitorExternalProcess.Type;
61
62export const ResourceMonitorCapabilities = Schema.Struct({
63 cumulativeCpuTime: Schema.Boolean,
64 currentCpuPercent: Schema.Boolean,
65 residentMemory: Schema.Boolean,
66 virtualMemory: Schema.Boolean,
67 ioBytes: Schema.Boolean,
68 processStartTime: Schema.Boolean,
69 processTree: Schema.Boolean,
70});
71export type ResourceMonitorCapabilities = typeof ResourceMonitorCapabilities.Type;
72
73export const ResourceMonitorProcessSample = Schema.Struct({
74 pid: PositiveInt,
75 ppid: NonNegativeInt,
76 startTimeMs: NonNegativeInt,
77 runTimeMs: NonNegativeInt,
78 name: Schema.String,
79 command: Schema.String,
80 status: Schema.String,
81 cpuPercent: Schema.Number,
82 cpuTimeMs: NonNegativeInt,
83 residentBytes: NonNegativeInt,
84 virtualBytes: NonNegativeInt,
85 ioReadBytes: NonNegativeInt,
86 ioWriteBytes: NonNegativeInt,
87 ioSemantics: Schema.Literals(["storage", "all-io"]),
88});
89export type ResourceMonitorProcessSample = typeof ResourceMonitorProcessSample.Type;
90
91export const ResourceMonitorConfigureCommand = Schema.Struct({
92 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
93 type: Schema.Literal("configure"),
94 rootPid: PositiveInt,
95 sampleIntervalMs: NonNegativeInt,
96 externalProcesses: Schema.Array(ResourceMonitorExternalProcess),
97});
98export type ResourceMonitorConfigureCommand = typeof ResourceMonitorConfigureCommand.Type;
99
100export const ResourceMonitorSetExternalProcessesCommand = Schema.Struct({
101 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
102 type: Schema.Literal("setExternalProcesses"),
103 processes: Schema.Array(ResourceMonitorExternalProcess),
104});
105export type ResourceMonitorSetExternalProcessesCommand =
106 typeof ResourceMonitorSetExternalProcessesCommand.Type;
107
108export const ResourceMonitorSampleNowCommand = Schema.Struct({
109 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
110 type: Schema.Literal("sampleNow"),
111 requestId: TrimmedNonEmptyString,
112});
113export type ResourceMonitorSampleNowCommand = typeof ResourceMonitorSampleNowCommand.Type;
114
115export const ResourceMonitorProcessTableCommand = Schema.Struct({
116 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
117 type: Schema.Literal("processTable"),
118 requestId: TrimmedNonEmptyString,
119});
120export type ResourceMonitorProcessTableCommand = typeof ResourceMonitorProcessTableCommand.Type;
121
122export const ResourceMonitorSetSampleIntervalCommand = Schema.Struct({
123 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
124 type: Schema.Literal("setSampleInterval"),
125 sampleIntervalMs: NonNegativeInt,
126});
127export type ResourceMonitorSetSampleIntervalCommand =
128 typeof ResourceMonitorSetSampleIntervalCommand.Type;
129
130export const ResourceMonitorSetStreamingCommand = Schema.Struct({
131 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
132 type: Schema.Literal("setStreaming"),
133 enabled: Schema.Boolean,
134});
135export type ResourceMonitorSetStreamingCommand = typeof ResourceMonitorSetStreamingCommand.Type;
136
137export const ResourceMonitorReadHistoryCommand = Schema.Struct({
138 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
139 type: Schema.Literal("readHistory"),
140 requestId: TrimmedNonEmptyString,
141 windowMs: NonNegativeInt,
142});
143export type ResourceMonitorReadHistoryCommand = typeof ResourceMonitorReadHistoryCommand.Type;
144
145export const ResourceMonitorShutdownCommand = Schema.Struct({
146 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
147 type: Schema.Literal("shutdown"),
148});
149export type ResourceMonitorShutdownCommand = typeof ResourceMonitorShutdownCommand.Type;
150
151export const ResourceMonitorCommand = Schema.Union([
152 ResourceMonitorConfigureCommand,
153 ResourceMonitorSetExternalProcessesCommand,
154 ResourceMonitorSetSampleIntervalCommand,
155 ResourceMonitorSetStreamingCommand,
156 ResourceMonitorSampleNowCommand,
157 ResourceMonitorProcessTableCommand,
158 ResourceMonitorReadHistoryCommand,
159 ResourceMonitorShutdownCommand,
160]);
161export type ResourceMonitorCommand = typeof ResourceMonitorCommand.Type;
162
163export const ResourceMonitorHelloEvent = Schema.Struct({
164 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
165 type: Schema.Literal("hello"),
166 sidecarVersion: TrimmedNonEmptyString,
167 sidecarPid: PositiveInt,
168 platform: TrimmedNonEmptyString,
169 arch: TrimmedNonEmptyString,
170 capabilities: ResourceMonitorCapabilities,
171});
172export type ResourceMonitorHelloEvent = typeof ResourceMonitorHelloEvent.Type;
173
174export const ResourceMonitorSnapshotEvent = Schema.Struct({
175 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
176 type: Schema.Literal("snapshot"),
177 sequence: NonNegativeInt,
178 sampledAtUnixMs: NonNegativeInt,
179 collectionDurationMicros: NonNegativeInt,
180 scannedProcessCount: NonNegativeInt,
181 retainedProcessCount: NonNegativeInt,
182 inaccessibleProcessCount: NonNegativeInt,
183 requestId: Schema.optionalKey(TrimmedNonEmptyString),
184 externalProcesses: Schema.optionalKey(Schema.Array(ResourceMonitorExternalProcess)),
185 processes: Schema.Array(ResourceMonitorProcessSample),
186});
187export type ResourceMonitorSnapshotEvent = typeof ResourceMonitorSnapshotEvent.Type;
188
189export const ResourceMonitorProcessTableEntry = Schema.Struct({
190 pid: PositiveInt,
191 ppid: NonNegativeInt,
192 name: Schema.String,
193});
194export type ResourceMonitorProcessTableEntry = typeof ResourceMonitorProcessTableEntry.Type;
195
196export const ResourceMonitorProcessTableEvent = Schema.Struct({
197 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
198 type: Schema.Literal("processTable"),
199 requestId: TrimmedNonEmptyString,
200 processes: Schema.Array(ResourceMonitorProcessTableEntry),
201});
202export type ResourceMonitorProcessTableEvent = typeof ResourceMonitorProcessTableEvent.Type;
203
204export const ResourceMonitorHistoryChunkEvent = Schema.Struct({
205 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
206 type: Schema.Literal("historyChunk"),
207 requestId: TrimmedNonEmptyString,
208 done: Schema.Boolean,
209 snapshots: Schema.Array(ResourceMonitorSnapshotEvent),
210});
211export type ResourceMonitorHistoryChunkEvent = typeof ResourceMonitorHistoryChunkEvent.Type;
212
213export const ResourceMonitorErrorEvent = Schema.Struct({
214 version: Schema.Literal(RESOURCE_MONITOR_PROTOCOL_VERSION),
215 type: Schema.Literal("error"),
216 code: TrimmedNonEmptyString,
217 message: TrimmedNonEmptyString,
218 recoverable: Schema.Boolean,
219});
220export type ResourceMonitorErrorEvent = typeof ResourceMonitorErrorEvent.Type;
221
222export const ResourceMonitorEvent = Schema.Union([
223 ResourceMonitorHelloEvent,
224 ResourceMonitorSnapshotEvent,
225 ResourceMonitorProcessTableEvent,
226 ResourceMonitorHistoryChunkEvent,
227 ResourceMonitorErrorEvent,
228]);
229export type ResourceMonitorEvent = typeof ResourceMonitorEvent.Type;
230
231export const DesktopElectronProcessType = Schema.Literals([
232 "Browser",
233 "Tab",
234 "Utility",
235 "Zygote",
236 "Sandbox helper",
237 "GPU",
238 "Pepper Plugin",
239 "Pepper Plugin Broker",
240 "Unknown",
241]);
242export type DesktopElectronProcessType = typeof DesktopElectronProcessType.Type;
243
244export const DesktopElectronProcessMetric = Schema.Struct({
245 pid: PositiveInt,
246 creationTimeMs: NonNegativeInt,
247 type: DesktopElectronProcessType,
248 name: Schema.optionalKey(Schema.String),
249 serviceName: Schema.optionalKey(Schema.String),
250 cpuPercent: Schema.Number,
251 cumulativeCpuSeconds: Schema.optionalKey(Schema.Number),
252 idleWakeupsPerSecond: Schema.Number,
253 workingSetBytes: NonNegativeInt,
254 peakWorkingSetBytes: NonNegativeInt,
255});
256export type DesktopElectronProcessMetric = typeof DesktopElectronProcessMetric.Type;
257
258const DesktopHostPowerSnapshot = Schema.Struct({
259 ...HostPowerSnapshot.fields,
260 updatedAt: Schema.DateTimeUtcFromString,
261});
262
263export const DesktopHostTelemetrySnapshot = Schema.Struct({
264 version: Schema.Literal(1),
265 type: Schema.Literal("desktopTelemetry"),
266 sequence: NonNegativeInt,
267 sampledAtUnixMs: NonNegativeInt,
268 electronPid: PositiveInt,
269 power: DesktopHostPowerSnapshot,
270 speedLimitPercent: Schema.OptionFromNullOr(Schema.Number),
271 electronProcesses: Schema.Array(DesktopElectronProcessMetric),
272});
273export type DesktopHostTelemetrySnapshot = typeof DesktopHostTelemetrySnapshot.Type;
274
275export const DesktopHostTelemetryHello = Schema.Struct({
276 version: Schema.Literal(1),
277 type: Schema.Literal("desktopTelemetryHello"),
278 electronPid: PositiveInt,
279});
280export type DesktopHostTelemetryHello = typeof DesktopHostTelemetryHello.Type;
281
282/** Terminal marker for a server-triggered desktop update run. */
283export const DesktopUpdateRemoteOutcome = Schema.Literals([
284 "ready-to-install",
285 "up-to-date",
286 "failed",
287]);
288export type DesktopUpdateRemoteOutcome = typeof DesktopUpdateRemoteOutcome.Type;
289
290/**
291 * Desktop main -> server: the desktop app's update state. Sent once when the
292 * backend attaches and again on every state change, so the server always
293 * knows whether the app on its machine can be updated and how a
294 * server-triggered run is progressing.
295 */
296export const DesktopUpdateStatusReport = Schema.Struct({
297 version: Schema.Literal(1),
298 type: Schema.Literal("desktopUpdateStatus"),
299 // Set while a server-triggered run owns the flow; absent for the attach
300 // snapshot and for locally driven update activity.
301 requestId: Schema.optionalKey(TrimmedNonEmptyString),
302 // Terminal marker for a server-triggered run; absent while it is working.
303 outcome: Schema.optionalKey(DesktopUpdateRemoteOutcome),
304 reason: Schema.optionalKey(TrimmedNonEmptyString),
305 state: DesktopUpdateStateSchema,
306});
307export type DesktopUpdateStatusReport = typeof DesktopUpdateStatusReport.Type;
308
309export const DesktopHostTelemetryMessage = Schema.Union([
310 DesktopHostTelemetryHello,
311 DesktopHostTelemetrySnapshot,
312 DesktopUpdateStatusReport,
313]);
314export type DesktopHostTelemetryMessage = typeof DesktopHostTelemetryMessage.Type;
315
316export const DesktopTelemetrySetDiagnosticsDemand = Schema.Struct({
317 version: Schema.Literal(1),
318 type: Schema.Literal("setDiagnosticsDemand"),
319 enabled: Schema.Boolean,
320});
321export type DesktopTelemetrySetDiagnosticsDemand = typeof DesktopTelemetrySetDiagnosticsDemand.Type;
322
323export const DesktopTelemetrySetHostPowerIntervals = Schema.Struct({
324 version: Schema.Literal(1),
325 type: Schema.Literal("setHostPowerIntervals"),
326 activeIntervalMs: PositiveInt,
327 idleIntervalMs: PositiveInt,
328});
329export type DesktopTelemetrySetHostPowerIntervals =
330 typeof DesktopTelemetrySetHostPowerIntervals.Type;
331
332/**
333 * Server -> desktop main: run the app's own update flow now (check ->
334 * download -> quit-and-install) with no local confirmation. The remote click
335 * on the machine that sent the RPC is the consent.
336 */
337export const DesktopTelemetryRequestDesktopUpdate = Schema.Struct({
338 version: Schema.Literal(1),
339 type: Schema.Literal("requestDesktopUpdate"),
340 requestId: TrimmedNonEmptyString,
341});
342export type DesktopTelemetryRequestDesktopUpdate = typeof DesktopTelemetryRequestDesktopUpdate.Type;
343
344export const DesktopTelemetryCommitDesktopUpdate = Schema.Struct({
345 version: Schema.Literal(1),
346 type: Schema.Literal("commitDesktopUpdate"),
347 requestId: TrimmedNonEmptyString,
348});
349export type DesktopTelemetryCommitDesktopUpdate = typeof DesktopTelemetryCommitDesktopUpdate.Type;
350
351export const DesktopTelemetryCancelDesktopUpdate = Schema.Struct({
352 version: Schema.Literal(1),
353 type: Schema.Literal("cancelDesktopUpdate"),
354 requestId: TrimmedNonEmptyString,
355});
356export type DesktopTelemetryCancelDesktopUpdate = typeof DesktopTelemetryCancelDesktopUpdate.Type;
357
358export const DesktopTelemetryControlMessage = Schema.Union([
359 DesktopTelemetrySetDiagnosticsDemand,
360 DesktopTelemetrySetHostPowerIntervals,
361 DesktopTelemetryRequestDesktopUpdate,
362 DesktopTelemetryCommitDesktopUpdate,
363 DesktopTelemetryCancelDesktopUpdate,
364]);
365export type DesktopTelemetryControlMessage = typeof DesktopTelemetryControlMessage.Type;
366
367export const ResourceTelemetryProcess = Schema.Struct({
368 identity: ResourceTelemetryProcessIdentity,
369 ppid: NonNegativeInt,
370 childPids: Schema.Array(PositiveInt),
371 depth: NonNegativeInt,
372 name: Schema.String,
373 command: Schema.String,
374 status: Schema.String,
375 category: ResourceTelemetryProcessCategory,
376 electronType: Schema.optionalKey(DesktopElectronProcessType),
377 electronServiceName: Schema.optionalKey(Schema.String),
378 cpuPercent: Schema.Number,
379 cpuTimeMs: NonNegativeInt,
380 residentBytes: NonNegativeInt,
381 peakResidentBytes: NonNegativeInt,
382 virtualBytes: NonNegativeInt,
383 ioReadBytes: NonNegativeInt,
384 ioWriteBytes: NonNegativeInt,
385 ioReadBytesPerSecond: Schema.Number,
386 ioWriteBytesPerSecond: Schema.Number,
387 ioSemantics: ResourceTelemetryIoSemantics,
388 idleWakeupsPerSecond: Schema.optionalKey(Schema.Number),
389 runTimeMs: NonNegativeInt,
390 firstSeenAt: Schema.DateTimeUtc,
391 lastSeenAt: Schema.DateTimeUtc,
392});
393export type ResourceTelemetryProcess = typeof ResourceTelemetryProcess.Type;
394
395export const ResourceTelemetryAggregate = Schema.Struct({
396 processCount: NonNegativeInt,
397 currentCpuPercent: Schema.Number,
398 cpuTimeMs: NonNegativeInt,
399 currentRssBytes: NonNegativeInt,
400 peakRssBytes: NonNegativeInt,
401 ioReadBytes: NonNegativeInt,
402 ioWriteBytes: NonNegativeInt,
403 ioReadBytesPerSecond: Schema.Number,
404 ioWriteBytesPerSecond: Schema.Number,
405 processStarts: NonNegativeInt,
406 processExits: NonNegativeInt,
407});
408export type ResourceTelemetryAggregate = typeof ResourceTelemetryAggregate.Type;
409
410export const ResourceTelemetryGroups = Schema.Struct({
411 backend: ResourceTelemetryAggregate,
412 electron: ResourceTelemetryAggregate,
413 monitor: ResourceTelemetryAggregate,
414 allT3: ResourceTelemetryAggregate,
415});
416export type ResourceTelemetryGroups = typeof ResourceTelemetryGroups.Type;
417
418export const ResourceTelemetrySourceHealth = Schema.Struct({
419 status: ResourceTelemetrySourceStatus,
420 lastSampleAt: Schema.Option(Schema.DateTimeUtc),
421 lastError: Schema.Option(TrimmedNonEmptyString),
422});
423export type ResourceTelemetrySourceHealth = typeof ResourceTelemetrySourceHealth.Type;
424
425export const ResourceTelemetryHealth = Schema.Struct({
426 native: ResourceTelemetrySourceHealth,
427 desktop: ResourceTelemetrySourceHealth,
428 sidecarVersion: Schema.Option(TrimmedNonEmptyString),
429 sidecarPid: Schema.Option(PositiveInt),
430 restartCount: NonNegativeInt,
431 collectionDurationMicros: NonNegativeInt,
432 scannedProcessCount: NonNegativeInt,
433 retainedProcessCount: NonNegativeInt,
434 inaccessibleProcessCount: NonNegativeInt,
435});
436export type ResourceTelemetryHealth = typeof ResourceTelemetryHealth.Type;
437
438export const ResourceAttributionEntry = Schema.Struct({
439 component: TrimmedNonEmptyString,
440 operation: TrimmedNonEmptyString,
441 logicalReadBytes: NonNegativeInt,
442 logicalWriteBytes: NonNegativeInt,
443 count: NonNegativeInt,
444 durationMs: NonNegativeInt,
445});
446export type ResourceAttributionEntry = typeof ResourceAttributionEntry.Type;
447
448export const ResourceAttributionSnapshot = Schema.Struct({
449 readAt: Schema.DateTimeUtc,
450 entries: Schema.Array(ResourceAttributionEntry),
451});
452export type ResourceAttributionSnapshot = typeof ResourceAttributionSnapshot.Type;
453
454export const ResourceTelemetrySnapshot = Schema.Struct({
455 readAt: Schema.DateTimeUtc,
456 sampleIntervalMs: NonNegativeInt,
457 processes: Schema.Array(ResourceTelemetryProcess),
458 groups: ResourceTelemetryGroups,
459 power: HostPowerSnapshot,
460 speedLimitPercent: Schema.Option(Schema.Number),
461 attribution: ResourceAttributionSnapshot,
462 health: ResourceTelemetryHealth,
463});
464export type ResourceTelemetrySnapshot = typeof ResourceTelemetrySnapshot.Type;
465
466export const ResourceTelemetryHistoryInput = Schema.Struct({
467 windowMs: NonNegativeInt,
468 bucketMs: NonNegativeInt,
469});
470export type ResourceTelemetryHistoryInput = typeof ResourceTelemetryHistoryInput.Type;
471
472export const ResourceTelemetryHistoryBucket = Schema.Struct({
473 startedAt: Schema.DateTimeUtc,
474 endedAt: Schema.DateTimeUtc,
475 avgCpuPercent: Schema.Number,
476 maxCpuPercent: Schema.Number,
477 maxRssBytes: NonNegativeInt,
478 ioReadBytes: NonNegativeInt,
479 ioWriteBytes: NonNegativeInt,
480 maxProcessCount: NonNegativeInt,
481});
482export type ResourceTelemetryHistoryBucket = typeof ResourceTelemetryHistoryBucket.Type;
483
484export const ResourceTelemetryProcessSummary = Schema.Struct({
485 identity: ResourceTelemetryProcessIdentity,
486 ppid: NonNegativeInt,
487 depth: NonNegativeInt,
488 name: Schema.String,
489 command: Schema.String,
490 category: ResourceTelemetryProcessCategory,
491 firstSeenAt: Schema.DateTimeUtc,
492 lastSeenAt: Schema.DateTimeUtc,
493 currentCpuPercent: Schema.Number,
494 avgCpuPercent: Schema.Number,
495 maxCpuPercent: Schema.Number,
496 cpuTimeMs: NonNegativeInt,
497 currentRssBytes: NonNegativeInt,
498 peakRssBytes: NonNegativeInt,
499 ioReadBytes: NonNegativeInt,
500 ioWriteBytes: NonNegativeInt,
501 ioSemantics: ResourceTelemetryIoSemantics,
502 sampleCount: NonNegativeInt,
503});
504export type ResourceTelemetryProcessSummary = typeof ResourceTelemetryProcessSummary.Type;
505
506export const ResourceTelemetryHistory = Schema.Struct({
507 readAt: Schema.DateTimeUtc,
508 windowMs: NonNegativeInt,
509 bucketMs: NonNegativeInt,
510 sampleIntervalMs: NonNegativeInt,
511 retainedSampleCount: NonNegativeInt,
512 buckets: Schema.Array(ResourceTelemetryHistoryBucket),
513 topProcesses: Schema.Array(ResourceTelemetryProcessSummary),
514 health: ResourceTelemetryHealth,
515});
516export type ResourceTelemetryHistory = typeof ResourceTelemetryHistory.Type;
517
518export const ResourceTelemetryRetryResult = Schema.Struct({
519 accepted: Schema.Boolean,
520 snapshot: ResourceTelemetrySnapshot,
521});
522export type ResourceTelemetryRetryResult = typeof ResourceTelemetryRetryResult.Type;