@pstdio/pocketcoder-sdk 0.6.1 → 0.8.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 CHANGED
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ import { createHash } from "node:crypto";
3
+ import { posix } from "node:path";
2
4
  import { isIP } from "node:net";
3
5
  //#region ../contracts/src/attachments/attachment.ts
4
6
  const ATTACHMENT_MAX_FILE_BYTES = 26214400;
@@ -93,14 +95,160 @@ const AttachmentResolvedPayload = z.object({
93
95
  missing_id: z.uuid().optional()
94
96
  });
95
97
  //#endregion
98
+ //#region ../contracts/src/common/canonical.ts
99
+ function canonicalJson(value) {
100
+ return JSON.stringify(sortValue(value));
101
+ }
102
+ function sortValue(value) {
103
+ if (Array.isArray(value)) return value.map(sortValue);
104
+ if (value !== null && typeof value === "object") {
105
+ const out = {};
106
+ for (const key of Object.keys(value).sort()) {
107
+ const v = value[key];
108
+ if (v !== void 0) out[key] = sortValue(v);
109
+ }
110
+ return out;
111
+ }
112
+ return value;
113
+ }
114
+ function sha256Hex(input) {
115
+ return createHash("sha256").update(input).digest("hex");
116
+ }
117
+ function digestOf(value) {
118
+ return `sha256:${sha256Hex(canonicalJson(value))}`;
119
+ }
120
+ //#endregion
121
+ //#region ../contracts/src/templates/workload-capabilities.ts
122
+ const WorkloadDigestSchema = z.string().regex(/^sha256:[0-9a-f]{64}$/);
123
+ const WorkloadImageSchema = z.string().regex(/^[^\s@]+@sha256:[0-9a-f]{64}$/);
124
+ const WorkloadNameSchema = z.string().regex(/^[a-z][a-z0-9-]{0,62}$/);
125
+ const WorkloadBoundSchema = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
126
+ const WorkloadCapabilitySchema = z.enum([
127
+ "ready-without-task",
128
+ "live-inputs",
129
+ "immutable-inputs",
130
+ "serial-operations",
131
+ "retained-exports",
132
+ "explicit-start",
133
+ "exact-exit",
134
+ "targeted-cancel",
135
+ "stop-before-export",
136
+ "request-lookup",
137
+ "deduplicated-accept",
138
+ "explicit-publication",
139
+ "workspace-services",
140
+ "revocable-preview-grants",
141
+ "workspace-cancel",
142
+ "unknown-outcome"
143
+ ]);
144
+ const WorkloadProfileSchema = z.enum([
145
+ "finite-process",
146
+ "cloud-interactive",
147
+ "service"
148
+ ]);
149
+ const common$1 = [
150
+ "ready-without-task",
151
+ "live-inputs",
152
+ "immutable-inputs",
153
+ "serial-operations",
154
+ "retained-exports"
155
+ ];
156
+ [...common$1], [...common$1];
157
+ const WorkloadLimitsSchema = z.strictObject({
158
+ deadlineSeconds: WorkloadBoundSchema,
159
+ finalizationSeconds: WorkloadBoundSchema,
160
+ inputFileBytes: WorkloadBoundSchema,
161
+ stagedInputBytes: WorkloadBoundSchema,
162
+ inputFiles: WorkloadBoundSchema,
163
+ inFlightTransfers: WorkloadBoundSchema,
164
+ exportBytes: WorkloadBoundSchema,
165
+ exportFiles: WorkloadBoundSchema,
166
+ unreferencedInputRetentionSeconds: WorkloadBoundSchema,
167
+ exportRetentionSeconds: WorkloadBoundSchema
168
+ }).superRefine((limits, ctx) => {
169
+ for (const [smaller, larger] of [["inputFileBytes", "stagedInputBytes"], ["finalizationSeconds", "deadlineSeconds"]]) if (limits[smaller] > limits[larger]) ctx.addIssue({
170
+ code: "custom",
171
+ path: [smaller],
172
+ message: `${smaller} must not exceed ${larger}`
173
+ });
174
+ });
175
+ const WorkloadRuntimePinSchema = z.strictObject({
176
+ digest: WorkloadDigestSchema,
177
+ protocol: z.literal("pocketcoder.workload/v1")
178
+ });
179
+ const InstalledWorkloadSchema = z.strictObject({
180
+ workspaceId: z.uuid(),
181
+ incarnationId: z.string().min(1).max(256),
182
+ templateDigest: WorkloadDigestSchema,
183
+ image: WorkloadImageSchema,
184
+ runtime: WorkloadRuntimePinSchema,
185
+ storage: z.strictObject({
186
+ identity: z.string().min(1).max(256),
187
+ profile: z.enum(["controller-readonly/v1", "shared-writer-readonly/v1"])
188
+ }),
189
+ capabilities: z.array(WorkloadCapabilitySchema),
190
+ adapters: z.record(WorkloadNameSchema, z.strictObject({
191
+ kind: z.enum([
192
+ "process",
193
+ "service",
194
+ "pi",
195
+ "agentapi"
196
+ ]),
197
+ digest: WorkloadDigestSchema,
198
+ protocol: z.string().min(1).max(128),
199
+ capabilities: z.array(WorkloadCapabilitySchema)
200
+ }))
201
+ });
202
+ //#endregion
203
+ //#region ../contracts/src/attachments/input-reference.ts
204
+ const InputObjectReferenceSchema = z.strictObject({
205
+ workspaceId: z.uuid(),
206
+ fileId: z.uuid(),
207
+ objectVersion: WorkloadDigestSchema,
208
+ name: z.string().min(1).max(255),
209
+ mediaType: AttachmentMediaTypeSchema,
210
+ sizeBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
211
+ digest: WorkloadDigestSchema,
212
+ visibility: z.enum([
213
+ "ready",
214
+ "unavailable",
215
+ "expired"
216
+ ])
217
+ });
218
+ const AcceptedInputManifestSchema = z.strictObject({
219
+ workspaceId: z.uuid(),
220
+ incarnationId: z.string().min(1).max(256),
221
+ digest: WorkloadDigestSchema,
222
+ objects: z.array(InputObjectReferenceSchema)
223
+ }).superRefine((manifest, ctx) => {
224
+ const { digest, ...contents } = manifest;
225
+ if (digest !== digestOf(contents)) ctx.addIssue({
226
+ code: "custom",
227
+ path: ["digest"],
228
+ message: "input manifest digest does not match its objects"
229
+ });
230
+ const ids = /* @__PURE__ */ new Set();
231
+ for (const [index, object] of manifest.objects.entries()) {
232
+ if (object.workspaceId !== manifest.workspaceId || object.visibility !== "ready" || ids.has(object.fileId)) ctx.addIssue({
233
+ code: "custom",
234
+ path: ["objects", index],
235
+ message: "accepted inputs must be unique, ready and workspace-scoped"
236
+ });
237
+ ids.add(object.fileId);
238
+ }
239
+ });
240
+ //#endregion
96
241
  //#region ../contracts/src/common/scopes.ts
