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