@myagentroam/protocol 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,714 @@
1
+ import { z } from 'zod';
2
+ export const healthStatusSchema = z.object({
3
+ status: z.literal('ok')
4
+ });
5
+ /** The only wire protocol version understood by this build. */
6
+ export const NODE_PROTOCOL_VERSION = 2;
7
+ export const WORKBENCH_PROTOCOL_VERSION = 1;
8
+ export const nodePlatformSchema = z.enum(['win32', 'linux']);
9
+ export const runnerAvailabilitySchema = z.object({
10
+ available: z.boolean(),
11
+ version: z.string().optional()
12
+ });
13
+ export const nodeCapabilitiesSchema = z.object({
14
+ platform: nodePlatformSchema,
15
+ architecture: z.string().min(1).max(128),
16
+ nodeVersion: z.string().min(1).max(128),
17
+ codex: runnerAvailabilitySchema,
18
+ claudeCode: runnerAvailabilitySchema
19
+ });
20
+ export const workspaceKindSchema = z.enum(['DIRECTORY', 'GIT_WORKSPACE']);
21
+ export const gitSummarySchema = z.object({
22
+ commonDir: z.string(),
23
+ head: z.string(),
24
+ branch: z.string().nullable(),
25
+ dirty: z.boolean(),
26
+ snapshot: z.string()
27
+ });
28
+ /** Runner names are deliberately closed in the first implementation. */
29
+ export const runnerNameSchema = z.enum(['codex', 'claude-code']);
30
+ /** Account-level quota visibility is intentionally separate from a Runner's execution availability. */
31
+ export const runnerUsageStatusSchema = z.object({
32
+ runner: runnerNameSchema,
33
+ available: z.boolean(),
34
+ reasonCode: z.string().min(1).max(80).nullable()
35
+ });
36
+ export const runnerUsageWindowSchema = z.object({
37
+ id: z.string().min(1).max(80),
38
+ label: z.string().min(1).max(128),
39
+ usedPercentage: z.number().finite().min(0).max(100),
40
+ resetsAt: z.number().int().nonnegative().nullable()
41
+ });
42
+ export const runnerUsageSchema = z.object({
43
+ runner: runnerNameSchema,
44
+ windows: z.array(runnerUsageWindowSchema).min(1).max(8),
45
+ fetchedAt: z.number().int().nonnegative(),
46
+ cached: z.boolean()
47
+ });
48
+ /** What the runner can do in an existing native conversation. */
49
+ export const runnerControlLevelSchema = z.enum(['OBSERVE', 'MESSAGE', 'INTERRUPT']);
50
+ export const runnerCapabilitiesSchema = z.object({
51
+ available: z.boolean(),
52
+ version: z.string().max(128).optional(),
53
+ controlLevel: runnerControlLevelSchema,
54
+ supportsResume: z.boolean(),
55
+ supportsExternalSession: z.boolean()
56
+ });
57
+ /** Fixed runner contracts; implementations are supplied by later runner tasks. */
58
+ export const runnerDefinitionSchema = z.object({
59
+ name: runnerNameSchema,
60
+ capabilities: runnerCapabilitiesSchema
61
+ });
62
+ export const runnerDefinitions = {
63
+ codex: {
64
+ name: 'codex',
65
+ capabilities: {
66
+ available: false,
67
+ controlLevel: 'INTERRUPT',
68
+ supportsResume: true,
69
+ supportsExternalSession: true
70
+ }
71
+ },
72
+ 'claude-code': {
73
+ name: 'claude-code',
74
+ capabilities: {
75
+ available: false,
76
+ controlLevel: 'INTERRUPT',
77
+ supportsResume: true,
78
+ supportsExternalSession: true
79
+ }
80
+ }
81
+ };
82
+ export const agentRunStatusSchema = z.enum([
83
+ 'QUEUED',
84
+ 'STARTING',
85
+ 'RUNNING',
86
+ 'CANCELLING',
87
+ 'SUCCEEDED',
88
+ 'FAILED',
89
+ 'CANCELLED',
90
+ 'INTERRUPTED'
91
+ ]);
92
+ /** Process-local Workspace FIFO projection; queue contents never enter SQLite. */
93
+ export const workspaceQueueItemSchema = z.object({
94
+ run: z.object({
95
+ id: z.string().min(1).max(128),
96
+ sessionId: z.string().min(1).max(128),
97
+ workspaceId: z.string().min(1).max(128),
98
+ runner: runnerNameSchema,
99
+ status: z.literal('QUEUED'),
100
+ clientMessageId: z.string().min(1).max(128),
101
+ createdAt: z.number().int().nonnegative()
102
+ }),
103
+ sessionId: z.string().min(1).max(128),
104
+ sessionTitle: z.string().max(160),
105
+ content: z.string().min(1).max(20_000)
106
+ });
107
+ export const workspaceQueueSchema = z.object({
108
+ version: z.number().int().nonnegative(),
109
+ paused: z.boolean(),
110
+ items: z.array(workspaceQueueItemSchema)
111
+ });
112
+ export const runEventSchema = z.object({
113
+ runId: z.string().min(1).max(128),
114
+ sequence: z.number().int().positive(),
115
+ eventType: z.string().regex(/^[A-Za-z0-9._-]{1,80}$/),
116
+ payload: z.unknown(),
117
+ status: agentRunStatusSchema.optional(),
118
+ version: z.number().int().nonnegative().optional()
119
+ });
120
+ /**
121
+ * Node-to-Workbench asynchronous notifications. This is deliberately a
122
+ * small envelope: business payloads remain owned by the Node and Server only
123
+ * authenticates and relays them to an Admin page connection.
124
+ */
125
+ export const workbenchEventCategorySchema = z.enum([
126
+ 'node',
127
+ 'session',
128
+ 'conversation',
129
+ 'run',
130
+ 'queue',
131
+ 'workspace'
132
+ ]);
133
+ export const workbenchEventSchema = z.object({
134
+ sequence: z.number().int().positive(),
135
+ category: workbenchEventCategorySchema,
136
+ payload: z.unknown()
137
+ });
138
+ const capabilityAvailabilityBaseSchema = z.object({
139
+ available: z.boolean(),
140
+ reasonCode: z.string().min(1).max(80).nullable()
141
+ });
142
+ function refineCapabilityAvailability(schema) {
143
+ return schema.superRefine((value, context) => {
144
+ if (value.available && value.reasonCode !== null) {
145
+ context.addIssue({ code: 'custom', message: 'available capability cannot have reasonCode' });
146
+ }
147
+ if (!value.available && value.reasonCode === null) {
148
+ context.addIssue({ code: 'custom', message: 'unavailable capability requires reasonCode' });
149
+ }
150
+ });
151
+ }
152
+ function capabilitySchemaWith(shape) {
153
+ return refineCapabilityAvailability(capabilityAvailabilityBaseSchema.extend(shape));
154
+ }
155
+ export const capabilityAvailabilitySchema = refineCapabilityAvailability(capabilityAvailabilityBaseSchema);
156
+ export const runnerCommandDescriptorSchema = z
157
+ .object({
158
+ id: z.string().min(1).max(128),
159
+ runner: runnerNameSchema,
160
+ label: z.string().min(1).max(128),
161
+ stateLabel: z.string().min(1).max(128).optional(),
162
+ description: z.string().max(500),
163
+ /** Commands are entered exactly as the Runner documents them: `/name args`. */
164
+ inputHint: z.string().max(200),
165
+ source: z.enum(['CODEX_APP_SERVER', 'CLAUDE_AGENT_SDK']),
166
+ interaction: z.enum(['RUNNER_TEXT', 'IMMEDIATE_ACTION', 'TOGGLE', 'VALUE']),
167
+ available: z.boolean(),
168
+ reasonCode: z.string().min(1).max(80).nullable()
169
+ })
170
+ .superRefine((value, context) => {
171
+ if (value.available && value.reasonCode !== null) {
172
+ context.addIssue({ code: 'custom', message: 'available command cannot have reasonCode' });
173
+ }
174
+ if (!value.available && value.reasonCode === null) {
175
+ context.addIssue({ code: 'custom', message: 'unavailable command requires reasonCode' });
176
+ }
177
+ });
178
+ export const runnerCommandStateSchema = z.object({
179
+ commandId: z.string().min(1).max(128),
180
+ active: z.boolean(),
181
+ label: z.string().min(1).max(128),
182
+ detail: z.string().max(500).nullable(),
183
+ closable: z.boolean(),
184
+ closeInput: z.string().min(2).max(20_000).nullable(),
185
+ /** A Node-declared, fixed continuation for a completed structured result. */
186
+ continuation: z
187
+ .object({
188
+ label: z.string().min(1).max(128),
189
+ prompt: z.string().min(1).max(20_000)
190
+ })
191
+ .nullable()
192
+ .optional()
193
+ });
194
+ export const runnerProfileSchema = z.object({
195
+ runner: runnerNameSchema,
196
+ models: z.array(z.object({
197
+ id: z.string().min(1).max(128),
198
+ label: z.string().min(1).max(128),
199
+ supportedEfforts: z.array(z.string().min(1).max(64)).max(32)
200
+ })),
201
+ accessOptions: z.array(z.object({
202
+ id: z.string().min(1).max(128),
203
+ label: z.string().min(1).max(128),
204
+ description: z.string().max(500)
205
+ })),
206
+ commands: z.array(runnerCommandDescriptorSchema),
207
+ capabilities: z.array(z.string().min(1).max(80)).max(64)
208
+ });
209
+ /** The last explicitly chosen Composer configuration belongs to one Node Runner. */
210
+ export const runnerDefaultConfigurationSchema = z.object({
211
+ runner: runnerNameSchema,
212
+ model: z.string().trim().min(1).max(128),
213
+ effort: z.string().trim().min(1).max(64),
214
+ access: z.string().trim().min(1).max(128)
215
+ });
216
+ /** A Runner command is selected from a Node-declared profile, never guessed from `/` text. */
217
+ export const runnerCommandExecutionSchema = z.object({
218
+ commandId: z.string().min(1).max(128),
219
+ input: z.string().min(2).max(20_000)
220
+ });
221
+ export const serverWorkbenchCapabilitiesSchema = z.object({
222
+ workbenchProtocolVersion: z.literal(WORKBENCH_PROTOCOL_VERSION),
223
+ conversationItemVersions: z.array(z.literal(1)).min(1),
224
+ supportsWorkspaceActivityStream: z.boolean(),
225
+ supportsSessionDetailStream: z.boolean(),
226
+ supportsTerminalBrowserRelay: z.boolean(),
227
+ supportsTerminalNodeDataChannel: z.boolean(),
228
+ maxSseSubscriptionsPerUser: z.number().int().positive().max(128),
229
+ maxTerminalBrowserConnectionsPerUser: z.number().int().nonnegative().max(32)
230
+ });
231
+ export const nodeWorkbenchCapabilitiesSchema = z.object({
232
+ manifestVersion: z.literal(1),
233
+ revision: z.number().int().nonnegative(),
234
+ observedAt: z.number().int().nonnegative(),
235
+ platform: z.enum(['windows', 'linux']),
236
+ architecture: z.string().min(1).max(128),
237
+ conversation: capabilitySchemaWith({
238
+ itemVersions: z.array(z.literal(1)),
239
+ supportsSessionCursor: z.boolean(),
240
+ supportsWorkspaceActivityCursor: z.boolean(),
241
+ supportsHistoryPaging: z.boolean(),
242
+ supportsItemFinalSnapshots: z.boolean(),
243
+ maxPageSize: z.number().int().positive().max(500)
244
+ }),
245
+ runners: z.array(runnerProfileSchema),
246
+ files: capabilitySchemaWith({
247
+ supportsList: z.boolean(),
248
+ supportsReadRange: z.boolean(),
249
+ supportsSearch: z.boolean(),
250
+ maxDirectoryPageSize: z.number().int().positive().max(1000),
251
+ maxReadBytes: z.number().int().positive(),
252
+ maxSearchResults: z.number().int().positive().max(10000)
253
+ }),
254
+ git: capabilitySchemaWith({
255
+ supportsCurrentChanges: z.boolean(),
256
+ supportsTurnChanges: z.boolean(),
257
+ maxDiffBytes: z.number().int().positive()
258
+ }),
259
+ terminal: capabilitySchemaWith({
260
+ supportsPty: z.boolean(),
261
+ supportsConPty: z.boolean(),
262
+ supportsReplay: z.boolean(),
263
+ supportsReadonlyAttach: z.boolean(),
264
+ supportsTakeover: z.boolean(),
265
+ maxTerminals: z.number().int().nonnegative().max(64),
266
+ maxReplayBytes: z.number().int().nonnegative(),
267
+ reconnectWindowMs: z.number().int().nonnegative(),
268
+ profiles: z.array(z.object({
269
+ id: z.string().min(1).max(128),
270
+ label: z.string().min(1).max(128),
271
+ isDefault: z.boolean()
272
+ }))
273
+ })
274
+ });
275
+ export const sessionRuntimeCapabilitySchema = z.enum([
276
+ 'history.read',
277
+ 'events.live',
278
+ 'turn.start',
279
+ 'turn.queue',
280
+ 'turn.interrupt',
281
+ 'run.cancel',
282
+ 'approval.respond',
283
+ 'user-input.respond',
284
+ 'session.rename',
285
+ 'session.remove',
286
+ 'session.pin'
287
+ ]);
288
+ export const sessionRuntimeCapabilitiesSchema = z.object({
289
+ level: z.enum(['MANAGED', 'SHARED_CONTROL', 'OBSERVE_ONLY', 'RESUMABLE', 'UNAVAILABLE']),
290
+ capabilities: z.array(sessionRuntimeCapabilitySchema),
291
+ reasonCode: z.string().min(1).max(80).nullable()
292
+ });
293
+ export const sessionActivityStateSchema = z.enum([
294
+ 'MANAGED_ACTIVE',
295
+ 'EXTERNAL_ACTIVE',
296
+ 'IDLE',
297
+ 'UNAVAILABLE'
298
+ ]);
299
+ export const eventCursorSchema = z.object({
300
+ scope: z.enum(['RUN', 'SESSION', 'WORKSPACE_ACTIVITY']),
301
+ streamId: z.string().min(1).max(128),
302
+ cursor: z.string().min(1).max(1024)
303
+ });
304
+ const conversationItemBaseSchema = z.object({
305
+ id: z.string().min(1).max(128),
306
+ sessionId: z.string().min(1).max(128),
307
+ turnId: z.string().min(1).max(128),
308
+ runId: z.string().min(1).max(128).nullable(),
309
+ parentItemId: z.string().min(1).max(128).nullable(),
310
+ sourceSequence: z.number().int().positive().nullable(),
311
+ revision: z.number().int().nonnegative(),
312
+ status: z.enum([
313
+ 'PENDING',
314
+ 'IN_PROGRESS',
315
+ 'COMPLETED',
316
+ 'FAILED',
317
+ 'DECLINED',
318
+ 'CANCELLED',
319
+ 'INTERRUPTED'
320
+ ]),
321
+ startedAt: z.number().int().nonnegative().nullable(),
322
+ completedAt: z.number().int().nonnegative().nullable()
323
+ });
324
+ export const contextReferenceSchema = z.object({
325
+ type: z.enum(['file', 'directory', 'changes', 'diff', 'turn', 'message', 'terminal_selection']),
326
+ referenceId: z.string().min(1).max(128),
327
+ label: z.string().min(1).max(500),
328
+ path: z.string().min(1).max(4096).optional(),
329
+ startLine: z.number().int().positive().optional(),
330
+ endLine: z.number().int().positive().optional()
331
+ });
332
+ const assistantMessageItemSchema = conversationItemBaseSchema.extend({
333
+ kind: z.literal('assistant_message'),
334
+ payload: z.object({
335
+ text: z.string(),
336
+ format: z.literal('markdown'),
337
+ model: z.string().max(128).nullable()
338
+ })
339
+ });
340
+ const userMessageItemSchema = conversationItemBaseSchema.extend({
341
+ kind: z.literal('user_message'),
342
+ payload: z.object({
343
+ clientMessageId: z.string().min(1).max(128).nullable(),
344
+ text: z.string(),
345
+ contexts: z.array(contextReferenceSchema).max(64).default([]),
346
+ delivery: z.enum(['PENDING', 'ACCEPTED', 'STEERED', 'QUEUED_NEXT_TURN', 'REJECTED'])
347
+ })
348
+ });
349
+ const planItemSchema = conversationItemBaseSchema.extend({
350
+ kind: z.literal('plan'),
351
+ payload: z.object({
352
+ explanation: z.string().nullable(),
353
+ steps: z.array(z.object({
354
+ id: z.string().min(1).max(128),
355
+ text: z.string(),
356
+ status: z.enum(['PENDING', 'IN_PROGRESS', 'COMPLETED'])
357
+ }))
358
+ })
359
+ });
360
+ const approvalItemSchema = conversationItemBaseSchema.extend({
361
+ kind: z.literal('approval'),
362
+ payload: z.object({
363
+ approvalId: z.string().min(1).max(128),
364
+ category: z.enum(['COMMAND', 'FILE_CHANGE', 'NETWORK', 'TOOL', 'OTHER']),
365
+ reason: z.string().nullable(),
366
+ expiresAt: z.number().int().nonnegative(),
367
+ resolvedDecision: z.string().nullable()
368
+ })
369
+ });
370
+ const summaryItemSchema = conversationItemBaseSchema.extend({
371
+ kind: z.literal('turn_summary'),
372
+ payload: z.object({
373
+ status: agentRunStatusSchema,
374
+ durationMs: z.number().int().nonnegative().nullable()
375
+ })
376
+ });
377
+ const reasoningSummaryItemSchema = conversationItemBaseSchema.extend({
378
+ kind: z.literal('reasoning_summary'),
379
+ payload: z.object({
380
+ sections: z.array(z.object({ index: z.number().int().nonnegative(), text: z.string() }))
381
+ })
382
+ });
383
+ const toolCallItemSchema = conversationItemBaseSchema.extend({
384
+ kind: z.literal('tool_call'),
385
+ payload: z.object({
386
+ namespace: z.string().min(1).max(128),
387
+ toolName: z.string().min(1).max(128),
388
+ title: z.string().min(1).max(500),
389
+ inputSummary: z.string().max(10000).nullable(),
390
+ outputSummary: z.string().max(10000).nullable(),
391
+ progressLabel: z.string().max(500).nullable(),
392
+ errorCode: z.string().max(80).nullable(),
393
+ truncated: z.boolean()
394
+ })
395
+ });
396
+ const commandExecutionItemSchema = conversationItemBaseSchema.extend({
397
+ kind: z.literal('command_execution'),
398
+ payload: z.object({
399
+ command: z.string().max(10000),
400
+ cwd: z.string().max(4096).nullable(),
401
+ outputPreview: z.string().max(10000),
402
+ outputRef: z.string().min(1).max(128).nullable(),
403
+ exitCode: z.number().int().nullable(),
404
+ durationMs: z.number().int().nonnegative().nullable(),
405
+ truncated: z.boolean()
406
+ })
407
+ });
408
+ const fileChangeItemSchema = conversationItemBaseSchema.extend({
409
+ kind: z.literal('file_change'),
410
+ payload: z.object({
411
+ changes: z.array(z.object({
412
+ path: z.string().min(1).max(4096),
413
+ change: z.enum(['ADDED', 'MODIFIED', 'DELETED', 'RENAMED', 'COPIED', 'TYPE_CHANGED']),
414
+ additions: z.number().int().nonnegative().nullable(),
415
+ deletions: z.number().int().nonnegative().nullable(),
416
+ diffAvailable: z.boolean()
417
+ }))
418
+ })
419
+ });
420
+ const userInputRequestItemSchema = conversationItemBaseSchema.extend({
421
+ kind: z.literal('user_input_request'),
422
+ payload: z.object({
423
+ requestId: z.string().min(1).max(128),
424
+ questions: z.array(z.object({
425
+ id: z.string().min(1).max(128),
426
+ prompt: z.string().max(5000),
427
+ input: z.enum(['SINGLE_SELECT', 'MULTI_SELECT', 'SHORT_TEXT']),
428
+ options: z.array(z.object({
429
+ value: z.string().max(500),
430
+ label: z.string().max(500),
431
+ description: z.string().max(1000).nullable()
432
+ })),
433
+ allowOther: z.boolean()
434
+ }))
435
+ })
436
+ });
437
+ const contextCompactionItemSchema = conversationItemBaseSchema.extend({
438
+ kind: z.literal('context_compaction'),
439
+ payload: z.object({
440
+ phase: z.enum(['STARTED', 'COMPLETED']),
441
+ summary: z.string().max(10000).nullable()
442
+ })
443
+ });
444
+ const warningItemSchema = conversationItemBaseSchema.extend({
445
+ kind: z.literal('warning'),
446
+ payload: z.object({
447
+ code: z.string().min(1).max(80),
448
+ message: z.string().max(5000),
449
+ recoverable: z.boolean()
450
+ })
451
+ });
452
+ const errorItemSchema = conversationItemBaseSchema.extend({
453
+ kind: z.literal('error'),
454
+ payload: z.object({
455
+ code: z.string().min(1).max(80),
456
+ message: z.string().max(5000),
457
+ recoverable: z.boolean(),
458
+ retryAfterMs: z.number().int().nonnegative().nullable()
459
+ })
460
+ });
461
+ const unknownItemSchema = conversationItemBaseSchema.extend({
462
+ kind: z.literal('unknown'),
463
+ payload: z.object({ sourceEventType: z.string().min(1).max(128), label: z.string().max(500) })
464
+ });
465
+ export const conversationItemSchema = z.discriminatedUnion('kind', [
466
+ assistantMessageItemSchema,
467
+ userMessageItemSchema,
468
+ reasoningSummaryItemSchema,
469
+ planItemSchema,
470
+ toolCallItemSchema,
471
+ commandExecutionItemSchema,
472
+ fileChangeItemSchema,
473
+ approvalItemSchema,
474
+ userInputRequestItemSchema,
475
+ contextCompactionItemSchema,
476
+ warningItemSchema,
477
+ errorItemSchema,
478
+ summaryItemSchema,
479
+ unknownItemSchema
480
+ ]);
481
+ export const conversationTurnSchema = z.object({
482
+ id: z.string().min(1).max(128),
483
+ sessionId: z.string().min(1).max(128),
484
+ runId: z.string().min(1).max(128).nullable(),
485
+ userItemId: z.string().min(1).max(128).nullable(),
486
+ status: z.enum([
487
+ 'QUEUED',
488
+ 'RUNNING',
489
+ 'WAITING_INPUT',
490
+ 'WAITING_APPROVAL',
491
+ 'SUCCEEDED',
492
+ 'FAILED',
493
+ 'CANCELLED',
494
+ 'INTERRUPTED',
495
+ 'LOST'
496
+ ]),
497
+ model: z.string().max(128).nullable(),
498
+ effort: z.string().max(64).nullable(),
499
+ access: z.string().max(128).nullable(),
500
+ items: z.array(conversationItemSchema),
501
+ startedAt: z.number().int().nonnegative().nullable(),
502
+ completedAt: z.number().int().nonnegative().nullable()
503
+ });
504
+ export const conversationItemDeltaSchema = z.discriminatedUnion('kind', [
505
+ z.object({
506
+ kind: z.literal('assistant_text_append'),
507
+ itemId: z.string().min(1).max(128),
508
+ itemRevision: z.number().int().nonnegative(),
509
+ text: z.string()
510
+ }),
511
+ z.object({
512
+ kind: z.literal('plan_replace'),
513
+ itemId: z.string().min(1).max(128),
514
+ itemRevision: z.number().int().nonnegative(),
515
+ steps: z.array(z.object({
516
+ id: z.string(),
517
+ text: z.string(),
518
+ status: z.enum(['PENDING', 'IN_PROGRESS', 'COMPLETED'])
519
+ }))
520
+ }),
521
+ z.object({
522
+ kind: z.literal('tool_progress_replace'),
523
+ itemId: z.string().min(1).max(128),
524
+ itemRevision: z.number().int().nonnegative(),
525
+ label: z.string().max(500)
526
+ })
527
+ ]);
528
+ export const terminalDataFrameSchema = z.discriminatedUnion('type', [
529
+ z.object({
530
+ type: z.literal('attach'),
531
+ terminalId: z.string().min(1).max(128),
532
+ afterSequence: z.number().int().nonnegative()
533
+ }),
534
+ z.object({
535
+ type: z.literal('resize'),
536
+ terminalId: z.string().min(1).max(128),
537
+ rows: z.number().int().positive().max(1000),
538
+ cols: z.number().int().positive().max(1000)
539
+ }),
540
+ z.object({
541
+ type: z.literal('input'),
542
+ terminalId: z.string().min(1).max(128),
543
+ data: z.string().max(65536)
544
+ }),
545
+ z.object({
546
+ type: z.literal('output'),
547
+ terminalId: z.string().min(1).max(128),
548
+ sequence: z.number().int().positive(),
549
+ data: z.string().max(65536)
550
+ }),
551
+ z.object({ type: z.literal('close'), terminalId: z.string().min(1).max(128) }),
552
+ z.object({
553
+ type: z.literal('exit'),
554
+ terminalId: z.string().min(1).max(128),
555
+ exitCode: z.number().int().nullable()
556
+ }),
557
+ z.object({
558
+ type: z.literal('error'),
559
+ terminalId: z.string().min(1).max(128),
560
+ code: z.string().min(1).max(80)
561
+ })
562
+ ]);
563
+ export const terminalAttachTicketSchema = z
564
+ .object({
565
+ ticket: z.string().min(32).max(512),
566
+ nodeId: z.string().min(1).max(128),
567
+ terminalId: z.string().min(1).max(128),
568
+ userSessionFingerprint: z.string().min(1).max(128),
569
+ issuedAt: z.number().int().nonnegative(),
570
+ expiresAt: z.number().int().positive()
571
+ })
572
+ .superRefine((value, context) => {
573
+ if (value.expiresAt <= value.issuedAt) {
574
+ context.addIssue({ code: 'custom', message: 'terminal ticket expiry must follow issuance' });
575
+ }
576
+ });
577
+ export const workspaceFileEntrySchema = z.object({
578
+ path: z.string().min(1).max(4096),
579
+ name: z.string().min(1).max(512),
580
+ kind: z.enum(['FILE', 'DIRECTORY']),
581
+ size: z.number().int().nonnegative().nullable(),
582
+ modifiedAt: z.number().int().nonnegative().nullable()
583
+ });
584
+ export const workspaceFileListSchema = z.object({
585
+ entries: z.array(workspaceFileEntrySchema),
586
+ nextCursor: z.string().min(1).max(1024).nullable(),
587
+ truncated: z.boolean()
588
+ });
589
+ export const workspaceFileReadSchema = z.object({
590
+ path: z.string().min(1).max(4096),
591
+ encoding: z.enum(['utf-8', 'base64']),
592
+ size: z.number().int().nonnegative(),
593
+ content: z.string(),
594
+ truncated: z.boolean(),
595
+ nextOffset: z.number().int().nonnegative().nullable()
596
+ });
597
+ export const workspaceUploadPreflightSchema = z.object({
598
+ name: z.string().min(1).max(512),
599
+ status: z.enum(['READY', 'EXISTS_FILE', 'EXISTS_DIRECTORY', 'INVALID_NAME']),
600
+ path: z.string().min(1).max(4096).nullable()
601
+ });
602
+ export const workspaceUploadSchema = z.object({
603
+ uploadId: z.string().uuid(),
604
+ path: z.string().min(1).max(4096),
605
+ receivedBytes: z.number().int().nonnegative(),
606
+ size: z.number().int().nonnegative()
607
+ });
608
+ export const workspaceChangeSchema = z.object({
609
+ path: z.string().min(1).max(4096),
610
+ state: z.enum(['STAGED', 'UNSTAGED', 'UNTRACKED']),
611
+ change: z.enum(['ADDED', 'MODIFIED', 'DELETED', 'RENAMED', 'COPIED', 'TYPE_CHANGED']),
612
+ additions: z.number().int().nonnegative().nullable(),
613
+ deletions: z.number().int().nonnegative().nullable(),
614
+ binary: z.boolean(),
615
+ diffAvailable: z.boolean()
616
+ });
617
+ export const workbenchNodeOperationSchema = z.enum([
618
+ 'node.workbench.capabilities',
619
+ 'runner.usage.status',
620
+ 'runner.usage.read',
621
+ 'workspace.directories.list',
622
+ 'workspace.remove',
623
+ 'session.metadata.update',
624
+ 'session.remove',
625
+ 'session.config.update',
626
+ 'session.turns.list',
627
+ 'session.events.read',
628
+ 'session.context.read',
629
+ 'session.command.execute',
630
+ 'runner.command.execute',
631
+ 'workspace.activity.read',
632
+ 'runner.profiles.list',
633
+ 'runner.defaults.save',
634
+ 'workspace.files.list',
635
+ 'workspace.file.read',
636
+ 'workspace.files.search',
637
+ 'workspace.files.upload.preflight',
638
+ 'workspace.files.upload.create',
639
+ 'workspace.files.upload.chunk',
640
+ 'workspace.files.upload.status',
641
+ 'workspace.files.upload.complete',
642
+ 'workspace.files.upload.cancel',
643
+ 'workspace.changes.current',
644
+ 'workspace.change.diff',
645
+ 'workspace.change.restore',
646
+ 'run.image.read',
647
+ 'terminal.open',
648
+ 'terminal.list',
649
+ 'terminal.close',
650
+ 'terminal.attach.authorize'
651
+ ]);
652
+ /**
653
+ * A small, explicit envelope keeps WebSocket messages independently parseable
654
+ * and makes it safe to reject future message kinds without guessing payloads.
655
+ */
656
+ export const nodeEnvelopeSchema = z.object({
657
+ version: z.literal(NODE_PROTOCOL_VERSION),
658
+ id: z.string().min(1).max(128),
659
+ type: z.string().min(1).max(80),
660
+ replyTo: z.string().min(1).max(128).optional(),
661
+ payload: z.unknown()
662
+ });
663
+ export const nodeErrorSchema = z.object({
664
+ code: z.enum([
665
+ 'PROTOCOL_VERSION_UNSUPPORTED',
666
+ 'MESSAGE_TYPE_UNKNOWN',
667
+ 'MESSAGE_INVALID',
668
+ 'NODE_UNAUTHENTICATED',
669
+ 'NODE_DISABLED',
670
+ 'REGISTRATION_TOKEN_INVALID',
671
+ 'WORKSPACE_INVALID',
672
+ 'RUN_NOT_FOUND',
673
+ 'RUN_STATE_INVALID',
674
+ 'RUN_LOST',
675
+ 'APPROVAL_NOT_FOUND',
676
+ 'APPROVAL_EXPIRED',
677
+ 'APPROVAL_ALREADY_DECIDED',
678
+ 'APPROVAL_ALREADY_EXISTS',
679
+ 'WORKBENCH_PROTOCOL_UNSUPPORTED',
680
+ 'CAPABILITY_UNAVAILABLE',
681
+ 'CAPABILITY_VERSION_UNSUPPORTED',
682
+ 'CAPABILITY_STALE',
683
+ 'EVENT_CURSOR_INVALID',
684
+ 'EVENT_CURSOR_GAP',
685
+ 'ITEM_REVISION_CONFLICT',
686
+ 'RUNNER_PROFILE_INVALID',
687
+ 'RUNNER_COMMAND_UNSUPPORTED',
688
+ 'FILE_NOT_FOUND',
689
+ 'FILE_NOT_REGULAR',
690
+ 'FILE_TOO_LARGE',
691
+ 'FILE_ENCODING_UNSUPPORTED',
692
+ 'FILE_PATH_ESCAPE',
693
+ 'DIRECTORY_CURSOR_INVALID',
694
+ 'FILE_SEARCH_LIMIT_EXCEEDED',
695
+ 'GIT_CHANGE_RESTORE_UNAVAILABLE',
696
+ 'TERMINAL_UNAVAILABLE',
697
+ 'TERMINAL_LIMIT_REACHED',
698
+ 'TERMINAL_NOT_FOUND',
699
+ 'TERMINAL_ATTACH_CONFLICT',
700
+ 'TERMINAL_REPLAY_GAP',
701
+ 'TERMINAL_DATA_CHANNEL_UNAVAILABLE',
702
+ 'INTERNAL_ERROR'
703
+ ])
704
+ });
705
+ export function createEnvelope(type, payload, options = {}) {
706
+ return {
707
+ version: NODE_PROTOCOL_VERSION,
708
+ id: options.id ?? crypto.randomUUID(),
709
+ type,
710
+ ...(options.replyTo === undefined ? {} : { replyTo: options.replyTo }),
711
+ payload
712
+ };
713
+ }
714
+ //# sourceMappingURL=index.js.map