97
242
  const SCOPES = [
98
243
  "templates:read",
244
+ "templates:write",
245
+ "secrets:write",
99
246
  "workspaces:create",
100
247
  "workspaces:read",
101
248
  "workspaces:cancel",
102
249
  "workspaces:purge",
103
250
  "workspaces:recover",
251
+ "principals:admin",
104
252
  "keys:read",
105
253
  "keys:write",
106
254
  "workspaces:preserve",
@@ -112,6 +260,9 @@ const SCOPES = [
112
260
  "conversations:write",
113
261
  "conversations:delete",
114
262
  "services:relay",
263
+ "previews:open",
264
+ "display:view",
265
+ "display:control",
115
266
  "attachments:write",
116
267
  "logs:read",
117
268
  "network:read",
@@ -122,13 +273,20 @@ const SCOPES = [
122
273
  z.strictObject({
123
274
  request_id: z.string().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/),
124
275
  scopes: z.array(z.enum(SCOPES)).min(1).max(SCOPES.length),
276
+ templates: z.array(z.string().min(1).max(128)).max(100).optional(),
277
+ managed_principal_ids: z.array(z.uuid()).max(100).optional(),
125
278
  expires_at: z.iso.datetime()
126
- });
279
+ }).pick({
280
+ request_id: true,
281
+ expires_at: true
282
+ }).partial({ expires_at: true });
127
283
  const KeyResourceSchema = z.object({
128
284
  id: z.uuid(),
129
285
  principal_id: z.uuid(),
130
286
  scopes: z.array(z.string()),
131
287
  effective_scopes: z.array(z.string()),
288
+ templates: z.array(z.string()).nullable(),
289
+ effective_templates: z.array(z.string()),
132
290
  managed_principal_ids: z.array(z.uuid()),
133
291
  issuance_request_id: z.string().nullable(),
134
292
  created_at: z.iso.datetime(),
@@ -144,12 +302,266 @@ const KeyListResponseSchema = z.object({
144
302
  items: z.array(KeyResourceSchema),
145
303
  next_cursor: z.uuid().nullable()
146
304
  });
305
+ const ExpiringKeySchema = KeyResourceSchema.extend({ principal_name: z.string() });
306
+ const ExpiringKeyListResponseSchema = z.object({ items: z.array(ExpiringKeySchema) });
307
+ //#endregion
308
+ //#region ../contracts/src/auth/principals.ts
309
+ const PrincipalGrantsSchema = z.strictObject({
310
+ scopes: z.array(z.enum(SCOPES)).max(SCOPES.length),
311
+ templates: z.array(z.string().min(1).max(128)).max(100)
312
+ });
313
+ PrincipalGrantsSchema.extend({ name: z.string().min(1).max(128) });
314
+ const PrincipalResourceSchema = z.object({
315
+ id: z.uuid(),
316
+ name: z.string(),
317
+ scopes: z.array(z.string()),
318
+ templates: z.array(z.string()),
319
+ disabled_at: z.iso.datetime().nullable(),
320
+ created_at: z.iso.datetime()
321
+ });
322
+ const PrincipalListResponseSchema = z.object({ items: z.array(PrincipalResourceSchema) });
323
+ PrincipalGrantsSchema.partial().extend({ disabled: z.boolean().optional() }).refine((value) => Object.keys(value).length > 0, { message: "Provide a grant or disabled status." });
147
324
  //#endregion
148
325
  //#region ../contracts/src/common/duration.ts
149
326
  const DURATION_RE = /^(\d+)(ms|s|m|h)$/;
327
+ const UNIT_MS = {
328
+ ms: 1,
329
+ s: 1e3,
330
+ m: 6e4,
331
+ h: 36e5
332
+ };
150
333
  function isDuration(value) {
151
334
  return DURATION_RE.test(value);
152
335
  }
336
+ function parseDurationMs(value) {
337
+ const match = DURATION_RE.exec(value);
338
+ if (!match) throw new Error(`invalid duration: ${value}`);
339
+ const amount = Number(match[1]);
340
+ const unit = UNIT_MS[match[2]];
341
+ if (unit === void 0) throw new Error(`invalid duration unit: ${value}`);
342
+ return amount * unit;
343
+ }
344
+ //#endregion
345
+ //#region ../contracts/src/persistence/artifacts.ts
346
+ const SEGMENT = /^(?!\.\.?$)[A-Za-z0-9._-]{1,128}$/;
347
+ const PrivateArtifactSchema = z.object({
348
+ name: z.string().regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
349
+ mount: z.string().regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
350
+ pattern: z.string().max(512).refine((pattern) => {
351
+ const segments = pattern.split("/");
352
+ return segments.every((segment) => segment === "*" || SEGMENT.test(segment)) && segments.filter((segment) => segment === "*").length <= 1;
353
+ }, "pattern must be relative path segments with at most one * segment"),
354
+ maxBytes: z.number().int().positive().max(67108864).default(8388608)
355
+ });
356
+ //#endregion
357
+ //#region ../contracts/src/persistence/persistence.ts
358
+ const DurationValueSchema = z.string().refine(isDuration, { message: "expected a duration like 15s, 20m, or 2h" });
359
+ const LAUNCH_MODES = ["create", "restore"];
360
+ const CONVERSATION_RESTORE_CAPABILITIES = [
361
+ "supported",
362
+ "filesystem_only",
363
+ "unknown"
364
+ ];
365
+ const SourceDescriptorSchema = z.object({
366
+ kind: z.literal("git"),
367
+ repository: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$/),
368
+ revision: z.string().min(1).max(256).refine((value) => !value.startsWith("-") && !value.startsWith("/") && !value.endsWith("/") && !value.endsWith(".") && !value.endsWith(".lock") && value !== "@" && !value.includes("..") && !value.includes("//") && !value.includes("@{") && !/[~^:?*[\]\\\s]/u.test(value) && [...value].every((character) => {
369
+ const code = character.codePointAt(0) ?? 0;
370
+ return code >= 32 && code !== 127;
371
+ }), { message: "revision is not a safe branch, tag, or commit name" })
372
+ });
373
+ const ResolvedSourceSchema = z.object({
374
+ kind: z.literal("git"),
375
+ repository: z.string().min(1).max(64),
376
+ requested_revision: z.string().min(1).max(256),
377
+ resolved_commit: z.string().regex(/^[0-9a-f]{40,64}$/)
378
+ });
379
+ const PersistenceMountSchema = z.object({
380
+ name: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
381
+ target: z.string().min(1),
382
+ maxBytes: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
383
+ maxFiles: z.number().int().positive().max(1e7)
384
+ });
385
+ const CheckpointPolicySchema = z.object({
386
+ onIdle: z.enum(["preserve", "destroy"]).default("destroy"),
387
+ onDeadline: z.enum(["preserve", "destroy"]).default("destroy"),
388
+ onCleanExit: z.enum(["preserve", "destroy"]).default("destroy"),
389
+ onFailure: z.enum([
390
+ "preserve",
391
+ "retain-for-recovery",
392
+ "destroy"
393
+ ]).default("destroy"),
394
+ retention: DurationValueSchema.default("168h")
395
+ });
396
+ const PersistenceSpecSchema = z.object({
397
+ mounts: z.array(PersistenceMountSchema).max(16).default([]),
398
+ conversationRestore: z.enum(CONVERSATION_RESTORE_CAPABILITIES).default("filesystem_only"),
399
+ conversationRetention: DurationValueSchema.default("168h"),
400
+ sessionCompatibility: z.string().min(1).max(128).optional(),
401
+ checkpoint: CheckpointPolicySchema.prefault({}),
402
+ artifacts: z.array(PrivateArtifactSchema).max(16).optional()
403
+ });
404
+ const OutputDeclarationSchema = z.discriminatedUnion("type", [
405
+ z.object({ type: z.literal("gitSha") }),
406
+ z.object({
407
+ type: z.literal("string"),
408
+ maxLength: z.number().int().positive().max(4096).default(512)
409
+ }),
410
+ z.object({
411
+ type: z.literal("httpsUrl"),
412
+ maxLength: z.number().int().positive().max(4096).default(2048)
413
+ })
414
+ ]);
415
+ const CHECKPOINT_STATES = [
416
+ "creating",
417
+ "ready",
418
+ "failed",
419
+ "deleting",
420
+ "deleted"
421
+ ];
422
+ const OPERATION_KINDS = [
423
+ "preserve",
424
+ "restore",
425
+ "verify",
426
+ "delete",
427
+ "purge"
428
+ ];
429
+ const OPERATION_STATES = [
430
+ "pending",
431
+ "running",
432
+ "succeeded",
433
+ "failed"
434
+ ];
435
+ const CheckpointResourceSchema = z.object({
436
+ id: z.uuid(),
437
+ workspace_id: z.uuid(),
438
+ state: z.enum(CHECKPOINT_STATES),
439
+ reason_code: z.string().nullable(),
440
+ template: z.object({
441
+ name: z.string(),
442
+ version: z.string(),
443
+ digest: z.string()
444
+ }),
445
+ manifest_digest: z.string().nullable(),
446
+ logical_bytes: z.number().int().nonnegative().nullable(),
447
+ stored_bytes: z.number().int().nonnegative().nullable(),
448
+ file_count: z.number().int().nonnegative().nullable(),
449
+ mounts: z.array(z.string()),
450
+ conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
451
+ label: z.string().nullable(),
452
+ created_at: z.iso.datetime(),
453
+ ready_at: z.iso.datetime().nullable(),
454
+ expires_at: z.iso.datetime().nullable()
455
+ });
456
+ const OperationResourceSchema = z.object({
457
+ id: z.uuid(),
458
+ kind: z.enum(OPERATION_KINDS),
459
+ state: z.enum(OPERATION_STATES),
460
+ workspace_id: z.uuid().nullable(),
461
+ checkpoint_id: z.uuid().nullable(),
462
+ result_workspace_id: z.uuid().nullable(),
463
+ reason_code: z.string().nullable(),
464
+ created_at: z.iso.datetime(),
465
+ updated_at: z.iso.datetime(),
466
+ completed_at: z.iso.datetime().nullable()
467
+ });
468
+ z.object({
469
+ retention: DurationValueSchema.optional(),
470
+ label: z.string().min(1).max(128).optional()
471
+ });
472
+ z.object({
473
+ external_id: z.string().min(1).max(256),
474
+ metadata: z.record(z.string().max(64), z.string().max(512)).optional(),
475
+ launch_input: z.record(z.string(), z.unknown()).optional(),
476
+ template_version: z.string().min(1).max(256).optional()
477
+ });
478
+ const CheckpointManifestEntrySchema = z.object({
479
+ path: z.string(),
480
+ kind: z.enum([
481
+ "directory",
482
+ "file",
483
+ "symlink"
484
+ ]),
485
+ mode: z.number().int().nonnegative(),
486
+ uid: z.number().int().nonnegative(),
487
+ gid: z.number().int().nonnegative(),
488
+ mtime_ns: z.string().regex(/^\d+$/),
489
+ size: z.number().int().nonnegative(),
490
+ digest: z.string().optional(),
491
+ link_target: z.string().optional()
492
+ });
493
+ z.object({
494
+ format: z.literal("pocketcoder-checkpoint/v1"),
495
+ checkpoint_id: z.uuid(),
496
+ template_digest: z.string(),
497
+ mounts: z.array(z.object({
498
+ name: z.string(),
499
+ entries: z.array(CheckpointManifestEntrySchema)
500
+ })),
501
+ logical_bytes: z.number().int().nonnegative(),
502
+ file_count: z.number().int().nonnegative()
503
+ });
504
+ //#endregion
505
+ //#region ../contracts/src/checkpoints/archive-format.ts
506
+ const CHECKPOINT_ARCHIVE_FORMAT = "pocketcoder-checkpoint-tar/v1";
507
+ const count = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
508
+ const digest$1 = z.string().regex(/^sha256:[a-f0-9]{64}$/);
509
+ const mount = z.strictObject({
510
+ name: PersistenceMountSchema.shape.name,
511
+ logical_bytes: count,
512
+ file_count: count
513
+ });
514
+ const mounts = z.array(mount).max(16).refine((items) => new Set(items.map((item) => item.name)).size === items.length);
515
+ const CheckpointArchiveHeaderSchema = z.strictObject({
516
+ format: z.literal(CHECKPOINT_ARCHIVE_FORMAT),
517
+ checkpoint_id: z.uuid(),
518
+ workspace_id: z.uuid(),
519
+ template_digest: digest$1,
520
+ mounts
521
+ });
522
+ function validUnicode(value) {
523
+ return Buffer.from(value).toString("utf8") === value;
524
+ }
525
+ function safeCheckpointPath(value) {
526
+ return validUnicode(value) && value === value.normalize("NFC") && !value.includes("\0") && !value.includes("\\") && value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
527
+ }
528
+ function safeCheckpointLink(path, target) {
529
+ if (!target || !validUnicode(target) || target.startsWith("/") || target.includes("\0") || target.includes("\\")) return false;
530
+ const resolved = posix.normalize(posix.join(posix.dirname(path), target));
531
+ return resolved !== ".." && !resolved.startsWith("../") && !posix.isAbsolute(resolved);
532
+ }
533
+ const common = {
534
+ mount: z.number().int().min(0).max(15),
535
+ path: z.string().refine(safeCheckpointPath),
536
+ mode: z.number().int().min(0).max(511),
537
+ mtime_ns: z.string().regex(/^(0|[1-9]\d*)$/)
538
+ };
539
+ z.discriminatedUnion("kind", [
540
+ z.strictObject({
541
+ ...common,
542
+ kind: z.literal("directory"),
543
+ size: z.literal(0)
544
+ }),
545
+ z.strictObject({
546
+ ...common,
547
+ kind: z.literal("file"),
548
+ size: count,
549
+ digest: digest$1
550
+ }),
551
+ z.strictObject({
552
+ ...common,
553
+ kind: z.literal("symlink"),
554
+ mode: z.literal(511),
555
+ size: count,
556
+ digest: digest$1,
557
+ link_target: z.string()
558
+ }).refine((entry) => safeCheckpointLink(entry.path, entry.link_target) && entry.size === Buffer.byteLength(entry.link_target) && entry.digest === `sha256:${sha256Hex(entry.link_target)}`)
559
+ ]);
560
+ z.strictObject({
561
+ mounts,
562
+ manifest_digest: digest$1,
563
+ content_digest: digest$1
564
+ });
153
565
  //#endregion
154
566
  //#region ../contracts/src/common/errors.ts
155
567
  const ERROR_CODES = {
@@ -157,13 +569,16 @@ const ERROR_CODES = {
157
569
  "auth.missing_scope": 403,
158
570
  "auth.disabled_principal": 403,
159
571
  "principal.not_found": 404,
572
+ "principal.name_conflict": 409,
160
573
  "key.not_found": 404,
574
+ "request.body_too_large": 413,
161
575
  "validation.invalid": 400,
162
576
  "idempotency.conflict": 409,
163
577
  "capacity.queue_full": 429,
164
578
  "capacity.waiters_full": 429,
165
579
  "template.not_found": 404,
166
580
  "template.version_not_found": 404,
581
+ "template.version_immutable": 409,
167
582
  "template.not_authorized": 403,
168
583
  "workspace.not_found": 404,
169
584
  "workspace.external_id_conflict": 409,
@@ -178,6 +593,10 @@ const ERROR_CODES = {
178
593
  "workspace.persistence_not_enabled": 409,
179
594
  "workspace.preserving": 409,
180
595
  "checkpoint.not_found": 404,
596
+ "checkpoint.unsupported": 409,
597
+ "artifact.not_found": 404,
598
+ "artifact.workspace_active": 409,
599
+ "artifact.too_large": 413,
181
600
  "checkpoint.not_ready": 409,
182
601
  "checkpoint.none_ready": 409,
183
602
  "checkpoint.corrupt": 409,
@@ -187,24 +606,35 @@ const ERROR_CODES = {
187
606
  "restore.image_unavailable": 409,
188
607
  "restore.incompatible": 409,
189
608
  "resume.unsupported": 409,
609
+ "conversation.not_found": 404,
190
610
  "conversation.expired": 410,
191
611
  "conversation.deleted": 410,
192
612
  "operation.conflict": 409,
193
613
  "source.not_allowed": 422,
194
614
  "source.invalid_revision": 422,
195
615
  "secret.unavailable": 503,
616
+ "secret.not_found": 404,
196
617
  "storage.capacity_exhausted": 507,
197
618
  "relay.body_too_large": 413,
198
619
  "relay.route_not_allowed": 422,
199
620
  "relay.streaming_unsupported": 409,
200
621
  "relay.deadline_exceeded": 504,
201
622
  "relay.upstream_error": 502,
623
+ "agent.interrupt_unsupported": 409,
202
624
  "attachment.invalid": 400,
203
625
  "attachment.too_large": 413,
204
626
  "attachment.not_found": 404,
205
627
  "attachment.conflict": 409,
206
628
  "attachment.unsupported": 409,
207
629
  "attachment.interrupted": 503,
630
+ "maintenance.active": 503,
631
+ "maintenance.timeout": 503,
632
+ "backup.failed": 409,
633
+ "journal.pending": 503,
634
+ "account.purged": 409,
635
+ "account.purge_pending": 503,
636
+ "recovery.required": 503,
637
+ "recovery.failed": 409,
208
638
  "internal.error": 500
209
639
  };
210
640
  const errorCodes = Object.keys(ERROR_CODES);
@@ -215,6 +645,9 @@ const ErrorEnvelopeSchema = z.object({ error: z.object({
215
645
  details: z.record(z.string(), z.unknown()).optional()
216
646
  }) });
217
647
  //#endregion
648
+ //#region ../contracts/src/common/json-value.ts
649
+ const JsonValueSchema = z.json();
650
+ //#endregion
218
651
  //#region ../contracts/src/common/pagination.ts
219
652
  const CursorSchema = z.string().min(1).max(2048);
220
653
  function CursorPageSchema(item) {
@@ -231,6 +664,7 @@ function CursorQuerySchema(maxLimit, defaultLimit) {
231
664
  }
232
665
  //#endregion
233
666
  //#region ../contracts/src/conversations/facts.ts
667
+ const ProducerCodeSchema = z.string().regex(/^[a-z][a-z0-9_]{0,63}$/);
234
668
  const ConversationFactSchema = z.discriminatedUnion("type", [
235
669
  z.object({ type: z.literal("intermediate") }),
236
670
  z.object({ type: z.literal("final_response") }),
@@ -245,7 +679,9 @@ const ConversationFactSchema = z.discriminatedUnion("type", [
245
679
  "retrieval"
246
680
  ]),
247
681
  label: z.string().min(1).max(160),
248
- started_at: z.iso.datetime().nullable()
682
+ started_at: z.iso.datetime().nullable(),
683
+ action: ProducerCodeSchema.optional(),
684
+ subject: ProducerCodeSchema.optional()
249
685
  }),
250
686
  z.object({
251
687
  type: z.literal("activity_finished"),
@@ -266,7 +702,8 @@ const ConversationFactSchema = z.discriminatedUnion("type", [
266
702
  "interrupted",
267
703
  "canceled",
268
704
  "lost"
269
- ])
705
+ ]),
706
+ reason: ProducerCodeSchema.optional()
270
707
  })
271
708
  ]);
272
709
  //#endregion
@@ -332,6 +769,160 @@ const ConversationResumeOutcomeSchema = z.object({
332
769
  checkpoint_id: z.uuid()
333
770
  });
334
771
  //#endregion
772
+ //#region ../contracts/src/conversations/document.ts
773
+ const CONVERSATION_RECORD_STATUSES = [
774
+ "completed",
775
+ "failed",
776
+ "interrupted",
777
+ "canceled",
778
+ "lost",
779
+ "message",
780
+ "legacy"
781
+ ];
782
+ const ConversationActivitySchema = z.object({
783
+ activity_id: z.uuid(),
784
+ kind: z.string(),
785
+ label: z.string(),
786
+ action: z.string().nullable(),
787
+ subject: z.string().nullable(),
788
+ started_at: z.iso.datetime().nullable(),
789
+ ended_at: z.iso.datetime().nullable(),
790
+ outcome: z.string().nullable()
791
+ });
792
+ z.object({
793
+ schema: z.literal("pocketcoder.conversation-record/v1"),
794
+ turn_id: z.uuid().nullable(),
795
+ request: ConversationRequestIdentitySchema.nullable(),
796
+ status: z.enum(CONVERSATION_RECORD_STATUSES),
797
+ reason: z.string().nullable(),
798
+ messages: z.array(ConversationMessageResourceSchema),
799
+ activities: z.array(ConversationActivitySchema),
800
+ final_response: z.string().nullable(),
801
+ started_at: z.iso.datetime().nullable(),
802
+ ended_at: z.iso.datetime().nullable()
803
+ });
804
+ const ConversationSummarySchema = z.object({
805
+ conversation_id: z.uuid().nullable(),
806
+ workspace_id: z.uuid(),
807
+ legacy: z.boolean(),
808
+ status: z.enum([
809
+ "retained",
810
+ "deleted",
811
+ "expired"
812
+ ]),
813
+ message_count: z.number().int().nonnegative(),
814
+ turn_count: z.number().int().nonnegative(),
815
+ last_activity_at: z.iso.datetime(),
816
+ expires_at: z.iso.datetime().nullable()
817
+ });
818
+ const ConversationListSchema = z.object({ items: z.array(ConversationSummarySchema) });
819
+ const ConversationDocumentPageSchema = CursorPageSchema(ConversationMessageResourceSchema).extend({
820
+ conversation: ConversationSummarySchema,
821
+ protocol: ConversationProtocolSupportSchema
822
+ });
823
+ const ConversationLiveEventSchema = z.discriminatedUnion("type", [z.object({
824
+ type: z.literal("turn"),
825
+ conversation_id: z.uuid(),
826
+ turn_id: z.uuid(),
827
+ messages: z.array(ConversationMessageResourceSchema.omit({ seq: true }))
828
+ }), z.object({
829
+ type: z.literal("committed"),
830
+ conversation_id: z.uuid().nullable(),
831
+ turn_id: z.uuid().nullable(),
832
+ status: z.enum(CONVERSATION_RECORD_STATUSES),
833
+ last_seq: z.number().int().nonnegative()
834
+ })]);
835
+ //#endregion
836
+ //#region ../contracts/src/previews/view-session.ts
837
+ const HttpsOriginSchema = z.string().refine((value) => {
838
+ try {
839
+ const url = new URL(value);
840
+ return url.protocol === "https:" && url.origin === value && !url.username && !url.password;
841
+ } catch {
842
+ return false;
843
+ }
844
+ }, "An exact HTTPS origin is required.");
845
+ const ViewSessionSchema = z.discriminatedUnion("mode", [
846
+ z.strictObject({ mode: z.literal("local") }),
847
+ z.strictObject({ mode: z.literal("top_level") }),
848
+ z.strictObject({
849
+ mode: z.literal("embedded"),
850
+ parentOrigin: HttpsOriginSchema
851
+ })
852
+ ]);
853
+ z.strictObject({ session: ViewSessionSchema.default({ mode: "local" }) });
854
+ //#endregion
855
+ //#region ../contracts/src/displays/display.ts
856
+ const DisplaySchema = z.strictObject({ mode: z.enum(["desktop", "browser"]) });
857
+ z.strictObject({
858
+ control: z.boolean().default(false),
859
+ session: ViewSessionSchema.default({ mode: "local" })
860
+ });
861
+ const BROWSER_WIDTH = 1280;
862
+ const coordinate = (max) => z.number().int().min(0).max(max - 1);
863
+ const url = z.string().max(2048).refine((value) => {
864
+ try {
865
+ const parsed = new URL(value);
866
+ return ["http:", "https:"].includes(parsed.protocol) && !parsed.username && !parsed.password;
867
+ } catch {
868
+ return false;
869
+ }
870
+ }, "Browser navigation requires an HTTP or HTTPS URL without credentials.");
871
+ z.discriminatedUnion("action", [
872
+ z.strictObject({
873
+ action: z.literal("navigate"),
874
+ url
875
+ }),
876
+ z.strictObject({
877
+ action: z.literal("click"),
878
+ x: coordinate(BROWSER_WIDTH),
879
+ y: coordinate(800)
880
+ }),
881
+ z.strictObject({
882
+ action: z.literal("text"),
883
+ text: z.string().min(1).max(1024)
884
+ }),
885
+ z.strictObject({
886
+ action: z.literal("key"),
887
+ key: z.enum([
888
+ "Enter",
889
+ "Tab",
890
+ "Backspace",
891
+ "Delete",
892
+ "Escape",
893
+ "ArrowLeft",
894
+ "ArrowRight",
895
+ "ArrowUp",
896
+ "ArrowDown",
897
+ "Home",
898
+ "End",
899
+ "PageUp",
900
+ "PageDown"
901
+ ])
902
+ }),
903
+ z.strictObject({
904
+ action: z.literal("scroll"),
905
+ x: coordinate(BROWSER_WIDTH),
906
+ y: coordinate(800),
907
+ deltaY: z.number().int().min(-1e3).max(1e3)
908
+ })
909
+ ]);
910
+ const ScreenshotResourceSchema = z.strictObject({
911
+ kind: z.literal("screenshot"),
912
+ id: z.uuid(),
913
+ workspace_id: z.uuid(),
914
+ content_type: z.literal("image/png"),
915
+ bytes: z.number().int().min(1).max(4 * 1024 ** 2),
916
+ digest: z.string().regex(/^sha256:[a-f0-9]{64}$/),
917
+ expires_at: z.iso.datetime()
918
+ });
919
+ const ScreenshotCapturePayload = z.strictObject({
920
+ output_id: z.uuid(),
921
+ credential: z.string().min(32).max(128),
922
+ url: z.url(),
923
+ expires_at: z.iso.datetime()
924
+ });
925
+ //#endregion
335
926
  //#region ../contracts/src/network/network.ts
336
927
  const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
337
928
  function isDomain(value) {
@@ -386,151 +977,153 @@ const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).ex
386
977
  CursorQuerySchema(200, 100);
387
978
  const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
388
979
  //#endregion
389
- //#region ../contracts/src/persistence/persistence.ts
390
- const DurationValueSchema = z.string().refine(isDuration, { message: "expected a duration like 15s, 20m, or 2h" });
391
- const LAUNCH_MODES = ["create", "restore"];
392
- const CONVERSATION_RESTORE_CAPABILITIES = [
393
- "supported",
394
- "filesystem_only",
395
- "unknown"
396
- ];
397
- const SourceDescriptorSchema = z.object({
398
- kind: z.literal("git"),
399
- repository: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$/),
400
- revision: z.string().min(1).max(256).refine((value) => !value.startsWith("-") && !value.startsWith("/") && !value.endsWith("/") && !value.endsWith(".") && !value.endsWith(".lock") && value !== "@" && !value.includes("..") && !value.includes("//") && !value.includes("@{") && !/[~^:?*[\]\\\s]/u.test(value) && [...value].every((character) => {
401
- const code = character.codePointAt(0) ?? 0;
402
- return code >= 32 && code !== 127;
403
- }), { message: "revision is not a safe branch, tag, or commit name" })
980
+ //#region ../contracts/src/workspaces/workload-operation.ts
981
+ const WorkloadExecutionReferenceSchema = z.strictObject({
982
+ workspaceId: z.uuid(),
983
+ incarnationId: z.string().min(1).max(256),
984
+ owner: z.enum(["process", "conversation-request"]),
985
+ requestId: z.uuid()
404
986
  });
405
- const ResolvedSourceSchema = z.object({
406
- kind: z.literal("git"),
407
- repository: z.string().min(1).max(64),
408
- requested_revision: z.string().min(1).max(256),
409
- resolved_commit: z.string().regex(/^[0-9a-f]{40,64}$/)
987
+ const WorkloadAcceptanceSchema = z.strictObject({
988
+ execution: WorkloadExecutionReferenceSchema,
989
+ callerPrincipalId: z.uuid(),
990
+ templateDigest: WorkloadDigestSchema,
991
+ adapterDigest: WorkloadDigestSchema,
992
+ operation: WorkloadNameSchema,
993
+ input: z.record(z.string(), JsonValueSchema),
994
+ inputs: AcceptedInputManifestSchema,
995
+ bindingSetDigest: WorkloadDigestSchema,
996
+ fingerprint: WorkloadDigestSchema
997
+ }).superRefine((value, ctx) => {
998
+ const { fingerprint, ...facts } = value;
999
+ if (fingerprint !== digestOf(facts)) ctx.addIssue({
1000
+ code: "custom",
1001
+ path: ["fingerprint"],
1002
+ message: "acceptance fingerprint does not match its facts"
1003
+ });
1004
+ if (value.inputs.workspaceId !== value.execution.workspaceId || value.inputs.incarnationId !== value.execution.incarnationId) ctx.addIssue({
1005
+ code: "custom",
1006
+ path: ["inputs"],
1007
+ message: "accepted input identity must match the execution"
1008
+ });
410
1009
  });
411
- const PersistenceMountSchema = z.object({
412
- name: z.string().min(1).max(64).regex(/^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/),
413
- target: z.string().min(1),
414
- maxBytes: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
415
- maxFiles: z.number().int().positive().max(1e7)
1010
+ const WorkloadCancelSchema = z.strictObject({
1011
+ execution: WorkloadExecutionReferenceSchema,
1012
+ cancelId: z.uuid(),
1013
+ scope: z.enum(["operation", "workspace"])
416
1014
  });
417
- const CheckpointPolicySchema = z.object({
418
- onIdle: z.enum(["preserve", "destroy"]).default("destroy"),
419
- onDeadline: z.enum(["preserve", "destroy"]).default("destroy"),
420
- onCleanExit: z.enum(["preserve", "destroy"]).default("destroy"),
421
- onFailure: z.enum([
422
- "preserve",
423
- "retain-for-recovery",
424
- "destroy"
425
- ]).default("destroy"),
426
- retention: DurationValueSchema.default("168h")
1015
+ //#endregion
1016
+ //#region ../contracts/src/outputs/export-reference.ts
1017
+ const ExportObjectReferenceSchema = z.strictObject({
1018
+ objectId: z.uuid(),
1019
+ objectVersion: WorkloadDigestSchema,
1020
+ name: z.string().min(1).max(255),
1021
+ sizeBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
1022
+ mediaType: AttachmentMediaTypeSchema,
1023
+ digest: WorkloadDigestSchema
427
1024
  });
428
- const PersistenceSpecSchema = z.object({
429
- mounts: z.array(PersistenceMountSchema).max(16).default([]),
430
- conversationRestore: z.enum(CONVERSATION_RESTORE_CAPABILITIES).default("filesystem_only"),
431
- conversationRetention: DurationValueSchema.default("168h"),
432
- sessionCompatibility: z.string().min(1).max(128).optional(),
433
- checkpoint: CheckpointPolicySchema.prefault({})
1025
+ z.strictObject({
1026
+ exportId: z.uuid(),
1027
+ execution: WorkloadExecutionReferenceSchema,
1028
+ manifestDigest: WorkloadDigestSchema,
1029
+ completeness: z.enum([
1030
+ "complete",
1031
+ "partial",
1032
+ "lost"
1033
+ ]),
1034
+ publication: z.enum([
1035
+ "published",
1036
+ "unavailable",
1037
+ "expired"
1038
+ ]),
1039
+ objects: z.array(ExportObjectReferenceSchema),
1040
+ errors: z.array(z.string().min(1).max(512)),
1041
+ publishedAt: z.iso.datetime(),
1042
+ expiresAt: z.iso.datetime()
1043
+ }).superRefine((value, ctx) => {
1044
+ const { execution, objects, completeness, errors } = value;
1045
+ if (value.manifestDigest !== digestOf({
1046
+ execution,
1047
+ objects,
1048
+ completeness,
1049
+ errors
1050
+ })) ctx.addIssue({
1051
+ code: "custom",
1052
+ path: ["manifestDigest"],
1053
+ message: "export digest does not match its frozen manifest"
1054
+ });
1055
+ if (Date.parse(value.expiresAt) <= Date.parse(value.publishedAt)) ctx.addIssue({
1056
+ code: "custom",
1057
+ path: ["expiresAt"],
1058
+ message: "export retention must be positive"
1059
+ });
1060
+ const ids = value.objects.map((object) => object.objectId);
1061
+ if (new Set(ids).size !== ids.length) ctx.addIssue({
1062
+ code: "custom",
1063
+ path: ["objects"],
1064
+ message: "export object IDs must be unique"
1065
+ });
434
1066
  });
435
- const OutputDeclarationSchema = z.discriminatedUnion("type", [
436
- z.object({ type: z.literal("gitSha") }),
1067
+ //#endregion
1068
+ //#region ../contracts/src/previews/preview.ts
1069
+ const PreviewNameSchema = z.string().regex(/^[a-z][a-z0-9-]{0,19}$/).refine((name) => !(/* @__PURE__ */ new Set([
1070
+ "display",
1071
+ "control",
1072
+ "agent",
1073
+ "supervisor",
1074
+ "vnc",
1075
+ "cdp"
1076
+ ])).has(name) && !name.startsWith("pc-"), "reserved preview name");
1077
+ const RESERVED_PORTS = /* @__PURE__ */ new Set([
1078
+ 3284,
1079
+ 5900,
1080
+ 5901,
1081
+ 6080,
1082
+ 9222,
1083
+ 9229
1084
+ ]);
1085
+ const PreviewSchema = z.strictObject({ port: z.number().int().min(1024).max(65535).refine((port) => !RESERVED_PORTS.has(port), "reserved port") });
1086
+ const PreviewsSchema = z.record(PreviewNameSchema, PreviewSchema).refine((previews) => Object.keys(previews).length <= 16, "at most 16 previews");
1087
+ const PreviewOpenResponseSchema = z.object({
1088
+ url: z.url(),
1089
+ expires_at: z.iso.datetime()
1090
+ });
1091
+ const PreviewListSchema = z.array(z.object({
1092
+ name: PreviewNameSchema,
1093
+ port: z.number().int()
1094
+ }));
1095
+ const PreviewSocketPayload = z.discriminatedUnion("op", [
437
1096
  z.object({
438
- type: z.literal("string"),
439
- maxLength: z.number().int().positive().max(4096).default(512)
1097
+ op: z.literal("open"),
1098
+ request_id: z.uuid(),
1099
+ name: z.union([PreviewNameSchema, z.literal("display")]),
1100
+ path: z.string().max(8192),
1101
+ origin: z.url(),
1102
+ cookie: z.string().max(8192).optional(),
1103
+ protocols: z.array(z.string().max(128)).max(16)
440
1104
  }),
441
1105
  z.object({
442
- type: z.literal("httpsUrl"),
443
- maxLength: z.number().int().positive().max(4096).default(2048)
1106
+ op: z.literal("ready"),
1107
+ request_id: z.uuid(),
1108
+ protocol: z.string().max(128)
1109
+ }),
1110
+ z.object({
1111
+ op: z.literal("data"),
1112
+ request_id: z.uuid(),
1113
+ seq: z.number().int().nonnegative(),
1114
+ binary: z.boolean(),
1115
+ content_b64: z.string().max(87384)
1116
+ }),
1117
+ z.object({
1118
+ op: z.literal("ack"),
1119
+ request_id: z.uuid(),
1120
+ seq: z.number().int().nonnegative()
1121
+ }),
1122
+ z.object({
1123
+ op: z.literal("close"),
1124
+ request_id: z.uuid()
444
1125
  })
445
1126
  ]);
446
- const CHECKPOINT_STATES = [
447
- "creating",
448
- "ready",
449
- "failed",
450
- "deleting",
451
- "deleted"
452
- ];
453
- const OPERATION_KINDS = [
454
- "preserve",
455
- "restore",
456
- "verify",
457
- "delete",
458
- "purge"
459
- ];
460
- const OPERATION_STATES = [
461
- "pending",
462
- "running",
463
- "succeeded",
464
- "failed"
465
- ];
466
- const CheckpointResourceSchema = z.object({
467
- id: z.uuid(),
468
- workspace_id: z.uuid(),
469
- state: z.enum(CHECKPOINT_STATES),
470
- reason_code: z.string().nullable(),
471
- template: z.object({
472
- name: z.string(),
473
- version: z.string(),
474
- digest: z.string()
475
- }),
476
- manifest_digest: z.string().nullable(),
477
- logical_bytes: z.number().int().nonnegative().nullable(),
478
- stored_bytes: z.number().int().nonnegative().nullable(),
479
- file_count: z.number().int().nonnegative().nullable(),
480
- mounts: z.array(z.string()),
481
- conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES),
482
- label: z.string().nullable(),
483
- created_at: z.iso.datetime(),
484
- ready_at: z.iso.datetime().nullable(),
485
- expires_at: z.iso.datetime().nullable()
486
- });
487
- const OperationResourceSchema = z.object({
488
- id: z.uuid(),
489
- kind: z.enum(OPERATION_KINDS),
490
- state: z.enum(OPERATION_STATES),
491
- workspace_id: z.uuid().nullable(),
492
- checkpoint_id: z.uuid().nullable(),
493
- result_workspace_id: z.uuid().nullable(),
494
- reason_code: z.string().nullable(),
495
- created_at: z.iso.datetime(),
496
- updated_at: z.iso.datetime(),
497
- completed_at: z.iso.datetime().nullable()
498
- });
499
- z.object({
500
- retention: DurationValueSchema.optional(),
501
- label: z.string().min(1).max(128).optional()
502
- });
503
- z.object({
504
- external_id: z.string().min(1).max(256),
505
- metadata: z.record(z.string().max(64), z.string().max(512)).optional(),
506
- launch_input: z.record(z.string(), z.unknown()).optional()
507
- });
508
- const CheckpointManifestEntrySchema = z.object({
509
- path: z.string(),
510
- kind: z.enum([
511
- "directory",
512
- "file",
513
- "symlink"
514
- ]),
515
- mode: z.number().int().nonnegative(),
516
- uid: z.number().int().nonnegative(),
517
- gid: z.number().int().nonnegative(),
518
- mtime_ns: z.string().regex(/^\d+$/),
519
- size: z.number().int().nonnegative(),
520
- digest: z.string().optional(),
521
- link_target: z.string().optional()
522
- });
523
- z.object({
524
- format: z.literal("pocketcoder-checkpoint/v1"),
525
- checkpoint_id: z.uuid(),
526
- template_digest: z.string(),
527
- mounts: z.array(z.object({
528
- name: z.string(),
529
- entries: z.array(CheckpointManifestEntrySchema)
530
- })),
531
- logical_bytes: z.number().int().nonnegative(),
532
- file_count: z.number().int().nonnegative()
533
- });
534
1127
  //#endregion
535
1128
  //#region ../contracts/src/workspaces/workspace.ts
536
1129
  const WORKSPACE_STATES = [
@@ -554,6 +1147,15 @@ const TERMINAL_STATES = [
554
1147
  "preserved"
555
1148
  ];
556
1149
  const REASON_CODES = [
1150
+ ...[
1151
+ "admission_global_capacity",
1152
+ "admission_principal_capacity",
1153
+ "admission_template_capacity",
1154
+ "admission_policy_global_capacity",
1155
+ "admission_actor_capacity",
1156
+ "admission_denied",
1157
+ "admission_unavailable"
1158
+ ],
557
1159
  "child_exit_success",
558
1160
  "child_exit_failure",
559
1161
  "setup_failed",
@@ -588,6 +1190,7 @@ const AGENT_STATES = [
588
1190
  "running",
589
1191
  "stable"
590
1192
  ];
1193
+ const AgentInterruptResponseSchema = z.object({ status: z.enum(["requested", "idle"]) });
591
1194
  z.object({
592
1195
  external_id: z.string().min(1).max(256),
593
1196
  template: z.object({
@@ -603,10 +1206,15 @@ const TemplateRefSchema = z.object({
603
1206
  version: z.string(),
604
1207
  digest: z.string()
605
1208
  });
1209
+ const AgentMetadataSchema = z.object({
1210
+ type: z.string(),
1211
+ model: z.string().min(1).max(255).nullable()
1212
+ });
606
1213
  const WorkspaceResourceSchema = z.object({
607
1214
  id: z.uuid(),
608
1215
  external_id: z.string(),
609
1216
  template: TemplateRefSchema,
1217
+ agent: AgentMetadataSchema.nullable(),
610
1218
  state: z.enum(WORKSPACE_STATES),
611
1219
  reason_code: z.enum(REASON_CODES).nullable(),
612
1220
  agent_state: z.enum(AGENT_STATES),
@@ -660,19 +1268,21 @@ z.object({
660
1268
  limit: z.coerce.number().int().positive().max(200).default(50),
661
1269
  cursor: z.string().optional()
662
1270
  });
663
- //#endregion
664
- //#region ../contracts/src/protocol/api.ts
665
- const TemplatePageSchema = CursorPageSchema(z.object({
1271
+ const TemplateListItemSchema = z.object({
666
1272
  name: z.string(),
667
1273
  version: z.string(),
668
1274
  digest: z.string(),
1275
+ agent: AgentMetadataSchema.nullable(),
669
1276
  description: z.string().optional(),
670
1277
  status: z.enum([
671
1278
  "active",
672
1279
  "available",
673
1280
  "retired"
674
1281
  ])
675
- }));
1282
+ });
1283
+ //#endregion
1284
+ //#region ../contracts/src/protocol/api.ts
1285
+ const TemplatePageSchema = CursorPageSchema(TemplateListItemSchema);
676
1286
  CursorQuerySchema(200, 100);
677
1287
  const CheckpointPageSchema = CursorPageSchema(CheckpointResourceSchema);
678
1288
  CursorQuerySchema(200, 50).extend({ state: z.enum(CHECKPOINT_STATES).optional() });
@@ -762,6 +1372,52 @@ const LogChunkSchema = z.object({
762
1372
  });
763
1373
  CursorQuerySchema(1e3, 200);
764
1374
  const LogPageSchema = CursorPageSchema(LogChunkSchema);
1375
+ const SecretTypeSchema = z.enum([
1376
+ "setup-issuer",
1377
+ "runtime-issuer",
1378
+ "registry"
1379
+ ]);
1380
+ const SecretNameSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/);
1381
+ z.object({ name: SecretNameSchema });
1382
+ const issuerUrl = z.url().refine((value) => {
1383
+ if (!URL.canParse(value)) return false;
1384
+ const url = new URL(value);
1385
+ return url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash;
1386
+ });
1387
+ const IssuerConfigSchema = z.strictObject({
1388
+ url: issuerUrl,
1389
+ authorization: z.string().min(1).max(16384),
1390
+ policy: z.record(z.string(), z.json()).meta({
1391
+ type: "object",
1392
+ additionalProperties: true
1393
+ })
1394
+ });
1395
+ const RegistryConfigSchema = z.strictObject({
1396
+ server: z.string().max(255).regex(/^[A-Za-z0-9.-]+(?::[0-9]{1,5})?$/),
1397
+ username: z.string().min(1).max(1024),
1398
+ password: z.string().min(1).max(16384)
1399
+ });
1400
+ z.discriminatedUnion("type", [
1401
+ z.strictObject({
1402
+ type: z.literal("setup-issuer"),
1403
+ value: IssuerConfigSchema
1404
+ }),
1405
+ z.strictObject({
1406
+ type: z.literal("runtime-issuer"),
1407
+ value: IssuerConfigSchema
1408
+ }),
1409
+ z.strictObject({
1410
+ type: z.literal("registry"),
1411
+ value: RegistryConfigSchema
1412
+ })
1413
+ ]);
1414
+ const SecretResourceSchema = z.strictObject({
1415
+ name: SecretNameSchema,
1416
+ type: SecretTypeSchema,
1417
+ updated_at: z.iso.datetime(),
1418
+ retired_at: z.iso.datetime().nullable()
1419
+ });
1420
+ const SecretListSchema = z.strictObject({ items: z.array(SecretResourceSchema) });
765
1421
  //#endregion
766
1422
  //#region ../contracts/src/templates/template-constants.ts
767
1423
  function isAbsolutePath(value) {
@@ -770,7 +1426,7 @@ function isAbsolutePath(value) {
770
1426
  const SECRET_ENV_PATTERN = /(SECRET|TOKEN|PASSWORD|PASSWD|API_?KEY|PRIVATE_?KEY|CREDENTIAL)/i;
771
1427
  const SECRET_REFERENCE_PREFIX = "secretRef:";
772
1428
  //#endregion
773
- //#region ../contracts/src/templates/template-schema.ts
1429
+ //#region ../contracts/src/templates/template-alpha-schema.ts
774
1430
  const NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
775
1431
  const SEMVER_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
776
1432
  const IMAGE_DIGEST_RE = /^[^\s@]+@sha256:[0-9a-f]{64}$/;
@@ -797,10 +1453,12 @@ const HarnessSchema = z.object({
797
1453
  cwd: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
798
1454
  });
799
1455
  const AgentSchema = HarnessSchema.extend({
1456
+ model: z.string().min(1).max(255).optional().describe("Display metadata only; does not configure the agent model."),
800
1457
  type: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).default("custom"),
801
1458
  transport: z.enum(["pty", "acp"]).default("pty"),
802
1459
  termWidth: z.number().int().min(10).max(65535).optional(),
803
- stateFile: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
1460
+ stateFile: z.string().refine(isAbsolutePath, "expected an absolute path").optional(),
1461
+ interruptKeys: z.string().min(1).max(16).optional()
804
1462
  });
805
1463
  const TerminalSchema = z.object({
806
1464
  command: CommandSchema,
@@ -883,6 +1541,7 @@ const CheckpointHookSchema = z.object({
883
1541
  const TemplateSpecSchema = z.object({
884
1542
  version: z.string().regex(SEMVER_RE),
885
1543
  image: z.string().regex(IMAGE_DIGEST_RE, { message: "image must be digest-pinned (repo@sha256:<64 hex>)" }),
1544
+ imagePullSecret: z.string().refine((value) => value.startsWith("secretRef:") && SecretNameSchema.safeParse(value.slice(10)).success, "imagePullSecret must name a stored secret").optional(),
886
1545
  command: CommandSchema.default([
887
1546
  "/usr/local/bin/pocketcoder-supervisor",
888
1547
  "supervise",
@@ -893,6 +1552,8 @@ const TemplateSpecSchema = z.object({
893
1552
  agent: AgentSchema.optional(),
894
1553
  harness: HarnessSchema.optional(),
895
1554
  terminal: TerminalSchema.optional(),
1555
+ previews: PreviewsSchema.optional(),
1556
+ display: DisplaySchema.optional(),
896
1557
  env: EnvSchema.default({}),
897
1558
  resources: ResourcesSchema,
898
1559
  timeouts: TimeoutsSchema.prefault({}),
@@ -915,7 +1576,11 @@ const TemplateSpecSchema = z.object({
915
1576
  message: "either agent or harness is required"
916
1577
  });
917
1578
  if (!spec.agent) return;
918
- if (spec.agent.transport === "acp") for (const field of ["termWidth", "stateFile"]) {
1579
+ if (spec.agent.transport === "acp") for (const field of [
1580
+ "termWidth",
1581
+ "stateFile",
1582
+ "interruptKeys"
1583
+ ]) {
919
1584
  if (spec.agent[field] === void 0) continue;
920
1585
  ctx.addIssue({
921
1586
  code: "custom",
@@ -951,7 +1616,7 @@ const TemplateSpecSchema = z.object({
951
1616
  };
952
1617
  });
953
1618
  const TemplateManifestBaseSchema = z.object({
954
- apiVersion: z.literal("pocketcoder.dev/v1alpha1"),
1619
+ apiVersion: z.literal("pocketcoder.dev/v1alpha1", { error: "unsupported template format for the alpha runtime; canonical workloads require an installed workload runtime" }),
955
1620
  kind: z.literal("Template"),
956
1621
  metadata: z.object({
957
1622
  name: z.string().regex(NAME_RE),
@@ -960,61 +1625,105 @@ const TemplateManifestBaseSchema = z.object({
960
1625
  spec: TemplateSpecSchema
961
1626
  });
962
1627
  //#endregion
963
- //#region ../contracts/src/templates/template-runtime.ts
964
- const LEGACY_AGENTAPI_SERVICE = ServiceSchema.parse({
965
- baseUrl: "http://127.0.0.1:3284",
966
- routes: [
967
- {
968
- method: "GET",
969
- path: "/status"
970
- },
971
- {
972
- method: "GET",
973
- path: "/messages",
974
- query: ["after"]
975
- },
976
- {
977
- method: "POST",
978
- path: "/message"
979
- }
980
- ]
1628
+ //#region ../contracts/src/workspaces/finite-process.ts
1629
+ const ProcessReferenceSchema = WorkloadExecutionReferenceSchema.extend({ owner: z.literal("process") });
1630
+ const FiniteProcessFactSchema = z.strictObject({
1631
+ execution: ProcessReferenceSchema,
1632
+ fingerprint: WorkloadDigestSchema,
1633
+ phase: z.enum([
1634
+ "claimed",
1635
+ "running",
1636
+ "exited",
1637
+ "lost"
1638
+ ]),
1639
+ processGroupId: z.number().int().positive().nullable(),
1640
+ claimedAt: z.iso.datetime(),
1641
+ startedAt: z.iso.datetime().nullable(),
1642
+ endedAt: z.iso.datetime().nullable(),
1643
+ exitCode: z.number().int().nullable(),
1644
+ signal: z.string().min(1).nullable()
1645
+ }).superRefine((fact, ctx) => {
1646
+ if ((fact.phase === "exited" || fact.phase === "lost") !== (fact.endedAt !== null)) ctx.addIssue({
1647
+ code: "custom",
1648
+ message: "terminal facts require an end time"
1649
+ });
1650
+ if (fact.phase === "exited" && fact.exitCode === null === (fact.signal === null)) ctx.addIssue({
1651
+ code: "custom",
1652
+ message: "exit requires exactly one observed code or signal"
1653
+ });
1654
+ if (fact.phase !== "exited" && (fact.exitCode !== null || fact.signal !== null)) ctx.addIssue({
1655
+ code: "custom",
1656
+ message: "unobserved exits cannot have a code or signal"
1657
+ });
1658
+ if (fact.phase === "running" && (!fact.processGroupId || !fact.startedAt)) ctx.addIssue({
1659
+ code: "custom",
1660
+ message: "running facts require an owned process group and start time"
1661
+ });
981
1662
  });
982
- const AGENTAPI_SERVICE = ServiceSchema.parse({
983
- ...LEGACY_AGENTAPI_SERVICE,
984
- routes: [...LEGACY_AGENTAPI_SERVICE.routes, {
985
- method: "GET",
986
- path: "/events",
987
- responseMode: "stream",
988
- maxResponseBytes: 4194304,
989
- deadlineSeconds: 300
990
- }]
1663
+ const WaitingRuntimeSpecSchema = z.strictObject({
1664
+ incarnationId: z.string().min(1).max(256),
1665
+ profile: WorkloadProfileSchema,
1666
+ operations: z.record(z.string(), HarnessSchema),
1667
+ infrastructure: z.record(z.string(), HarnessSchema)
1668
+ });
1669
+ const ProcessStartPayload = z.strictObject({
1670
+ execution: ProcessReferenceSchema,
1671
+ fingerprint: WorkloadDigestSchema,
1672
+ input: z.record(z.string(), JsonValueSchema).default({}),
1673
+ inputDirectory: z.string().min(1).optional(),
1674
+ operation: z.string().min(1).max(64)
1675
+ });
1676
+ const ProcessTargetPayload = z.strictObject({ execution: ProcessReferenceSchema });
1677
+ const ProcessCancelPayload = ProcessTargetPayload.extend({
1678
+ cancelId: z.uuid(),
1679
+ signal: z.enum(["SIGTERM", "SIGKILL"]),
1680
+ deadlineAt: z.iso.datetime()
1681
+ });
1682
+ const ProcessCancelResultPayload = ProcessTargetPayload.extend({
1683
+ cancelId: z.uuid(),
1684
+ status: z.enum([
1685
+ "canceled",
1686
+ "requested",
1687
+ "terminal"
1688
+ ])
1689
+ });
1690
+ //#endregion
1691
+ //#region ../contracts/src/secrets/credential-slot.ts
1692
+ const CredentialSlotSchema = z.strictObject({
1693
+ policyRef: z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,254}$/).refine((value) => !value.includes("..")),
1694
+ policyVersion: z.string().min(1).max(128),
1695
+ binding: z.enum(["fixed-scope", "post-create-grant"]),
1696
+ delivery: z.literal("workspace-expiring-file"),
1697
+ maxLeaseSeconds: z.number().int().positive().max(Number.MAX_SAFE_INTEGER)
1698
+ });
1699
+ //#endregion
1700
+ //#region ../contracts/src/templates/workload-input-schema.ts
1701
+ const WorkloadInputPropertySchema = z.lazy(() => z.discriminatedUnion("type", [
1702
+ z.strictObject({ type: z.enum([
1703
+ "string",
1704
+ "number",
1705
+ "integer",
1706
+ "boolean",
1707
+ "null"
1708
+ ]) }),
1709
+ z.strictObject({
1710
+ type: z.literal("array"),
1711
+ items: WorkloadInputPropertySchema
1712
+ }),
1713
+ WorkloadInputSchema
1714
+ ]));
1715
+ const WorkloadInputSchema = z.strictObject({
1716
+ type: z.literal("object"),
1717
+ properties: z.record(z.string(), WorkloadInputPropertySchema),
1718
+ required: z.array(z.string()).default([]),
1719
+ additionalProperties: z.literal(false)
1720
+ }).superRefine((schema, ctx) => {
1721
+ if (new Set(schema.required).size !== schema.required.length || schema.required.some((name) => !Object.hasOwn(schema.properties, name))) ctx.addIssue({
1722
+ code: "custom",
1723
+ path: ["required"],
1724
+ message: "required input properties must be declared and unique"
1725
+ });
991
1726
  });
992
- function isAgentApiNative(spec) {
993
- return "agent" in spec && spec.agent !== void 0;
994
- }
995
- function agentApiHarness(spec) {
996
- if (!isAgentApiNative(spec)) return spec.harness;
997
- return {
998
- command: [
999
- "/usr/local/bin/agentapi",
1000
- "server",
1001
- "--type",
1002
- spec.agent.type,
1003
- ...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
1004
- ...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
1005
- ...spec.agent.stateFile === void 0 ? [] : ["--state-file", spec.agent.stateFile],
1006
- "--port",
1007
- "3284",
1008
- "--",
1009
- ...spec.agent.command
1010
- ],
1011
- env: spec.agent.env,
1012
- ...spec.agent.cwd ? { cwd: spec.agent.cwd } : {}
1013
- };
1014
- }
1015
- function templateServices(spec) {
1016
- return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services;
1017
- }
1018
1727
  //#endregion
1019
1728
  //#region ../contracts/src/templates/template-validation.ts
1020
1729
  function envSources(spec) {
@@ -1062,7 +1771,7 @@ function validateNetworkEnvironment(spec, ctx) {
1062
1771
  });
1063
1772
  }
1064
1773
  }
1065
- function validateServices(spec, ctx) {
1774
+ function validateServices$1(spec, ctx) {
1066
1775
  for (const [serviceName, service] of Object.entries(templateServices(spec))) {
1067
1776
  if (!isLoopbackBaseUrl(service.baseUrl)) ctx.addIssue({
1068
1777
  code: "custom",
@@ -1205,7 +1914,7 @@ const FORBIDDEN_PERSISTENCE_ROOTS = [
1205
1914
  function pathContains(parent, child) {
1206
1915
  return child === parent || child.startsWith(`${parent}/`);
1207
1916
  }
1208
- function validatePersistence(spec, ctx) {
1917
+ function validateTemplatePersistence(spec, ctx) {
1209
1918
  const seenNames = /* @__PURE__ */ new Set();
1210
1919
  const mounts = spec.persistence.mounts;
1211
1920
  for (const [index, mount] of mounts.entries()) validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx);
@@ -1222,7 +1931,7 @@ function validatePersistence(spec, ctx) {
1222
1931
  });
1223
1932
  }
1224
1933
  function validateAgentStateFile(spec, mounts, ctx) {
1225
- if (!isAgentApiNative(spec)) return;
1934
+ if (!spec.agent) return;
1226
1935
  const stateFile = spec.agent.stateFile;
1227
1936
  const normalized = stateFile !== void 0 && isNormalizedFilesystemPath(stateFile);
1228
1937
  if (stateFile !== void 0 && !normalized) ctx.addIssue({
@@ -1343,18 +2052,436 @@ function isNormalizedPath(path) {
1343
2052
  return true;
1344
2053
  }
1345
2054
  function validateTemplateSpec(spec, ctx) {
1346
- validateServices(spec, ctx);
2055
+ validateServices$1(spec, ctx);
2056
+ validatePreviews(spec, ctx);
1347
2057
  validateEnvironment(spec, ctx);
1348
2058
  validateOutputs(spec, ctx);
1349
2059
  validateWritableMemoryPaths(spec, ctx);
1350
- validatePersistence(spec, ctx);
2060
+ validateTemplatePersistence(spec, ctx);
1351
2061
  validateNetworkEnvironment(spec, ctx);
1352
2062
  validateTerminal(spec, ctx);
1353
2063
  }
1354
- TemplateManifestBaseSchema.superRefine((manifest, ctx) => {
2064
+ function validatePreviews(spec, ctx) {
2065
+ const servicePorts = new Set(Object.values(templateServices(spec)).filter((service) => isLoopbackBaseUrl(service.baseUrl)).map((service) => {
2066
+ const url = new URL(service.baseUrl);
2067
+ return Number(url.port || 80);
2068
+ }));
2069
+ for (const [name, preview] of Object.entries(spec.previews ?? {})) if (servicePorts.has(preview.port)) ctx.addIssue({
2070
+ code: "custom",
2071
+ path: [
2072
+ "spec",
2073
+ "previews",
2074
+ name,
2075
+ "port"
2076
+ ],
2077
+ message: "preview port is reserved by a template service"
2078
+ });
2079
+ }
2080
+ //#endregion
2081
+ //#region ../contracts/src/templates/workload-validation.ts
2082
+ function isWorkloadPath(path) {
2083
+ return path.startsWith("/") && !/[\\,\s]/.test(path) && !path.includes("//") && !path.endsWith("/") && ![...path].some((character) => {
2084
+ const code = character.charCodeAt(0);
2085
+ return code < 32 || code === 127;
2086
+ }) && !path.split("/").some((part) => part === "." || part === "..");
2087
+ }
2088
+ function overlaps(left, right) {
2089
+ return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
2090
+ }
2091
+ function validateProfile(spec, issue) {
2092
+ const operations = Object.values(spec.operations);
2093
+ if (operations.length === 0 && spec.profile !== "service") issue(["operations"], "at least one operation is required");
2094
+ if (spec.profile === "service" && Object.keys(spec.services).length === 0) issue(["services"], "service profile requires declared transport");
2095
+ if (spec.profile === "finite-process" && (operations.length !== 1 || !Object.hasOwn(spec.adapters, operations[0]?.adapter ?? "") || spec.adapters[operations[0]?.adapter ?? ""]?.kind !== "process")) issue(["operations"], "finite-process requires one process operation");
2096
+ }
2097
+ function validateSecurity(spec, issue) {
2098
+ if (!spec.security.readOnlyRoot || !spec.security.dropCapabilities.includes("ALL")) issue(["security"], "workload profiles require a read-only root and all capabilities dropped");
2099
+ for (const path of spec.security.writableMemoryPaths) if (!isWorkloadPath(path) || [
2100
+ "/run/pocketcoder",
2101
+ "/proc",
2102
+ "/sys",
2103
+ "/dev"
2104
+ ].some((root) => overlaps(path, root))) issue(["security", "writableMemoryPaths"], "writable path overlaps protected storage or is not normalized");
2105
+ }
2106
+ function validateAdapters(spec, issue) {
2107
+ for (const [name, adapter] of Object.entries(spec.adapters)) {
2108
+ const path = ["adapters", name];
2109
+ if (adapter.kind === "service") {
2110
+ if (!adapter.service || !Object.hasOwn(spec.services, adapter.service) || adapter.command) issue(path, "service adapters require a declared service and no process command");
2111
+ } else if (!adapter.command || adapter.service) issue(path, "process, Pi and AgentAPI adapters require a command and no service reference");
2112
+ }
2113
+ }
2114
+ function validateProcessEnvironment(spec, issue) {
2115
+ const processes = [
2116
+ ...spec.lifecycle?.checkpointHook ? [{
2117
+ path: ["lifecycle", "checkpointHook"],
2118
+ process: spec.lifecycle.checkpointHook
2119
+ }] : [],
2120
+ ...Object.entries(spec.adapters).map(([name, adapter]) => ({
2121
+ path: ["adapters", name],
2122
+ process: adapter
2123
+ })),
2124
+ ...Object.entries(spec.services).flatMap(([name, service]) => service.process ? [{
2125
+ path: [
2126
+ "services",
2127
+ name,
2128
+ "process"
2129
+ ],
2130
+ process: service.process
2131
+ }] : [])
2132
+ ];
2133
+ for (const { path, process } of processes) {
2134
+ if (process.cwd && process.cwd !== "/" && !isWorkloadPath(process.cwd)) issue(path, "cwd must be normalized");
2135
+ validateEnv(spec, process.env, path, issue);
2136
+ }
2137
+ }
2138
+ function validateEnv(spec, env, path, issue) {
2139
+ for (const [key, value] of Object.entries(env)) {
2140
+ if (value.startsWith("credentialSlot:")) {
2141
+ if (!Object.hasOwn(spec.credentialSlots, value.slice(15))) issue([
2142
+ ...path,
2143
+ "env",
2144
+ key
2145
+ ], "unknown credential slot");
2146
+ } else if (SECRET_ENV_PATTERN.test(key) && value !== "" || value.startsWith("secretRef:")) issue([
2147
+ ...path,
2148
+ "env",
2149
+ key
2150
+ ], "runtime credentials require a credentialSlot: reference");
2151
+ if (spec.network.mode === "restricted" && /^(http_proxy|https_proxy|all_proxy|no_proxy)$/i.test(key)) issue([
2152
+ ...path,
2153
+ "env",
2154
+ key
2155
+ ], "proxy variables are reserved by restricted networking");
2156
+ }
2157
+ }
2158
+ function validateOperations(spec, issue) {
2159
+ for (const [name, operation] of Object.entries(spec.operations)) {
2160
+ const adapter = Object.hasOwn(spec.adapters, operation.adapter) ? spec.adapters[operation.adapter] : void 0;
2161
+ if (!adapter) issue([
2162
+ "operations",
2163
+ name,
2164
+ "adapter"
2165
+ ], "operation must name a pinned adapter");
2166
+ if (spec.profile === "cloud-interactive" && adapter?.kind === "process") issue(["operations", name], "cloud-interactive requires request-aware adapters");
2167
+ for (const root of operation.outputRoots) if (!isWorkloadPath(root) || [
2168
+ "/run",
2169
+ "/proc",
2170
+ "/sys",
2171
+ "/dev"
2172
+ ].some((path) => overlaps(path, root))) issue([
2173
+ "operations",
2174
+ name,
2175
+ "outputRoots"
2176
+ ], "output roots must be normalized and outside protected storage");
2177
+ }
2178
+ }
2179
+ function serviceUrl(value) {
2180
+ try {
2181
+ return new URL(value);
2182
+ } catch {
2183
+ return null;
2184
+ }
2185
+ }
2186
+ function isLoopbackService(url) {
2187
+ return url.protocol === "http:" && [
2188
+ "127.0.0.1",
2189
+ "localhost",
2190
+ "[::1]"
2191
+ ].includes(url.hostname) && !url.username && !url.password && url.pathname === "/" && !url.search && !url.hash;
2192
+ }
2193
+ function validateServices(spec, issue) {
2194
+ for (const [name, service] of Object.entries(spec.services)) {
2195
+ const url = serviceUrl(service.baseUrl);
2196
+ if (!url || !isLoopbackService(url)) {
2197
+ issue(["services", name], "service must use loopback HTTP without credentials or a path");
2198
+ continue;
2199
+ }
2200
+ const routes = /* @__PURE__ */ new Set();
2201
+ if (!isNormalizedPath(service.healthPath)) issue([
2202
+ "services",
2203
+ name,
2204
+ "healthPath"
2205
+ ], "health path must be normalized");
2206
+ for (const route of service.routes) {
2207
+ const key = `${route.method} ${route.path}`;
2208
+ if (!isNormalizedPath(route.path) || routes.has(key)) issue([
2209
+ "services",
2210
+ name,
2211
+ "routes"
2212
+ ], "routes must be normalized and unique");
2213
+ routes.add(key);
2214
+ }
2215
+ for (const [previewName, preview] of Object.entries(spec.previews)) if (preview.port === Number(url.port || 80)) issue(["previews", previewName], "preview port is reserved by a service");
2216
+ }
2217
+ }
2218
+ function validateWorkloadTemplate(manifest, ctx) {
2219
+ const spec = manifest.spec;
2220
+ const issue = (path, message) => ctx.addIssue({
2221
+ code: "custom",
2222
+ path: ["spec", ...path],
2223
+ message
2224
+ });
2225
+ validateTemplatePersistence({
2226
+ security: spec.security,
2227
+ source: null,
2228
+ persistence: spec.lifecycle?.persistence ?? PersistenceSpecSchema.parse({})
2229
+ }, ctx);
2230
+ const timeouts = spec.lifecycle?.timeouts;
2231
+ if (timeouts && (parseDurationMs(timeouts.maxAge) > spec.limits.deadlineSeconds * 1e3 || parseDurationMs(timeouts.terminateGrace) > spec.limits.finalizationSeconds * 1e3)) issue(["lifecycle", "timeouts"], "Lifecycle timeouts exceed the workload limits.");
2232
+ validateProfile(spec, issue);
2233
+ validateSecurity(spec, issue);
2234
+ validateAdapters(spec, issue);
2235
+ validateProcessEnvironment(spec, issue);
2236
+ validateOperations(spec, issue);
2237
+ validateServices(spec, issue);
2238
+ for (const [name, slot] of Object.entries(spec.credentialSlots)) if (slot.maxLeaseSeconds > spec.limits.deadlineSeconds) issue(["credentialSlots", name], "credential lifetime exceeds the workspace deadline");
2239
+ }
2240
+ //#endregion
2241
+ //#region ../contracts/src/templates/workload-template.ts
2242
+ const WorkloadAdapterSchema = z.strictObject({
2243
+ kind: z.enum([
2244
+ "process",
2245
+ "service",
2246
+ "pi",
2247
+ "agentapi"
2248
+ ]),
2249
+ digest: WorkloadDigestSchema,
2250
+ protocol: z.string().min(1).max(128),
2251
+ command: HarnessSchema.shape.command.optional(),
2252
+ cwd: HarnessSchema.shape.cwd,
2253
+ env: HarnessSchema.shape.env,
2254
+ service: WorkloadNameSchema.optional()
2255
+ });
2256
+ const WorkloadOperationSchema = z.strictObject({
2257
+ adapter: WorkloadNameSchema,
2258
+ inputSchema: WorkloadInputSchema,
2259
+ requiredCapabilities: z.array(WorkloadCapabilitySchema),
2260
+ outputRoots: z.array(z.string()).max(16)
2261
+ });
2262
+ const WorkloadTemplateSchema = z.strictObject({
2263
+ apiVersion: z.literal("pocketcoder.dev/v1"),
2264
+ kind: z.literal("Template"),
2265
+ metadata: z.strictObject({
2266
+ name: WorkloadNameSchema,
2267
+ description: z.string().max(512).optional()
2268
+ }),
2269
+ spec: z.strictObject({
2270
+ version: z.string().regex(/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/),
2271
+ image: WorkloadImageSchema,
2272
+ runtime: WorkloadRuntimePinSchema,
2273
+ profile: WorkloadProfileSchema,
2274
+ adapters: z.record(WorkloadNameSchema, WorkloadAdapterSchema),
2275
+ operations: z.record(WorkloadNameSchema, WorkloadOperationSchema),
2276
+ services: z.record(WorkloadNameSchema, ServiceSchema.extend({ process: HarnessSchema.strict().optional() }).strict()),
2277
+ previews: PreviewsSchema,
2278
+ credentialSlots: z.record(WorkloadNameSchema, CredentialSlotSchema),
2279
+ resources: ResourcesSchema.strict(),
2280
+ network: NetworkPolicySchema,
2281
+ security: SecuritySchema.strict(),
2282
+ limits: WorkloadLimitsSchema,
2283
+ lifecycle: z.strictObject({
2284
+ persistence: PersistenceSpecSchema.prefault({}),
2285
+ checkpointHook: CheckpointHookSchema.optional(),
2286
+ timeouts: TimeoutsSchema.optional(),
2287
+ maxLaunchInputBytes: z.number().int().positive().max(1048576).default(65536)
2288
+ }).optional()
2289
+ })
2290
+ }).superRefine(validateWorkloadTemplate);
2291
+ const WorkloadTemplateSnapshotSchema = z.strictObject({
2292
+ format: z.literal("pocketcoder.dev/v1"),
2293
+ name: WorkloadNameSchema,
2294
+ version: z.string(),
2295
+ digest: WorkloadDigestSchema,
2296
+ manifest: WorkloadTemplateSchema
2297
+ }).superRefine((snapshot, ctx) => {
2298
+ if (snapshot.name !== snapshot.manifest.metadata.name || snapshot.version !== snapshot.manifest.spec.version || snapshot.digest !== digestOf(snapshot.manifest)) ctx.addIssue({
2299
+ code: "custom",
2300
+ message: "snapshot identity does not match its immutable manifest"
2301
+ });
2302
+ });
2303
+ //#endregion
2304
+ //#region ../contracts/src/templates/template-runtime.ts
2305
+ const LEGACY_AGENTAPI_SERVICE = ServiceSchema.parse({
2306
+ baseUrl: "http://127.0.0.1:3284",
2307
+ routes: [
2308
+ {
2309
+ method: "GET",
2310
+ path: "/status"
2311
+ },
2312
+ {
2313
+ method: "GET",
2314
+ path: "/messages",
2315
+ query: ["after"]
2316
+ },
2317
+ {
2318
+ method: "POST",
2319
+ path: "/message"
2320
+ }
2321
+ ]
2322
+ });
2323
+ const AGENTAPI_SERVICE = ServiceSchema.parse({
2324
+ ...LEGACY_AGENTAPI_SERVICE,
2325
+ routes: [...LEGACY_AGENTAPI_SERVICE.routes, {
2326
+ method: "GET",
2327
+ path: "/events",
2328
+ responseMode: "stream",
2329
+ maxResponseBytes: 4194304,
2330
+ deadlineSeconds: 300
2331
+ }]
2332
+ });
2333
+ function isAgentApiNative(spec) {
2334
+ return "agent" in spec && spec.agent !== void 0;
2335
+ }
2336
+ function agentApiHarness(spec) {
2337
+ if (!isAgentApiNative(spec)) {
2338
+ if (!spec.harness) throw new Error("A workload infrastructure process is not an alpha harness.");
2339
+ return spec.harness;
2340
+ }
2341
+ return {
2342
+ command: [
2343
+ "/usr/local/bin/agentapi",
2344
+ "server",
2345
+ "--type",
2346
+ spec.agent.type,
2347
+ ...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
2348
+ ...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
2349
+ ...spec.agent.stateFile === void 0 ? [] : ["--state-file", spec.agent.stateFile],
2350
+ "--port",
2351
+ "3284",
2352
+ "--",
2353
+ ...spec.agent.command
2354
+ ],
2355
+ env: spec.agent.env,
2356
+ ...spec.agent.cwd ? { cwd: spec.agent.cwd } : {}
2357
+ };
2358
+ }
2359
+ function templateServices(spec) {
2360
+ return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services ?? {};
2361
+ }
2362
+ z.object({
2363
+ name: z.string(),
2364
+ version: z.string(),
2365
+ digest: WorkloadDigestSchema,
2366
+ spec: TemplateSpecSchema
2367
+ });
2368
+ //#endregion
2369
+ //#region ../contracts/src/templates/template.ts
2370
+ const TemplateManifestSchema = TemplateManifestBaseSchema.superRefine((manifest, ctx) => {
1355
2371
  validateTemplateSpec(manifest.spec, ctx);
1356
2372
  });
1357
2373
  //#endregion
2374
+ //#region ../contracts/src/protocol/protocol-checkpoint.ts
2375
+ const bytes = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
2376
+ const digest = z.string().regex(/^sha256:[a-f0-9]{64}$/);
2377
+ const PrepareCheckpointArchivePayload = z.object({
2378
+ operation_id: z.uuid(),
2379
+ checkpoint_id: z.uuid(),
2380
+ deadline_ms: bytes,
2381
+ mounts: z.array(PersistenceMountSchema).max(16),
2382
+ max_archive_bytes: bytes,
2383
+ max_index_bytes: bytes,
2384
+ max_queue_bytes: bytes
2385
+ });
2386
+ const CheckpointPreparedPayload = z.object({
2387
+ operation_id: z.uuid(),
2388
+ checkpoint_id: z.uuid(),
2389
+ header: CheckpointArchiveHeaderSchema,
2390
+ archive_bytes: bytes
2391
+ }).refine((value) => value.header.checkpoint_id === value.checkpoint_id, "Checkpoint declaration identity differs.");
2392
+ const grant = z.object({
2393
+ operation_id: z.uuid(),
2394
+ transfer_id: z.uuid(),
2395
+ checkpoint_id: z.uuid(),
2396
+ credential: z.string().min(1).max(4096),
2397
+ url: z.url(),
2398
+ expires_at: z.iso.datetime()
2399
+ });
2400
+ const CheckpointUploadPayload = grant;
2401
+ const RestoreTransferSpecSchema = grant.extend({
2402
+ source: z.object({
2403
+ checkpoint_id: z.uuid(),
2404
+ workspace_id: z.uuid(),
2405
+ template_digest: digest,
2406
+ archive_digest: digest
2407
+ }),
2408
+ mounts: z.array(PersistenceMountSchema).max(16),
2409
+ max_archive_bytes: bytes,
2410
+ max_index_bytes: bytes,
2411
+ max_ledger_bytes: bytes
2412
+ }).refine((value) => value.source.checkpoint_id === value.checkpoint_id, "Restore source identity differs.");
2413
+ const CheckpointInstalledPayload = z.object({
2414
+ operation_id: z.uuid(),
2415
+ transfer_id: z.uuid(),
2416
+ checkpoint_id: z.uuid(),
2417
+ archive_digest: digest,
2418
+ phase: z.enum(["installed", "failed"])
2419
+ });
2420
+ const CheckpointUploadStatusPayload = z.object({
2421
+ operation_id: z.uuid(),
2422
+ transfer_id: z.uuid(),
2423
+ checkpoint_id: z.uuid(),
2424
+ phase: z.enum(["uploaded", "failed"])
2425
+ });
2426
+ //#endregion
2427
+ //#region ../contracts/src/protocol/protocol-exec.ts
2428
+ const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
2429
+ const RestoreModeSchema = z.enum(["provider_installed", "controller_archive"]);
2430
+ const ExecSpecSchema = z.object({
2431
+ agentapi_native: z.boolean().default(false),
2432
+ setup: z.array(SetupStepSchema),
2433
+ harness: HarnessSchema.nullable(),
2434
+ workload: WaitingRuntimeSpecSchema.optional(),
2435
+ env: z.record(z.string(), z.string()),
2436
+ services: z.record(z.string(), ServiceSchema),
2437
+ previews: PreviewsSchema.optional(),
2438
+ display: DisplaySchema.optional(),
2439
+ terminal: z.object({
2440
+ command: z.array(z.string().min(1)).min(1),
2441
+ cwd: z.string().optional(),
2442
+ env: z.record(z.string(), z.string()),
2443
+ max_sessions: z.number().int().min(1).max(8),
2444
+ idle_timeout_seconds: z.number().int().positive(),
2445
+ replay_buffer_bytes: z.number().int().positive()
2446
+ }).nullable().default(null),
2447
+ timeouts: TimeoutsSchema,
2448
+ security: z.object({ writable_memory_paths: z.array(z.string()) }).default({ writable_memory_paths: [] }),
2449
+ network: z.discriminatedUnion("mode", [z.object({ mode: z.literal("unrestricted") }), z.object({
2450
+ mode: z.literal("restricted"),
2451
+ proxy_url: z.url(),
2452
+ health_url: z.url()
2453
+ })]).default({ mode: "unrestricted" }),
2454
+ launch_mode: z.enum(LAUNCH_MODES).default("create"),
2455
+ source: SourceDescriptorSchema.extend({
2456
+ url: z.url(),
2457
+ destination: z.string(),
2458
+ max_bytes: z.number().int().positive().optional(),
2459
+ max_files: z.number().int().positive().optional(),
2460
+ credential_expires_at: z.iso.datetime().nullable().default(null),
2461
+ credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0"), "credential must not contain NUL bytes").nullable().default(null)
2462
+ }).nullable().default(null),
2463
+ restore: z.object({
2464
+ mode: RestoreModeSchema,
2465
+ checkpoint_id: z.uuid(),
2466
+ origin_workspace_id: z.uuid(),
2467
+ transfer: RestoreTransferSpecSchema.nullable().default(null)
2468
+ }).nullable().default(null),
2469
+ persistence: z.object({
2470
+ mounts: z.array(z.object({
2471
+ name: z.string(),
2472
+ target: z.string()
2473
+ })),
2474
+ conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES)
2475
+ }),
2476
+ checkpoint_hook: z.object({
2477
+ command: z.array(z.string().min(1)).min(1),
2478
+ timeout_seconds: z.number().int().positive(),
2479
+ env: z.record(z.string(), z.string()),
2480
+ cwd: z.string().optional()
2481
+ }).nullable(),
2482
+ outputs: z.record(z.string(), z.unknown())
2483
+ });
2484
+ //#endregion
1358
2485
  //#region ../contracts/src/terminals/terminal.ts
1359
2486
  const TERMINAL_CHUNK_BYTES = 32768;
1360
2487
  const TERMINAL_REPLAY_BUFFER_BYTES = 65536;
@@ -1469,11 +2596,34 @@ const TerminalSessionSchema = z.object({
1469
2596
  CursorQuerySchema(200, 50);
1470
2597
  const TerminalSessionPageSchema = CursorPageSchema(TerminalSessionSchema);
1471
2598
  //#endregion
2599
+ //#region ../contracts/src/protocol/protocol-credentials.ts
2600
+ const WorkspaceCredentialSchema = z.object({
2601
+ lease_id: z.uuid(),
2602
+ path: z.string().regex(/^\/run\/pocketcoder\/secrets\/leases\/[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/),
2603
+ credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0")),
2604
+ expires_at: z.iso.datetime(),
2605
+ purpose: z.enum(["setup-issuer", "runtime-issuer"])
2606
+ });
2607
+ const CredentialRenewPayload = z.object({
2608
+ lease_id: z.uuid(),
2609
+ request_id: z.uuid()
2610
+ });
2611
+ const CredentialRenewedPayload = z.object({
2612
+ previous_lease_id: z.uuid(),
2613
+ request_id: z.uuid(),
2614
+ credential: WorkspaceCredentialSchema
2615
+ });
2616
+ const CredentialInstalledPayload = z.object({
2617
+ previous_lease_id: z.uuid(),
2618
+ lease_id: z.uuid()
2619
+ });
2620
+ //#endregion
1472
2621
  //#region ../contracts/src/protocol/protocol-stream.ts
1473
2622
  const ProxyStreamStartPayload = z.object({
1474
2623
  request_id: z.uuid(),
1475
2624
  status: z.number().int().min(100).max(599),
1476
- headers: z.record(z.string(), z.string())
2625
+ headers: z.record(z.string(), z.string()),
2626
+ cookies: z.array(z.string().max(4096)).max(16).optional()
1477
2627
  });
1478
2628
  const ProxyStreamChunkPayload = z.object({
1479
2629
  request_id: z.uuid(),
@@ -1541,7 +2691,6 @@ z.object({
1541
2691
  type: z.literal("lease_assignment"),
1542
2692
  input: ProviderInputSchema
1543
2693
  });
1544
- const SOURCE_CREDENTIAL_MAX_BYTES = 65536;
1545
2694
  const EnvelopeBase = z.object({
1546
2695
  v: z.union([
1547
2696
  z.literal(1),
@@ -1549,7 +2698,14 @@ const EnvelopeBase = z.object({
1549
2698
  z.literal(3),
1550
2699
  z.literal(4),
1551
2700
  z.literal(5),
1552
- z.literal(6)
2701
+ z.literal(6),
2702
+ z.literal(7),
2703
+ z.literal(8),
2704
+ z.literal(9),
2705
+ z.literal(10),
2706
+ z.literal(11),
2707
+ z.literal(12),
2708
+ z.literal(13)
1553
2709
  ]),
1554
2710
  workspace_id: z.uuid(),
1555
2711
  connection_id: z.uuid(),
@@ -1571,6 +2727,7 @@ const HeartbeatPayload = z.object({
1571
2727
  child: z.enum([
1572
2728
  "starting",
1573
2729
  "setup",
2730
+ "waiting",
1574
2731
  "running",
1575
2732
  "exited",
1576
2733
  "terminating"
@@ -1585,6 +2742,7 @@ const ProcessStatePayload = z.object({
1585
2742
  phase: z.enum([
1586
2743
  "starting",
1587
2744
  "setup",
2745
+ "waiting",
1588
2746
  "running",
1589
2747
  "exited",
1590
2748
  "terminating"
@@ -1666,6 +2824,38 @@ const RestoreStatusPayload = z.object({
1666
2824
  });
1667
2825
  const ConversationMessagePayload = ConversationMessageInputSchema;
1668
2826
  z.discriminatedUnion("type", [
2827
+ EnvelopeBase.extend({
2828
+ type: z.literal("runtime_ready"),
2829
+ payload: z.object({ incarnationId: z.string().min(1) })
2830
+ }),
2831
+ EnvelopeBase.extend({
2832
+ type: z.literal("finite_process"),
2833
+ payload: FiniteProcessFactSchema
2834
+ }),
2835
+ EnvelopeBase.extend({
2836
+ type: z.literal("process_absent"),
2837
+ payload: ProcessTargetPayload
2838
+ }),
2839
+ EnvelopeBase.extend({
2840
+ type: z.literal("process_cancel_result"),
2841
+ payload: ProcessCancelResultPayload
2842
+ }),
2843
+ EnvelopeBase.extend({
2844
+ type: z.literal("preview_socket"),
2845
+ payload: PreviewSocketPayload
2846
+ }),
2847
+ EnvelopeBase.extend({
2848
+ type: z.literal("credential_renew"),
2849
+ payload: CredentialRenewPayload
2850
+ }),
2851
+ EnvelopeBase.extend({
2852
+ type: z.literal("credential_installed"),
2853
+ payload: CredentialInstalledPayload
2854
+ }),
2855
+ EnvelopeBase.extend({
2856
+ type: z.literal("setup_complete"),
2857
+ payload: z.object({ request_id: z.uuid() })
2858
+ }),
1669
2859
  EnvelopeBase.extend({
1670
2860
  type: z.literal("registered"),
1671
2861
  payload: RegisteredPayload
@@ -1735,77 +2925,44 @@ z.discriminatedUnion("type", [
1735
2925
  payload: CheckpointStatusPayload
1736
2926
  }),
1737
2927
  EnvelopeBase.extend({
1738
- type: z.literal("output_published"),
1739
- payload: OutputPublishedPayload
1740
- }),
1741
- EnvelopeBase.extend({
1742
- type: z.literal("restore_status"),
1743
- payload: RestoreStatusPayload
2928
+ type: z.literal("checkpoint_prepared"),
2929
+ payload: CheckpointPreparedPayload
1744
2930
  }),
1745
2931
  EnvelopeBase.extend({
1746
- type: z.literal("conversation_message"),
1747
- payload: ConversationMessagePayload
2932
+ type: z.literal("checkpoint_installed"),
2933
+ payload: CheckpointInstalledPayload
1748
2934
  }),
1749
2935
  EnvelopeBase.extend({
1750
- type: z.literal("attachment_ack"),
1751
- payload: AttachmentAckPayload
2936
+ type: z.literal("checkpoint_upload_status"),
2937
+ payload: CheckpointUploadStatusPayload
1752
2938
  }),
1753
2939
  EnvelopeBase.extend({
1754
- type: z.literal("attachment_result"),
1755
- payload: AttachmentResultPayload
2940
+ type: z.literal("output_published"),
2941
+ payload: OutputPublishedPayload
1756
2942
  }),
1757
2943
  EnvelopeBase.extend({
1758
- type: z.literal("attachment_resolved"),
1759
- payload: AttachmentResolvedPayload
1760
- })
1761
- ]);
1762
- const ExecSpecSchema = z.object({
1763
- agentapi_native: z.boolean().default(false),
1764
- setup: z.array(SetupStepSchema),
1765
- harness: HarnessSchema,
1766
- env: z.record(z.string(), z.string()),
1767
- services: z.record(z.string(), ServiceSchema),
1768
- terminal: z.object({
1769
- command: z.array(z.string().min(1)).min(1),
1770
- cwd: z.string().optional(),
1771
- env: z.record(z.string(), z.string()),
1772
- max_sessions: z.number().int().min(1).max(8),
1773
- idle_timeout_seconds: z.number().int().positive(),
1774
- replay_buffer_bytes: z.number().int().positive()
1775
- }).nullable().default(null),
1776
- timeouts: TimeoutsSchema,
1777
- security: z.object({ writable_memory_paths: z.array(z.string()) }).default({ writable_memory_paths: [] }),
1778
- network: z.discriminatedUnion("mode", [z.object({ mode: z.literal("unrestricted") }), z.object({
1779
- mode: z.literal("restricted"),
1780
- proxy_url: z.url(),
1781
- health_url: z.url()
1782
- })]).default({ mode: "unrestricted" }),
1783
- launch_mode: z.enum(LAUNCH_MODES).default("create"),
1784
- source: SourceDescriptorSchema.extend({
1785
- url: z.url(),
1786
- destination: z.string(),
1787
- credential: z.string().min(1).max(SOURCE_CREDENTIAL_MAX_BYTES).refine((value) => !value.includes("\0"), "credential must not contain NUL bytes").nullable().default(null)
1788
- }).nullable().default(null),
1789
- restore: z.object({
1790
- checkpoint_id: z.uuid(),
1791
- origin_workspace_id: z.uuid()
1792
- }).nullable().default(null),
1793
- persistence: z.object({
1794
- mounts: z.array(z.object({
1795
- name: z.string(),
1796
- target: z.string()
1797
- })),
1798
- conversation_restore: z.enum(CONVERSATION_RESTORE_CAPABILITIES)
2944
+ type: z.literal("restore_status"),
2945
+ payload: RestoreStatusPayload
1799
2946
  }),
1800
- checkpoint_hook: z.object({
1801
- command: z.array(z.string().min(1)).min(1),
1802
- timeout_seconds: z.number().int().positive(),
1803
- env: z.record(z.string(), z.string()),
1804
- cwd: z.string().optional()
1805
- }).nullable(),
1806
- outputs: z.record(z.string(), z.unknown())
1807
- });
2947
+ EnvelopeBase.extend({
2948
+ type: z.literal("conversation_message"),
2949
+ payload: ConversationMessagePayload
2950
+ }),
2951
+ EnvelopeBase.extend({
2952
+ type: z.literal("attachment_ack"),
2953
+ payload: AttachmentAckPayload
2954
+ }),
2955
+ EnvelopeBase.extend({
2956
+ type: z.literal("attachment_result"),
2957
+ payload: AttachmentResultPayload
2958
+ }),
2959
+ EnvelopeBase.extend({
2960
+ type: z.literal("attachment_resolved"),
2961
+ payload: AttachmentResolvedPayload
2962
+ })
2963
+ ]);
1808
2964
  const RegisteredAckPayload = z.object({
2965
+ credentials: z.array(WorkspaceCredentialSchema).max(64).default([]),
1809
2966
  epoch: z.number().int().positive(),
1810
2967
  reconnect_credential: z.string().optional(),
1811
2968
  limits: z.object({
@@ -1824,7 +2981,9 @@ const ProxyRequestPayload = z.object({
1824
2981
  "POST",
1825
2982
  "PUT",
1826
2983
  "PATCH",
1827
- "DELETE"
2984
+ "DELETE",
2985
+ "HEAD",
2986
+ "OPTIONS"
1828
2987
  ]),
1829
2988
  path: z.string(),
1830
2989
  query: z.record(z.string(), z.string()).default({}),
@@ -1840,6 +2999,42 @@ const PrepareCheckpointPayload = z.object({
1840
2999
  deadline_ms: z.number().int().positive()
1841
3000
  });
1842
3001
  z.discriminatedUnion("type", [
3002
+ EnvelopeBase.extend({
3003
+ type: z.literal("process_start"),
3004
+ payload: ProcessStartPayload
3005
+ }),
3006
+ EnvelopeBase.extend({
3007
+ type: z.literal("process_lookup"),
3008
+ payload: ProcessTargetPayload
3009
+ }),
3010
+ EnvelopeBase.extend({
3011
+ type: z.literal("process_cancel"),
3012
+ payload: ProcessCancelPayload
3013
+ }),
3014
+ EnvelopeBase.extend({
3015
+ type: z.literal("process_ack"),
3016
+ payload: ProcessTargetPayload
3017
+ }),
3018
+ EnvelopeBase.extend({
3019
+ type: z.literal("screenshot_capture"),
3020
+ payload: ScreenshotCapturePayload
3021
+ }),
3022
+ EnvelopeBase.extend({
3023
+ type: z.literal("preview_socket"),
3024
+ payload: PreviewSocketPayload
3025
+ }),
3026
+ EnvelopeBase.extend({
3027
+ type: z.literal("credential_renewed"),
3028
+ payload: CredentialRenewedPayload
3029
+ }),
3030
+ EnvelopeBase.extend({
3031
+ type: z.literal("credential_installed_ack"),
3032
+ payload: CredentialInstalledPayload
3033
+ }),
3034
+ EnvelopeBase.extend({
3035
+ type: z.literal("setup_complete_ack"),
3036
+ payload: z.object({ request_id: z.uuid() })
3037
+ }),
1843
3038
  EnvelopeBase.extend({
1844
3039
  type: z.literal("registered_ack"),
1845
3040
  payload: RegisteredAckPayload
@@ -1888,6 +3083,14 @@ z.discriminatedUnion("type", [
1888
3083
  type: z.literal("prepare_checkpoint"),
1889
3084
  payload: PrepareCheckpointPayload
1890
3085
  }),
3086
+ EnvelopeBase.extend({
3087
+ type: z.literal("prepare_checkpoint_archive"),
3088
+ payload: PrepareCheckpointArchivePayload
3089
+ }),
3090
+ EnvelopeBase.extend({
3091
+ type: z.literal("checkpoint_upload"),
3092
+ payload: CheckpointUploadPayload
3093
+ }),
1891
3094
  EnvelopeBase.extend({
1892
3095
  type: z.literal("attachment_start"),
1893
3096
  payload: AttachmentStartPayload
@@ -1910,6 +3113,107 @@ z.discriminatedUnion("type", [
1910
3113
  })
1911
3114
  ]);
1912
3115
  //#endregion
3116
+ //#region ../contracts/src/recovery/account-purge.ts
3117
+ const Digest = z.string().regex(/^[a-f0-9]{64}$/);
3118
+ const AccountPurgeIntentSchema = z.strictObject({
3119
+ format: z.literal("pocketcoder-account-purge/v1"),
3120
+ accountId: z.uuid(),
3121
+ operationId: z.uuid(),
3122
+ createRequestDigest: Digest,
3123
+ purgeRequestDigest: Digest,
3124
+ admittedAt: z.iso.datetime()
3125
+ });
3126
+ z.strictObject({
3127
+ format: z.literal("pocketcoder-account-purge-completion/v1"),
3128
+ intent: AccountPurgeIntentSchema,
3129
+ completedAt: z.iso.datetime()
3130
+ });
3131
+ //#endregion
3132
+ //#region ../contracts/src/recovery/source-writer.ts
3133
+ const fileIdentity = z.strictObject({
3134
+ device: z.string().regex(/^\d+$/).max(20),
3135
+ inode: z.string().regex(/^[1-9]\d*$/).max(20)
3136
+ });
3137
+ z.strictObject({
3138
+ format: z.literal("pocketcoder-source-writer/v1"),
3139
+ directory: z.string().startsWith("/").max(4096),
3140
+ root: fileIdentity,
3141
+ lock: fileIdentity
3142
+ });
3143
+ z.strictObject({ manifest: z.union([TemplateManifestSchema, WorkloadTemplateSchema]) });
3144
+ z.object({
3145
+ name: z.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/),
3146
+ version: z.string().min(1).max(256)
3147
+ });
3148
+ z.strictObject({
3149
+ workspaceId: z.uuid(),
3150
+ incarnationId: z.string().min(1).max(256),
3151
+ profiles: z.array(WorkloadProfileSchema),
3152
+ storageProfiles: z.array(z.enum(["controller-readonly/v1", "shared-writer-readonly/v1"])),
3153
+ ceilings: WorkloadLimitsSchema,
3154
+ now: z.iso.datetime(),
3155
+ expiresAt: z.iso.datetime(),
3156
+ installed: InstalledWorkloadSchema.optional()
3157
+ });
3158
+ //#endregion
3159
+ //#region ../contracts/src/workspaces/execution-dispatch.ts
3160
+ const ExecutionPhaseSchema = z.enum([
3161
+ "accepted",
3162
+ "dispatching",
3163
+ "running",
3164
+ "terminal",
3165
+ "canceled",
3166
+ "unknown",
3167
+ "lost"
3168
+ ]);
3169
+ z.strictObject({
3170
+ acceptance: WorkloadAcceptanceSchema,
3171
+ conversationIdentity: ConversationRequestIdentitySchema.nullable(),
3172
+ outcome: JsonValueSchema.nullable(),
3173
+ acceptedAt: z.iso.datetime(),
3174
+ dispatchClaimedAt: z.iso.datetime().nullable(),
3175
+ phase: ExecutionPhaseSchema,
3176
+ cancel: z.strictObject({
3177
+ cancelId: z.uuid(),
3178
+ requestedAt: z.iso.datetime()
3179
+ }).nullable(),
3180
+ settledAt: z.iso.datetime().nullable(),
3181
+ acknowledgedAt: z.iso.datetime().nullable(),
3182
+ finalizedAt: z.iso.datetime().nullable()
3183
+ });
3184
+ z.strictObject({
3185
+ acceptance: WorkloadAcceptanceSchema,
3186
+ conversationIdentity: ConversationRequestIdentitySchema.optional()
3187
+ }).refine(({ acceptance, conversationIdentity }) => acceptance.execution.owner !== "conversation-request" || conversationIdentity !== void 0, {
3188
+ path: ["conversationIdentity"],
3189
+ message: "Conversation execution requires a conversation identity."
3190
+ });
3191
+ WorkloadCancelSchema.refine((cancel) => cancel.scope === "operation", { message: "Use workspace release for workspace cancellation" });
3192
+ z.strictObject({
3193
+ workspaceId: z.uuid(),
3194
+ callerPrincipalId: z.uuid(),
3195
+ incarnationId: z.string().min(1),
3196
+ template: WorkloadTemplateSnapshotSchema,
3197
+ producer: InstalledWorkloadSchema,
3198
+ readyAt: z.iso.datetime().nullable(),
3199
+ endedAt: z.iso.datetime().nullable()
3200
+ });
3201
+ z.discriminatedUnion("phase", [z.strictObject({
3202
+ execution: WorkloadExecutionReferenceSchema,
3203
+ phase: z.literal("absent")
3204
+ }), z.strictObject({
3205
+ execution: WorkloadExecutionReferenceSchema,
3206
+ fingerprint: WorkloadDigestSchema,
3207
+ phase: z.enum([
3208
+ "accepted",
3209
+ "running",
3210
+ "terminal",
3211
+ "canceled"
3212
+ ]),
3213
+ outcome: JsonValueSchema.nullable()
3214
+ })]);
3215
+ WorkloadCancelSchema.extend({ fingerprint: WorkloadDigestSchema });
3216
+ //#endregion
1913
3217
  //#region src/resources/admin/admin.ts
1914
3218
  var AdministrationApi = class {
1915
3219
  transport;
@@ -1987,44 +3291,8 @@ function responseError(response, body) {
1987
3291
  });
1988
3292
  }
1989
3293
  //#endregion
1990
- //#region src/resources/attachments/attachments.ts
3294
+ //#region src/resources/agent/agent.ts
1991
3295
  const DEFAULT_READY_TIMEOUT_MS = 12e4;
1992
- function contentDisposition(name) {
1993
- const clean = name.replace(/[\r\n]/g, "");
1994
- if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
1995
- return `attachment; filename*=UTF-8''${encodeURIComponent(clean)}`;
1996
- }
1997
- function progressStream(body, total, onProgress) {
1998
- const source = new Response(body).body ?? new ReadableStream({ start: (c) => c.close() });
1999
- let uploaded = 0;
2000
- return source.pipeThrough(new TransformStream({ transform(chunk, controller) {
2001
- uploaded += chunk.byteLength;
2002
- controller.enqueue(chunk);
2003
- onProgress(Math.min(uploaded, total), total);
2004
- } }));
2005
- }
2006
- var AttachmentsApi = class {
2007
- transport;
2008
- constructor(transport) {
2009
- this.transport = transport;
2010
- }
2011
- async upload(workspaceId, input) {
2012
- const id = (input.id ?? crypto.randomUUID()).toLowerCase();
2013
- const body = input.onProgress ? progressStream(input.body, input.sizeBytes, input.onProgress) : input.body;
2014
- const init = {
2015
- method: "PUT",
2016
- ...input.signal ? { signal: input.signal } : {},
2017
- headers: {
2018
- "content-type": input.mediaType ?? "application/octet-stream",
2019
- "content-disposition": contentDisposition(input.name),
2020
- "content-length": String(input.sizeBytes)
2021
- },
2022
- body,
2023
- ...body instanceof ReadableStream ? { duplex: "half" } : {}
2024
- };
2025
- return await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/attachments/${id}`, AttachmentDescriptorSchema, init);
2026
- }
2027
- };
2028
3296
  var AgentApi = class {
2029
3297
  transport;
2030
3298
  workspaces;
@@ -2058,6 +3326,58 @@ var AgentApi = class {
2058
3326
  }
2059
3327
  throw responseError(response, body);
2060
3328
  }
3329
+ interrupt(workspaceId, options = {}) {
3330
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/agent/interrupt`, AgentInterruptResponseSchema, {
3331
+ method: "POST",
3332
+ signal: options.signal
3333
+ });
3334
+ }
3335
+ };
3336
+ //#endregion
3337
+ //#region src/resources/attachments/attachments.ts
3338
+ function contentDisposition(name) {
3339
+ const clean = name.replace(/[\r\n]/g, "");
3340
+ if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
3341
+ return `attachment; filename*=UTF-8''${encodeURIComponent(clean)}`;
3342
+ }
3343
+ function progressStream(body, total, onProgress) {
3344
+ const source = new Response(body).body ?? new ReadableStream({ start: (c) => c.close() });
3345
+ let uploaded = 0;
3346
+ return source.pipeThrough(new TransformStream({ transform(chunk, controller) {
3347
+ uploaded += chunk.byteLength;
3348
+ controller.enqueue(chunk);
3349
+ onProgress(Math.min(uploaded, total), total);
3350
+ } }));
3351
+ }
3352
+ var AttachmentsApi = class {
3353
+ transport;
3354
+ constructor(transport) {
3355
+ this.transport = transport;
3356
+ }
3357
+ async upload(workspaceId, input) {
3358
+ const id = (input.id ?? crypto.randomUUID()).toLowerCase();
3359
+ let sha256 = input.sha256;
3360
+ if (!sha256) {
3361
+ if (input.body instanceof ReadableStream) throw new Error("Stream uploads require sha256 before body consumption.");
3362
+ const digest = await crypto.subtle.digest("SHA-256", await new Response(input.body).arrayBuffer());
3363
+ sha256 = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
3364
+ }
3365
+ if (!/^[0-9a-f]{64}$/.test(sha256)) throw new Error("sha256 must be 64 lowercase hexadecimal characters.");
3366
+ const body = input.onProgress ? progressStream(input.body, input.sizeBytes, input.onProgress) : input.body;
3367
+ const init = {
3368
+ method: "PUT",
3369
+ ...input.signal ? { signal: input.signal } : {},
3370
+ headers: {
3371
+ "content-type": input.mediaType ?? "application/octet-stream",
3372
+ "content-disposition": contentDisposition(input.name),
3373
+ "content-length": String(input.sizeBytes),
3374
+ "x-content-sha256": sha256
3375
+ },
3376
+ body,
3377
+ ...body instanceof ReadableStream ? { duplex: "half" } : {}
3378
+ };
3379
+ return await this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/attachments/${id}`, InputObjectReferenceSchema, init);
3380
+ }
2061
3381
  };
2062
3382
  //#endregion
2063
3383
  //#region src/transport/common.ts
@@ -2122,13 +3442,32 @@ var OperationsApi = class {
2122
3442
  };
2123
3443
  //#endregion
2124
3444
  //#region src/resources/conversations/conversations.ts
3445
+ function workspacePath(id) {
3446
+ return `/v1/workspaces/${encodeURIComponent(id)}`;
3447
+ }
3448
+ async function* liveEvents(body) {
3449
+ const decoder = new TextDecoder();
3450
+ let buffer = "";
3451
+ for await (const chunk of body) {
3452
+ buffer += decoder.decode(chunk, { stream: true });
3453
+ let end = buffer.indexOf("\n\n");
3454
+ while (end !== -1) {
3455
+ const block = buffer.slice(0, end);
3456
+ buffer = buffer.slice(end + 2);
3457
+ const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
3458
+ const event = data ? ConversationLiveEventSchema.safeParse(JSON.parse(data)) : null;
3459
+ if (event?.success) yield event.data;
3460
+ end = buffer.indexOf("\n\n");
3461
+ }
3462
+ }
3463
+ }
2125
3464
  var ConversationsApi = class {
2126
3465
  transport;
2127
3466
  constructor(transport) {
2128
3467
  this.transport = transport;
2129
3468
  }
2130
3469
  async list(id, query = {}, options = {}) {
2131
- const body = await this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/conversation?${queryString({
3470
+ const body = await this.transport.request(`${workspacePath(id)}/conversation?${queryString({
2132
3471
  cursor: query.cursor,
2133
3472
  limit: query.limit ?? 100
2134
3473
  })}`, ConversationPageSchema, options);
@@ -2139,12 +3478,37 @@ var ConversationsApi = class {
2139
3478
  };
2140
3479
  }
2141
3480
  async acceptRequest(id, identity, options = {}) {
2142
- return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/conversation/requests`, ConversationRequestReceiptSchema, {
3481
+ return this.transport.request(`${workspacePath(id)}/conversation/requests`, ConversationRequestReceiptSchema, {
2143
3482
  ...options,
2144
3483
  method: "POST",
2145
3484
  body: JSON.stringify(identity)
2146
3485
  });
2147
3486
  }
3487
+ async listConversations(id, options = {}) {
3488
+ return (await this.transport.request(`${workspacePath(id)}/conversations`, ConversationListSchema, options)).items;
3489
+ }
3490
+ async read(id, conversationId, query = {}, options = {}) {
3491
+ const body = await this.transport.request(`${workspacePath(id)}/conversations/${encodeURIComponent(conversationId)}?${queryString({
3492
+ cursor: query.cursor,
3493
+ limit: query.limit ?? 100
3494
+ })}`, ConversationDocumentPageSchema, options);
3495
+ return {
3496
+ ...page(body),
3497
+ conversation: body.conversation,
3498
+ protocol: body.protocol
3499
+ };
3500
+ }
3501
+ async delete(id, conversationId, options = {}) {
3502
+ await this.transport.request(`${workspacePath(id)}/conversations/${encodeURIComponent(conversationId)}`, z.undefined(), {
3503
+ ...options,
3504
+ method: "DELETE"
3505
+ });
3506
+ }
3507
+ async *live(id, options = {}) {
3508
+ const path = options.conversationId ? `${workspacePath(id)}/conversations/${encodeURIComponent(options.conversationId)}/live` : `${workspacePath(id)}/conversation/live`;
3509
+ const response = await this.transport.stream(path, options.signal);
3510
+ if (response.body) yield* liveEvents(response.body);
3511
+ }
2148
3512
  };
2149
3513
  //#endregion
2150
3514
  //#region src/resources/diagnostics/diagnostics.ts
@@ -2175,8 +3539,40 @@ var NetworkEventsApi = class extends WorkspaceCursorApi {
2175
3539
  }
2176
3540
  };
2177
3541
  var OutputsApi = class extends WorkspaceCursorApi {
3542
+ binaryTransport;
3543
+ async download(workspaceId, outputId, options = {}) {
3544
+ const response = await this.binaryTransport.raw(`/v1/workspaces/${encodeURIComponent(workspaceId)}/outputs/${encodeURIComponent(outputId)}/content`, options);
3545
+ if (!response.ok) throw responseError(response, await response.json());
3546
+ return response;
3547
+ }
2178
3548
  constructor(transport) {
2179
3549
  super(transport, "outputs", OutputPageSchema);
3550
+ this.binaryTransport = transport;
3551
+ }
3552
+ };
3553
+ //#endregion
3554
+ //#region src/resources/displays/displays.ts
3555
+ var DisplaysApi = class {
3556
+ transport;
3557
+ constructor(transport) {
3558
+ this.transport = transport;
3559
+ }
3560
+ capture(workspaceId, options = {}) {
3561
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/display/screenshot`, ScreenshotResourceSchema, {
3562
+ method: "POST",
3563
+ body: "{}",
3564
+ signal: options.signal
3565
+ });
3566
+ }
3567
+ open(workspaceId, control = false, options = {}) {
3568
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/display`, PreviewOpenResponseSchema, {
3569
+ method: "POST",
3570
+ body: JSON.stringify({
3571
+ control,
3572
+ session: options.session ?? { mode: "local" }
3573
+ }),
3574
+ signal: options.signal
3575
+ });
2180
3576
  }
2181
3577
  };
2182
3578
  //#endregion
@@ -2204,6 +3600,9 @@ var KeysApi = class {
2204
3600
  cursor = page.next_cursor ?? void 0;
2205
3601
  } while (cursor);
2206
3602
  }
3603
+ expiring(query = {}, options = {}) {
3604
+ return this.transport.request(`/v1/keys/expiring?${queryString({ within_hours: query.withinHours })}`, ExpiringKeyListResponseSchema, options);
3605
+ }
2207
3606
  issue(principalId, input, options = {}) {
2208
3607
  return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys`, KeyIssueResponseSchema, {
2209
3608
  method: "POST",
@@ -2211,6 +3610,13 @@ var KeysApi = class {
2211
3610
  signal: options.signal
2212
3611
  });
2213
3612
  }
3613
+ rotate(principalId, keyId, input, options = {}) {
3614
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}/rotate`, KeyIssueResponseSchema, {
3615
+ method: "POST",
3616
+ body: JSON.stringify(input),
3617
+ signal: options.signal
3618
+ });
3619
+ }
2214
3620
  revoke(principalId, keyId, options = {}) {
2215
3621
  return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}`, z.object({ revoked: z.literal(true) }), {
2216
3622
  method: "DELETE",
@@ -2242,6 +3648,76 @@ var RecoveryApi = class {
2242
3648
  }
2243
3649
  };
2244
3650
  //#endregion
3651
+ //#region src/resources/previews/previews.ts
3652
+ var PreviewsApi = class {
3653
+ transport;
3654
+ constructor(transport) {
3655
+ this.transport = transport;
3656
+ }
3657
+ list(workspaceId, options = {}) {
3658
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/previews`, PreviewListSchema, options);
3659
+ }
3660
+ open(workspaceId, name, options = {}) {
3661
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(workspaceId)}/previews/${encodeURIComponent(name)}`, PreviewOpenResponseSchema, {
3662
+ method: "POST",
3663
+ body: JSON.stringify({ session: options.session ?? { mode: "local" } }),
3664
+ signal: options.signal
3665
+ });
3666
+ }
3667
+ };
3668
+ //#endregion
3669
+ //#region src/resources/principals/principals.ts
3670
+ var PrincipalsApi = class {
3671
+ transport;
3672
+ constructor(transport) {
3673
+ this.transport = transport;
3674
+ }
3675
+ list(options = {}) {
3676
+ return this.transport.request("/v1/principals", PrincipalListResponseSchema, options);
3677
+ }
3678
+ get(principalId, options = {}) {
3679
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}`, PrincipalResourceSchema, options);
3680
+ }
3681
+ create(input, options = {}) {
3682
+ return this.transport.request("/v1/principals", PrincipalResourceSchema, {
3683
+ method: "POST",
3684
+ body: JSON.stringify(input),
3685
+ signal: options.signal
3686
+ });
3687
+ }
3688
+ update(principalId, input, options = {}) {
3689
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}`, PrincipalResourceSchema, {
3690
+ method: "PATCH",
3691
+ body: JSON.stringify(input),
3692
+ signal: options.signal
3693
+ });
3694
+ }
3695
+ };
3696
+ //#endregion
3697
+ //#region src/resources/secrets/secrets.ts
3698
+ var SecretsApi = class {
3699
+ transport;
3700
+ constructor(transport) {
3701
+ this.transport = transport;
3702
+ }
3703
+ async put(name, input, options = {}) {
3704
+ return this.transport.request(`/v1/secrets/${encodeURIComponent(name)}`, SecretResourceSchema, {
3705
+ ...options,
3706
+ method: "PUT",
3707
+ body: JSON.stringify(input)
3708
+ });
3709
+ }
3710
+ async list(options = {}) {
3711
+ return (await this.transport.request("/v1/secrets", SecretListSchema, options)).items;
3712
+ }
3713
+ async retire(name, options = {}) {
3714
+ return this.transport.request(`/v1/secrets/${encodeURIComponent(name)}`, SecretResourceSchema, {
3715
+ ...options,
3716
+ method: "DELETE"
3717
+ });
3718
+ }
3719
+ };
3720
+ //#endregion
2245
3721
  //#region src/resources/templates/templates.ts
2246
3722
  var TemplatesApi = class {
2247
3723
  transport;
@@ -2255,7 +3731,27 @@ var TemplatesApi = class {
2255
3731
  })}`, TemplatePageSchema, options));
2256
3732
  }
2257
3733
  async list(options = {}) {
2258
- return (await this.page({}, options)).items;
3734
+ const items = [];
3735
+ let cursor;
3736
+ do {
3737
+ const result = await this.page({ cursor }, options);
3738
+ items.push(...result.items);
3739
+ cursor = result.nextCursor ?? void 0;
3740
+ } while (cursor);
3741
+ return items;
3742
+ }
3743
+ async publish(manifest, options = {}) {
3744
+ return this.transport.request("/v1/templates", TemplateListItemSchema, {
3745
+ ...options,
3746
+ method: "POST",
3747
+ body: JSON.stringify({ manifest })
3748
+ });
3749
+ }
3750
+ async retire(name, version, options = {}) {
3751
+ return this.transport.request(`/v1/templates/${encodeURIComponent(name)}/${encodeURIComponent(version)}`, TemplateListItemSchema, {
3752
+ ...options,
3753
+ method: "DELETE"
3754
+ });
2259
3755
  }
2260
3756
  };
2261
3757
  //#endregion
@@ -2519,6 +4015,20 @@ var PocketCoderTransport = class {
2519
4015
  await delay(100 * 2 ** attempt, signal);
2520
4016
  }
2521
4017
  }
4018
+ async stream(path, signal) {
4019
+ const response = await this.fetchImpl(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`, {
4020
+ headers: {
4021
+ authorization: `Bearer ${this.apiKey}`,
4022
+ accept: "text/event-stream"
4023
+ },
4024
+ ...signal ? { signal } : {}
4025
+ });
4026
+ if (!response.ok) {
4027
+ const text = await response.text();
4028
+ throw responseError(response, text ? JSON.parse(text) : void 0);
4029
+ }
4030
+ return response;
4031
+ }
2522
4032
  async request(path, schema, init = {}) {
2523
4033
  const response = await this.raw(path, init);
2524
4034
  const text = await response.text();
@@ -2583,8 +4093,11 @@ async function delay(milliseconds, signal) {
2583
4093
  //#region src/client.ts
2584
4094
  var PocketCoderClient = class {
2585
4095
  transport;
4096
+ secrets;
2586
4097
  templates;
2587
4098
  workspaces;
4099
+ previews;
4100
+ displays;
2588
4101
  attachments;
2589
4102
  agent;
2590
4103
  conversations;
@@ -2595,12 +4108,16 @@ var PocketCoderClient = class {
2595
4108
  outputs;
2596
4109
  administration;
2597
4110
  terminals;
4111
+ principals;
2598
4112
  keys;
2599
4113
  recovery;
2600
4114
  constructor(config, fetchImpl = fetch) {
2601
4115
  this.transport = new PocketCoderTransport(config, fetchImpl);
4116
+ this.secrets = new SecretsApi(this.transport);
2602
4117
  this.templates = new TemplatesApi(this.transport);
2603
4118
  this.workspaces = new WorkspacesApi(this.transport);
4119
+ this.previews = new PreviewsApi(this.transport);
4120
+ this.displays = new DisplaysApi(this.transport);
2604
4121
  this.attachments = new AttachmentsApi(this.transport);
2605
4122
  this.agent = new AgentApi(this.transport, this.workspaces);
2606
4123
  this.conversations = new ConversationsApi(this.transport);
@@ -2611,6 +4128,7 @@ var PocketCoderClient = class {
2611
4128
  this.outputs = new OutputsApi(this.transport);
2612
4129
  this.administration = new AdministrationApi(this.transport);
2613
4130
  this.terminals = new TerminalsApi(this.transport);
4131
+ this.principals = new PrincipalsApi(this.transport);
2614
4132
  this.keys = new KeysApi(this.transport);
2615
4133
  this.recovery = new RecoveryApi(this.transport);
2616
4134
  }
@@ -2763,4 +4281,4 @@ var WorkspaceTurnResolver = class {
2763
4281
  }
2764
4282
  };
2765
4283
  //#endregion
2766
- export { AdministrationApi, AgentApi, AgentNotReadyError, AttachmentsApi, CheckpointsApi, ConversationGoneError, ConversationsApi, KeysApi, LogsApi, NetworkEventsApi, OperationsApi, OutputsApi, PocketCoderClient, PocketCoderError, RecoveryApi, TERMINAL_WORKSPACE_STATES, TemplatesApi, TerminalConnection, TerminalsApi, WorkspaceTerminalError, WorkspaceTurnResolutionError, WorkspaceTurnResolver, WorkspacesApi, isPocketCoderErrorCode, splitAttachmentManifest };
4284
+ export { AdministrationApi, AgentApi, AgentNotReadyError, AttachmentsApi, CheckpointsApi, ConversationGoneError, ConversationsApi, KeysApi, LogsApi, NetworkEventsApi, OperationsApi, OutputsApi, PocketCoderClient, PocketCoderError, PrincipalsApi, RecoveryApi, SecretsApi, TERMINAL_WORKSPACE_STATES, TemplatesApi, TerminalConnection, TerminalsApi, WorkspaceTerminalError, WorkspaceTurnResolutionError, WorkspaceTurnResolver, WorkspacesApi, isPocketCoderErrorCode, splitAttachmentManifest };