@pstdio/pocketcoder-sdk 0.4.0 → 0.6.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +709 -470
  2. package/dist/index.js +565 -371
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,220 @@
1
1
  import { z } from "zod";
2
2
  import { isIP } from "node:net";
3
- //#region ../contracts/src/pagination.ts
3
+ //#region ../contracts/src/attachments/attachment.ts
4
+ const ATTACHMENT_MAX_FILE_BYTES = 26214400;
5
+ const ATTACHMENT_CHUNK_BYTES = 524288;
6
+ const AttachmentMediaTypeSchema = z.string().regex(/^[\w.+-]+\/[\w.+-]+$/).max(255);
7
+ const AttachmentDescriptorSchema = z.object({
8
+ id: z.uuid(),
9
+ name: z.string().min(1).max(255),
10
+ path: z.string().min(1),
11
+ media_type: AttachmentMediaTypeSchema,
12
+ size_bytes: z.number().int().min(0).max(ATTACHMENT_MAX_FILE_BYTES),
13
+ sha256: z.string().regex(/^[0-9a-f]{64}$/)
14
+ });
15
+ z.looseObject({
16
+ type: z.literal("user"),
17
+ content: z.string(),
18
+ attachment_ids: z.array(z.uuid()).min(1).max(10).refine((ids) => new Set(ids).size === ids.length, "attachment_ids must be unique").optional()
19
+ });
20
+ const MANIFEST_OPEN = "<pocketcoder-attachments>";
21
+ const MANIFEST_CLOSE = "</pocketcoder-attachments>";
22
+ function splitAttachmentManifest(content) {
23
+ const open = content.lastIndexOf(MANIFEST_OPEN);
24
+ if (open < 0 || !content.trimEnd().endsWith(MANIFEST_CLOSE)) return {
25
+ text: content,
26
+ attachments: null
27
+ };
28
+ const close = content.lastIndexOf(MANIFEST_CLOSE);
29
+ const parsed = z.array(AttachmentDescriptorSchema).safeParse(safeJson(content.slice(open + 25, close)));
30
+ if (!parsed.success) return {
31
+ text: content,
32
+ attachments: null
33
+ };
34
+ return {
35
+ text: content.slice(0, open).trimEnd(),
36
+ attachments: parsed.data
37
+ };
38
+ }
39
+ function safeJson(value) {
40
+ try {
41
+ return JSON.parse(value);
42
+ } catch {
43
+ return;
44
+ }
45
+ }
46
+ const AttachmentStartPayload = z.object({
47
+ operation_id: z.uuid(),
48
+ attachment_id: z.uuid(),
49
+ name: z.string().min(1).max(255),
50
+ media_type: AttachmentMediaTypeSchema,
51
+ size_bytes: z.number().int().min(0).max(ATTACHMENT_MAX_FILE_BYTES)
52
+ });
53
+ const AttachmentChunkPayload = z.object({
54
+ operation_id: z.uuid(),
55
+ seq: z.number().int().nonnegative(),
56
+ content_b64: z.string().max(Math.ceil(ATTACHMENT_CHUNK_BYTES / 3) * 4 + 4)
57
+ });
58
+ const AttachmentFinishPayload = z.object({ operation_id: z.uuid() });
59
+ const AttachmentAbortPayload = z.object({
60
+ operation_id: z.uuid(),
61
+ reason: z.string().max(512)
62
+ });
63
+ const AttachmentResolvePayload = z.object({
64
+ operation_id: z.uuid(),
65
+ attachment_ids: z.array(z.uuid()).min(1).max(10)
66
+ });
67
+ const AttachmentAckPayload = z.object({
68
+ operation_id: z.uuid(),
69
+ seq: z.number().int().nonnegative(),
70
+ received_bytes: z.number().int().nonnegative()
71
+ });
72
+ const AttachmentResultPayload = z.object({
73
+ operation_id: z.uuid(),
74
+ status: z.enum([
75
+ "created",
76
+ "existing",
77
+ "conflict",
78
+ "failed"
79
+ ]),
80
+ descriptor: AttachmentDescriptorSchema.optional(),
81
+ failure_code: z.enum([
82
+ "invalid",
83
+ "too_large",
84
+ "sequence",
85
+ "io",
86
+ "interrupted"
87
+ ]).optional(),
88
+ detail: z.string().max(512).optional()
89
+ });
90
+ const AttachmentResolvedPayload = z.object({
91
+ operation_id: z.uuid(),
92
+ descriptors: z.array(AttachmentDescriptorSchema).optional(),
93
+ missing_id: z.uuid().optional()
94
+ });
95
+ //#endregion
96
+ //#region ../contracts/src/common/scopes.ts
97
+ const SCOPES = [
98
+ "templates:read",
99
+ "workspaces:create",
100
+ "workspaces:read",
101
+ "workspaces:cancel",
102
+ "workspaces:purge",
103
+ "workspaces:recover",
104
+ "keys:read",
105
+ "keys:write",
106
+ "workspaces:preserve",
107
+ "workspaces:restore",
108
+ "checkpoints:read",
109
+ "checkpoints:delete",
110
+ "outputs:read",
111
+ "conversations:read",
112
+ "conversations:delete",
113
+ "services:relay",
114
+ "attachments:write",
115
+ "logs:read",
116
+ "network:read",
117
+ "terminal:attach",
118
+ "terminal:read",
119
+ "admin"
120
+ ];
121
+ z.strictObject({
122
+ request_id: z.string().min(1).max(128).regex(/^[A-Za-z0-9._:-]+$/),
123
+ scopes: z.array(z.enum(SCOPES)).min(1).max(SCOPES.length),
124
+ expires_at: z.iso.datetime()
125
+ });
126
+ const KeyResourceSchema = z.object({
127
+ id: z.uuid(),
128
+ principal_id: z.uuid(),
129
+ scopes: z.array(z.string()),
130
+ effective_scopes: z.array(z.string()),
131
+ managed_principal_ids: z.array(z.uuid()),
132
+ issuance_request_id: z.string().nullable(),
133
+ created_at: z.iso.datetime(),
134
+ expires_at: z.iso.datetime().nullable(),
135
+ revoked_at: z.iso.datetime().nullable(),
136
+ last_used_at: z.iso.datetime().nullable()
137
+ });
138
+ const KeyIssueResponseSchema = z.object({
139
+ key: KeyResourceSchema,
140
+ token: z.string().nullable()
141
+ });
142
+ const KeyListResponseSchema = z.object({
143
+ items: z.array(KeyResourceSchema),
144
+ next_cursor: z.uuid().nullable()
145
+ });
146
+ //#endregion
147
+ //#region ../contracts/src/common/duration.ts
148
+ const DURATION_RE = /^(\d+)(ms|s|m|h)$/;
149
+ function isDuration(value) {
150
+ return DURATION_RE.test(value);
151
+ }
152
+ //#endregion
153
+ //#region ../contracts/src/common/errors.ts
154
+ const ERROR_CODES = {
155
+ "auth.invalid_key": 401,
156
+ "auth.missing_scope": 403,
157
+ "auth.disabled_principal": 403,
158
+ "principal.not_found": 404,
159
+ "key.not_found": 404,
160
+ "validation.invalid": 400,
161
+ "idempotency.conflict": 409,
162
+ "capacity.queue_full": 429,
163
+ "capacity.waiters_full": 429,
164
+ "template.not_found": 404,
165
+ "template.version_not_found": 404,
166
+ "template.not_authorized": 403,
167
+ "workspace.not_found": 404,
168
+ "workspace.external_id_conflict": 409,
169
+ "workspace.not_ready": 409,
170
+ "workspace.terminal": 410,
171
+ "workspace.disconnected": 503,
172
+ "terminal.not_declared": 422,
173
+ "terminal.session_limit": 409,
174
+ "terminal.session_not_found": 404,
175
+ "terminal.session_closed": 409,
176
+ "terminal.protocol_unsupported": 426,
177
+ "workspace.persistence_not_enabled": 409,
178
+ "workspace.preserving": 409,
179
+ "checkpoint.not_found": 404,
180
+ "checkpoint.not_ready": 409,
181
+ "checkpoint.none_ready": 409,
182
+ "checkpoint.corrupt": 409,
183
+ "checkpoint.quota_exceeded": 413,
184
+ "checkpoint.in_use": 409,
185
+ "restore.template_not_authorized": 403,
186
+ "restore.image_unavailable": 409,
187
+ "restore.incompatible": 409,
188
+ "resume.unsupported": 409,
189
+ "conversation.expired": 410,
190
+ "conversation.deleted": 410,
191
+ "operation.conflict": 409,
192
+ "source.not_allowed": 422,
193
+ "source.invalid_revision": 422,
194
+ "secret.unavailable": 503,
195
+ "storage.capacity_exhausted": 507,
196
+ "relay.body_too_large": 413,
197
+ "relay.route_not_allowed": 422,
198
+ "relay.streaming_unsupported": 409,
199
+ "relay.deadline_exceeded": 504,
200
+ "relay.upstream_error": 502,
201
+ "attachment.invalid": 400,
202
+ "attachment.too_large": 413,
203
+ "attachment.not_found": 404,
204
+ "attachment.conflict": 409,
205
+ "attachment.unsupported": 409,
206
+ "attachment.interrupted": 503,
207
+ "internal.error": 500
208
+ };
209
+ const errorCodes = Object.keys(ERROR_CODES);
210
+ const ErrorEnvelopeSchema = z.object({ error: z.object({
211
+ code: z.enum(errorCodes),
212
+ message: z.string().min(1),
213
+ request_id: z.string().min(1),
214
+ details: z.record(z.string(), z.unknown()).optional()
215
+ }) });
216
+ //#endregion
217
+ //#region ../contracts/src/common/pagination.ts
4
218
  const CursorSchema = z.string().min(1).max(2048);
5
219
  function CursorPageSchema(item) {
6
220
  return z.object({
@@ -15,7 +229,7 @@ function CursorQuerySchema(maxLimit, defaultLimit) {
15
229
  });
16
230
  }
17
231
  //#endregion
18
- //#region ../contracts/src/conversation.ts
232
+ //#region ../contracts/src/conversations/conversation.ts
19
233
  const CONVERSATION_ROLES = [
20
234
  "user",
21
235
  "assistant",
@@ -43,13 +257,61 @@ const ConversationResumeOutcomeSchema = z.object({
43
257
  checkpoint_id: z.uuid()
44
258
  });
45
259
  //#endregion
46
- //#region ../contracts/src/duration.ts
47
- const DURATION_RE = /^(\d+)(ms|s|m|h)$/;
48
- function isDuration(value) {
49
- return DURATION_RE.test(value);
260
+ //#region ../contracts/src/network/network.ts
261
+ const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
262
+ function isDomain(value) {
263
+ const domain = value.startsWith("*.") ? value.slice(2) : value;
264
+ return domain.length <= 253 && !domain.endsWith(".") && isIP(domain) === 0 && domain.split(".").length >= 2 && domain.split(".").every((label) => DOMAIN_LABEL.test(label));
50
265
  }
266
+ const NetworkRuleSchema = z.object({
267
+ domain: z.string().refine(isDomain, "expected a lowercase ASCII domain or leading *. wildcard"),
268
+ ports: z.array(z.number().int().min(1).max(65535)).min(1).max(32).default([80, 443]),
269
+ allowPrivate: z.boolean().default(false)
270
+ }).superRefine((rule, ctx) => {
271
+ if (new Set(rule.ports).size !== rule.ports.length) ctx.addIssue({
272
+ code: "custom",
273
+ path: ["ports"],
274
+ message: "ports must be unique"
275
+ });
276
+ });
277
+ const UnrestrictedNetworkPolicySchema = z.object({ mode: z.literal("unrestricted") });
278
+ const RestrictedNetworkPolicySchema = z.object({
279
+ mode: z.literal("restricted"),
280
+ allow: z.array(NetworkRuleSchema).max(256).default([])
281
+ });
282
+ const NetworkPolicySchema = z.preprocess((value) => value ?? { mode: "unrestricted" }, z.discriminatedUnion("mode", [UnrestrictedNetworkPolicySchema, RestrictedNetworkPolicySchema]));
283
+ const NETWORK_STATES = [
284
+ "disabled",
285
+ "starting",
286
+ "ready",
287
+ "degraded"
288
+ ];
289
+ const NetworkEventInputSchema = z.object({
290
+ source_seq: z.number().int().positive(),
291
+ occurred_at: z.iso.datetime(),
292
+ decision: z.enum(["allow", "deny"]),
293
+ transport: z.enum(["http", "https"]),
294
+ host: z.string().min(1).max(253),
295
+ port: z.number().int().min(1).max(65535),
296
+ method: z.string().max(32).nullable().default(null),
297
+ path: z.string().max(2048).refine((path) => !path.includes("?") && !path.includes("#"), "path cannot contain query or fragment").nullable().default(null),
298
+ matched_rule: z.string().max(512).nullable().default(null),
299
+ reason: z.string().min(1).max(128)
300
+ });
301
+ z.object({
302
+ source_session_id: z.uuid(),
303
+ events: z.array(NetworkEventInputSchema).min(1).max(100)
304
+ });
305
+ const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).extend({
306
+ seq: z.number().int().positive(),
307
+ workspace_id: z.uuid(),
308
+ source_session_id: z.uuid(),
309
+ source_seq: z.number().int().positive()
310
+ });
311
+ CursorQuerySchema(200, 100);
312
+ const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
51
313
  //#endregion
52
- //#region ../contracts/src/persistence.ts
314
+ //#region ../contracts/src/persistence/persistence.ts
53
315
  const DurationValueSchema = z.string().refine(isDuration, { message: "expected a duration like 15s, 20m, or 2h" });
54
316
  const LAUNCH_MODES = ["create", "restore"];
55
317
  const CONVERSATION_RESTORE_CAPABILITIES = [
@@ -117,7 +379,8 @@ const OPERATION_KINDS = [
117
379
  "preserve",
118
380
  "restore",
119
381
  "verify",
120
- "delete"
382
+ "delete",
383
+ "purge"
121
384
  ];
122
385
  const OPERATION_STATES = [
123
386
  "pending",
@@ -194,61 +457,7 @@ z.object({
194
457
  file_count: z.number().int().nonnegative()
195
458
  });
196
459
  //#endregion
197
- //#region ../contracts/src/network.ts
198
- const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
199
- function isDomain(value) {
200
- const domain = value.startsWith("*.") ? value.slice(2) : value;
201
- return domain.length <= 253 && !domain.endsWith(".") && isIP(domain) === 0 && domain.split(".").length >= 2 && domain.split(".").every((label) => DOMAIN_LABEL.test(label));
202
- }
203
- const NetworkRuleSchema = z.object({
204
- domain: z.string().refine(isDomain, "expected a lowercase ASCII domain or leading *. wildcard"),
205
- ports: z.array(z.number().int().min(1).max(65535)).min(1).max(32).default([80, 443]),
206
- allowPrivate: z.boolean().default(false)
207
- }).superRefine((rule, ctx) => {
208
- if (new Set(rule.ports).size !== rule.ports.length) ctx.addIssue({
209
- code: "custom",
210
- path: ["ports"],
211
- message: "ports must be unique"
212
- });
213
- });
214
- const UnrestrictedNetworkPolicySchema = z.object({ mode: z.literal("unrestricted") });
215
- const RestrictedNetworkPolicySchema = z.object({
216
- mode: z.literal("restricted"),
217
- allow: z.array(NetworkRuleSchema).max(256).default([])
218
- });
219
- const NetworkPolicySchema = z.preprocess((value) => value ?? { mode: "unrestricted" }, z.discriminatedUnion("mode", [UnrestrictedNetworkPolicySchema, RestrictedNetworkPolicySchema]));
220
- const NETWORK_STATES = [
221
- "disabled",
222
- "starting",
223
- "ready",
224
- "degraded"
225
- ];
226
- const NetworkEventInputSchema = z.object({
227
- source_seq: z.number().int().positive(),
228
- occurred_at: z.iso.datetime(),
229
- decision: z.enum(["allow", "deny"]),
230
- transport: z.enum(["http", "https"]),
231
- host: z.string().min(1).max(253),
232
- port: z.number().int().min(1).max(65535),
233
- method: z.string().max(32).nullable().default(null),
234
- path: z.string().max(2048).refine((path) => !path.includes("?") && !path.includes("#"), "path cannot contain query or fragment").nullable().default(null),
235
- matched_rule: z.string().max(512).nullable().default(null),
236
- reason: z.string().min(1).max(128)
237
- });
238
- z.object({
239
- source_session_id: z.uuid(),
240
- events: z.array(NetworkEventInputSchema).min(1).max(100)
241
- });
242
- const NetworkEventSchema = NetworkEventInputSchema.omit({ source_seq: true }).extend({
243
- seq: z.number().int().positive(),
244
- workspace_id: z.uuid(),
245
- source_session_id: z.uuid(),
246
- source_seq: z.number().int().positive()
247
- });
248
- CursorQuerySchema(200, 100);
249
- const NetworkEventPageSchema = CursorPageSchema(NetworkEventSchema);
250
- //#endregion
251
- //#region ../contracts/src/workspace.ts
460
+ //#region ../contracts/src/workspaces/workspace.ts
252
461
  const WORKSPACE_STATES = [
253
462
  "queued",
254
463
  "provisioning",
@@ -377,7 +586,7 @@ z.object({
377
586
  cursor: z.string().optional()
378
587
  });
379
588
  //#endregion
380
- //#region ../contracts/src/api.ts
589
+ //#region ../contracts/src/protocol/api.ts
381
590
  const TemplatePageSchema = CursorPageSchema(z.object({
382
591
  name: z.string(),
383
592
  version: z.string(),
@@ -438,161 +647,6 @@ const StoragePruneResultSchema = z.object({
438
647
  skipped: z.number().int().nonnegative(),
439
648
  transcripts_deleted: z.number().int().nonnegative()
440
649
  });
441
- //#endregion
442
- //#region ../contracts/src/attachment.ts
443
- const ATTACHMENT_MAX_FILE_BYTES = 26214400;
444
- const ATTACHMENT_CHUNK_BYTES = 524288;
445
- const AttachmentMediaTypeSchema = z.string().regex(/^[\w.+-]+\/[\w.+-]+$/).max(255);
446
- const AttachmentDescriptorSchema = z.object({
447
- id: z.uuid(),
448
- name: z.string().min(1).max(255),
449
- path: z.string().min(1),
450
- media_type: AttachmentMediaTypeSchema,
451
- size_bytes: z.number().int().min(0).max(ATTACHMENT_MAX_FILE_BYTES),
452
- sha256: z.string().regex(/^[0-9a-f]{64}$/)
453
- });
454
- z.looseObject({
455
- type: z.literal("user"),
456
- content: z.string(),
457
- attachment_ids: z.array(z.uuid()).min(1).max(10).refine((ids) => new Set(ids).size === ids.length, "attachment_ids must be unique").optional()
458
- });
459
- const MANIFEST_OPEN = "<pocketcoder-attachments>";
460
- const MANIFEST_CLOSE = "</pocketcoder-attachments>";
461
- function splitAttachmentManifest(content) {
462
- const open = content.lastIndexOf(MANIFEST_OPEN);
463
- if (open < 0 || !content.trimEnd().endsWith(MANIFEST_CLOSE)) return {
464
- text: content,
465
- attachments: null
466
- };
467
- const close = content.lastIndexOf(MANIFEST_CLOSE);
468
- const parsed = z.array(AttachmentDescriptorSchema).safeParse(safeJson(content.slice(open + 25, close)));
469
- if (!parsed.success) return {
470
- text: content,
471
- attachments: null
472
- };
473
- return {
474
- text: content.slice(0, open).trimEnd(),
475
- attachments: parsed.data
476
- };
477
- }
478
- function safeJson(value) {
479
- try {
480
- return JSON.parse(value);
481
- } catch {
482
- return;
483
- }
484
- }
485
- const AttachmentStartPayload = z.object({
486
- operation_id: z.uuid(),
487
- attachment_id: z.uuid(),
488
- name: z.string().min(1).max(255),
489
- media_type: AttachmentMediaTypeSchema,
490
- size_bytes: z.number().int().min(0).max(ATTACHMENT_MAX_FILE_BYTES)
491
- });
492
- const AttachmentChunkPayload = z.object({
493
- operation_id: z.uuid(),
494
- seq: z.number().int().nonnegative(),
495
- content_b64: z.string().max(Math.ceil(ATTACHMENT_CHUNK_BYTES / 3) * 4 + 4)
496
- });
497
- const AttachmentFinishPayload = z.object({ operation_id: z.uuid() });
498
- const AttachmentAbortPayload = z.object({
499
- operation_id: z.uuid(),
500
- reason: z.string().max(512)
501
- });
502
- const AttachmentResolvePayload = z.object({
503
- operation_id: z.uuid(),
504
- attachment_ids: z.array(z.uuid()).min(1).max(10)
505
- });
506
- const AttachmentAckPayload = z.object({
507
- operation_id: z.uuid(),
508
- seq: z.number().int().nonnegative(),
509
- received_bytes: z.number().int().nonnegative()
510
- });
511
- const AttachmentResultPayload = z.object({
512
- operation_id: z.uuid(),
513
- status: z.enum([
514
- "created",
515
- "existing",
516
- "conflict",
517
- "failed"
518
- ]),
519
- descriptor: AttachmentDescriptorSchema.optional(),
520
- failure_code: z.enum([
521
- "invalid",
522
- "too_large",
523
- "sequence",
524
- "io",
525
- "interrupted"
526
- ]).optional(),
527
- detail: z.string().max(512).optional()
528
- });
529
- const AttachmentResolvedPayload = z.object({
530
- operation_id: z.uuid(),
531
- descriptors: z.array(AttachmentDescriptorSchema).optional(),
532
- missing_id: z.uuid().optional()
533
- });
534
- //#endregion
535
- //#region ../contracts/src/errors.ts
536
- const ERROR_CODES = {
537
- "auth.invalid_key": 401,
538
- "auth.missing_scope": 403,
539
- "auth.disabled_principal": 403,
540
- "validation.invalid": 400,
541
- "idempotency.conflict": 409,
542
- "capacity.queue_full": 429,
543
- "capacity.waiters_full": 429,
544
- "template.not_found": 404,
545
- "template.version_not_found": 404,
546
- "template.not_authorized": 403,
547
- "workspace.not_found": 404,
548
- "workspace.external_id_conflict": 409,
549
- "workspace.not_ready": 409,
550
- "workspace.terminal": 410,
551
- "workspace.disconnected": 503,
552
- "terminal.not_declared": 422,
553
- "terminal.session_limit": 409,
554
- "terminal.session_not_found": 404,
555
- "terminal.session_closed": 409,
556
- "terminal.protocol_unsupported": 426,
557
- "workspace.persistence_not_enabled": 409,
558
- "workspace.preserving": 409,
559
- "checkpoint.not_found": 404,
560
- "checkpoint.not_ready": 409,
561
- "checkpoint.none_ready": 409,
562
- "checkpoint.corrupt": 409,
563
- "checkpoint.quota_exceeded": 413,
564
- "checkpoint.in_use": 409,
565
- "restore.template_not_authorized": 403,
566
- "restore.image_unavailable": 409,
567
- "restore.incompatible": 409,
568
- "resume.unsupported": 409,
569
- "conversation.expired": 410,
570
- "conversation.deleted": 410,
571
- "operation.conflict": 409,
572
- "source.not_allowed": 422,
573
- "source.invalid_revision": 422,
574
- "secret.unavailable": 503,
575
- "storage.capacity_exhausted": 507,
576
- "relay.body_too_large": 413,
577
- "relay.route_not_allowed": 422,
578
- "relay.streaming_unsupported": 409,
579
- "relay.deadline_exceeded": 504,
580
- "relay.upstream_error": 502,
581
- "attachment.invalid": 400,
582
- "attachment.too_large": 413,
583
- "attachment.not_found": 404,
584
- "attachment.conflict": 409,
585
- "attachment.unsupported": 409,
586
- "attachment.interrupted": 503,
587
- "internal.error": 500
588
- };
589
- const errorCodes = Object.keys(ERROR_CODES);
590
- const ErrorEnvelopeSchema = z.object({ error: z.object({
591
- code: z.enum(errorCodes),
592
- message: z.string().min(1),
593
- request_id: z.string().min(1),
594
- details: z.record(z.string(), z.unknown()).optional()
595
- }) });
596
650
  WORKSPACE_STATES.map((s) => `workspace.${s}`);
597
651
  z.object({
598
652
  id: z.uuid(),
@@ -624,7 +678,7 @@ z.object({
624
678
  })
625
679
  });
626
680
  //#endregion
627
- //#region ../contracts/src/logs.ts
681
+ //#region ../contracts/src/protocol/logs.ts
628
682
  const LogChunkSchema = z.object({
629
683
  seq: z.number().int().positive(),
630
684
  stream: z.string(),
@@ -634,47 +688,14 @@ const LogChunkSchema = z.object({
634
688
  CursorQuerySchema(1e3, 200);
635
689
  const LogPageSchema = CursorPageSchema(LogChunkSchema);
636
690
  //#endregion
637
- //#region ../contracts/src/protocol-stream.ts
638
- const ProxyStreamStartPayload = z.object({
639
- request_id: z.uuid(),
640
- status: z.number().int().min(100).max(599),
641
- headers: z.record(z.string(), z.string())
642
- });
643
- const ProxyStreamChunkPayload = z.object({
644
- request_id: z.uuid(),
645
- seq: z.number().int().nonnegative(),
646
- content_b64: z.string().max(87400)
647
- });
648
- const ProxyStreamEndPayload = z.object({
649
- request_id: z.uuid(),
650
- error_code: z.enum([
651
- "unreachable",
652
- "deadline",
653
- "too_large"
654
- ]).optional()
655
- });
656
- const ProxyStreamAckPayload = z.object({
657
- request_id: z.uuid(),
658
- seq: z.number().int().nonnegative()
659
- });
660
- const ProxyStreamCancelPayload = z.object({
661
- request_id: z.uuid(),
662
- reason: z.enum([
663
- "downstream_closed",
664
- "deadline",
665
- "too_large",
666
- "workspace_disconnected"
667
- ])
668
- });
669
- //#endregion
670
- //#region ../contracts/src/template-constants.ts
691
+ //#region ../contracts/src/templates/template-constants.ts
671
692
  function isAbsolutePath(value) {
672
693
  return value.startsWith("/") && ![...value].some((character) => character.codePointAt(0) === 0);
673
694
  }
674
695
  const SECRET_ENV_PATTERN = /(SECRET|TOKEN|PASSWORD|PASSWD|API_?KEY|PRIVATE_?KEY|CREDENTIAL)/i;
675
696
  const SECRET_REFERENCE_PREFIX = "secretRef:";
676
697
  //#endregion
677
- //#region ../contracts/src/template-schema.ts
698
+ //#region ../contracts/src/templates/template-schema.ts
678
699
  const NAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
679
700
  const SEMVER_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/;
680
701
  const IMAGE_DIGEST_RE = /^[^\s@]+@sha256:[0-9a-f]{64}$/;
@@ -703,7 +724,8 @@ const HarnessSchema = z.object({
703
724
  const AgentSchema = HarnessSchema.extend({
704
725
  type: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).default("custom"),
705
726
  transport: z.enum(["pty", "acp"]).default("pty"),
706
- termWidth: z.number().int().min(10).max(65535).optional()
727
+ termWidth: z.number().int().min(10).max(65535).optional(),
728
+ stateFile: z.string().refine(isAbsolutePath, "expected an absolute path").optional()
707
729
  });
708
730
  const TerminalSchema = z.object({
709
731
  command: CommandSchema,
@@ -751,7 +773,8 @@ const TimeoutsSchema = z.object({
751
773
  });
752
774
  const ResourcesSchema = z.object({
753
775
  cpu: z.string().regex(/^\d+(\.\d+)?m?$/),
754
- memory: z.string().regex(/^\d+(Mi|Gi)$/)
776
+ memory: z.string().regex(/^\d+(Mi|Gi)$/),
777
+ ephemeralStorage: z.string().regex(/^\d+(Mi|Gi)$/).optional()
755
778
  });
756
779
  const RepositorySchema = z.object({
757
780
  url: z.url().refine((value) => {
@@ -817,11 +840,14 @@ const TemplateSpecSchema = z.object({
817
840
  message: "either agent or harness is required"
818
841
  });
819
842
  if (!spec.agent) return;
820
- if (spec.agent.transport === "acp" && spec.agent.termWidth !== void 0) ctx.addIssue({
821
- code: "custom",
822
- path: ["agent", "termWidth"],
823
- message: "termWidth is only valid for PTY transport"
824
- });
843
+ if (spec.agent.transport === "acp") for (const field of ["termWidth", "stateFile"]) {
844
+ if (spec.agent[field] === void 0) continue;
845
+ ctx.addIssue({
846
+ code: "custom",
847
+ path: ["agent", field],
848
+ message: `${field} is only valid for PTY transport`
849
+ });
850
+ }
825
851
  for (const field of [
826
852
  "harness",
827
853
  "services",
@@ -859,7 +885,7 @@ const TemplateManifestBaseSchema = z.object({
859
885
  spec: TemplateSpecSchema
860
886
  });
861
887
  //#endregion
862
- //#region ../contracts/src/template-runtime.ts
888
+ //#region ../contracts/src/templates/template-runtime.ts
863
889
  const LEGACY_AGENTAPI_SERVICE = ServiceSchema.parse({
864
890
  baseUrl: "http://127.0.0.1:3284",
865
891
  routes: [
@@ -901,6 +927,7 @@ function agentApiHarness(spec) {
901
927
  spec.agent.type,
902
928
  ...spec.agent.transport === "acp" ? ["--experimental-acp"] : [],
903
929
  ...spec.agent.termWidth === void 0 ? [] : ["--term-width", String(spec.agent.termWidth)],
930
+ ...spec.agent.stateFile === void 0 ? [] : ["--state-file", spec.agent.stateFile],
904
931
  "--port",
905
932
  "3284",
906
933
  "--",
@@ -914,7 +941,7 @@ function templateServices(spec) {
914
941
  return isAgentApiNative(spec) ? { agent: AGENTAPI_SERVICE } : spec.services;
915
942
  }
916
943
  //#endregion
917
- //#region ../contracts/src/template-validation.ts
944
+ //#region ../contracts/src/templates/template-validation.ts
918
945
  function envSources(spec) {
919
946
  const sources = [[["spec", "env"], spec.env], [isAgentApiNative(spec) ? [
920
947
  "spec",
@@ -1108,14 +1135,52 @@ function validatePersistence(spec, ctx) {
1108
1135
  const mounts = spec.persistence.mounts;
1109
1136
  for (const [index, mount] of mounts.entries()) validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx);
1110
1137
  validateSourceMount(spec, mounts, ctx);
1138
+ validateAgentStateFile(spec, mounts, ctx);
1111
1139
  if (spec.persistence.conversationRestore === "supported" && (!spec.persistence.sessionCompatibility || mounts.length < 2)) ctx.addIssue({
1112
1140
  code: "custom",
1113
1141
  path: [
1114
1142
  "spec",
1115
- "persistence",
1116
- "conversationRestore"
1143
+ "persistence",
1144
+ "conversationRestore"
1145
+ ],
1146
+ message: "supported conversation restore requires sessionCompatibility and a separate harness-state mount"
1147
+ });
1148
+ }
1149
+ function validateAgentStateFile(spec, mounts, ctx) {
1150
+ if (!isAgentApiNative(spec)) return;
1151
+ const stateFile = spec.agent.stateFile;
1152
+ const normalized = stateFile !== void 0 && isNormalizedFilesystemPath(stateFile);
1153
+ if (stateFile !== void 0 && !normalized) ctx.addIssue({
1154
+ code: "custom",
1155
+ path: [
1156
+ "spec",
1157
+ "agent",
1158
+ "stateFile"
1159
+ ],
1160
+ message: "stateFile must be a normalized absolute filesystem path"
1161
+ });
1162
+ if (spec.agent.transport === "acp" && spec.persistence.conversationRestore === "supported") {
1163
+ ctx.addIssue({
1164
+ code: "custom",
1165
+ path: [
1166
+ "spec",
1167
+ "persistence",
1168
+ "conversationRestore"
1169
+ ],
1170
+ message: "supported conversation restore requires PTY transport"
1171
+ });
1172
+ return;
1173
+ }
1174
+ if (spec.persistence.conversationRestore !== "supported") return;
1175
+ if (normalized && mounts.some((mount) => stateFile.startsWith(`${mount.target}/`))) return;
1176
+ ctx.addIssue({
1177
+ code: "custom",
1178
+ path: [
1179
+ "spec",
1180
+ "agent",
1181
+ "stateFile"
1117
1182
  ],
1118
- message: "supported conversation restore requires sessionCompatibility and a separate harness-state mount"
1183
+ message: "supported conversation restore requires agent.stateFile below a persistence mount"
1119
1184
  });
1120
1185
  }
1121
1186
  function validatePersistenceMount(spec, mounts, mount, index, seenNames, ctx) {
@@ -1215,12 +1280,14 @@ TemplateManifestBaseSchema.superRefine((manifest, ctx) => {
1215
1280
  validateTemplateSpec(manifest.spec, ctx);
1216
1281
  });
1217
1282
  //#endregion
1218
- //#region ../contracts/src/terminal.ts
1283
+ //#region ../contracts/src/terminals/terminal.ts
1219
1284
  const TERMINAL_CHUNK_BYTES = 32768;
1220
1285
  const TERMINAL_REPLAY_BUFFER_BYTES = 65536;
1221
1286
  function base64Schema(maxBytes) {
1222
1287
  return z.string().regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/).refine((value) => {
1223
- const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
1288
+ let padding = 0;
1289
+ if (value.endsWith("==")) padding = 2;
1290
+ else if (value.endsWith("=")) padding = 1;
1224
1291
  return value.length / 4 * 3 - padding <= maxBytes;
1225
1292
  }, `decoded payload must not exceed ${maxBytes} bytes`);
1226
1293
  }
@@ -1326,6 +1393,39 @@ const TerminalSessionSchema = z.object({
1326
1393
  });
1327
1394
  CursorQuerySchema(200, 50);
1328
1395
  const TerminalSessionPageSchema = CursorPageSchema(TerminalSessionSchema);
1396
+ //#endregion
1397
+ //#region ../contracts/src/protocol/protocol-stream.ts
1398
+ const ProxyStreamStartPayload = z.object({
1399
+ request_id: z.uuid(),
1400
+ status: z.number().int().min(100).max(599),
1401
+ headers: z.record(z.string(), z.string())
1402
+ });
1403
+ const ProxyStreamChunkPayload = z.object({
1404
+ request_id: z.uuid(),
1405
+ seq: z.number().int().nonnegative(),
1406
+ content_b64: z.string().max(87400)
1407
+ });
1408
+ const ProxyStreamEndPayload = z.object({
1409
+ request_id: z.uuid(),
1410
+ error_code: z.enum([
1411
+ "unreachable",
1412
+ "deadline",
1413
+ "too_large"
1414
+ ]).optional()
1415
+ });
1416
+ const ProxyStreamAckPayload = z.object({
1417
+ request_id: z.uuid(),
1418
+ seq: z.number().int().nonnegative()
1419
+ });
1420
+ const ProxyStreamCancelPayload = z.object({
1421
+ request_id: z.uuid(),
1422
+ reason: z.enum([
1423
+ "downstream_closed",
1424
+ "deadline",
1425
+ "too_large",
1426
+ "workspace_disconnected"
1427
+ ])
1428
+ });
1329
1429
  const ProviderInputSchema = z.object({
1330
1430
  workspace_id: z.uuid(),
1331
1431
  server_url: z.string(),
@@ -1735,7 +1835,7 @@ z.discriminatedUnion("type", [
1735
1835
  })
1736
1836
  ]);
1737
1837
  //#endregion
1738
- //#region src/admin.ts
1838
+ //#region src/resources/admin/admin.ts
1739
1839
  var AdministrationApi = class {
1740
1840
  transport;
1741
1841
  constructor(transport) {
@@ -1755,7 +1855,7 @@ var AdministrationApi = class {
1755
1855
  }
1756
1856
  };
1757
1857
  //#endregion
1758
- //#region src/errors.ts
1858
+ //#region src/transport/errors.ts
1759
1859
  z.enum(["client.non_json_response", "client.invalid_response"]);
1760
1860
  var PocketCoderError = class extends Error {
1761
1861
  code;
@@ -1782,6 +1882,16 @@ var WorkspaceTerminalError = class extends Error {
1782
1882
  this.workspace = workspace;
1783
1883
  }
1784
1884
  };
1885
+ var AgentNotReadyError = class extends Error {
1886
+ workspaceId;
1887
+ agentState;
1888
+ constructor(workspaceId, agentState, timeoutMs) {
1889
+ super(`agent in workspace ${workspaceId} was still ${agentState} after ${timeoutMs}ms and cannot accept a message`);
1890
+ this.name = "AgentNotReadyError";
1891
+ this.workspaceId = workspaceId;
1892
+ this.agentState = agentState;
1893
+ }
1894
+ };
1785
1895
  function isPocketCoderErrorCode(value) {
1786
1896
  return typeof value === "string" && value in ERROR_CODES;
1787
1897
  }
@@ -1802,7 +1912,8 @@ function responseError(response, body) {
1802
1912
  });
1803
1913
  }
1804
1914
  //#endregion
1805
- //#region src/attachments.ts
1915
+ //#region src/resources/attachments/attachments.ts
1916
+ const DEFAULT_READY_TIMEOUT_MS = 12e4;
1806
1917
  function contentDisposition(name) {
1807
1918
  const clean = name.replace(/[\r\n]/g, "");
1808
1919
  if (/^[ -~]*$/.test(clean)) return `attachment; filename="${clean.replace(/(["\\])/g, "\\$1")}"`;
@@ -1841,10 +1952,13 @@ var AttachmentsApi = class {
1841
1952
  };
1842
1953
  var AgentApi = class {
1843
1954
  transport;
1844
- constructor(transport) {
1955
+ workspaces;
1956
+ constructor(transport, workspaces) {
1845
1957
  this.transport = transport;
1958
+ this.workspaces = workspaces;
1846
1959
  }
1847
1960
  async sendMessage(workspaceId, input) {
1961
+ await this.workspaces.waitForAgentInput(workspaceId, input.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS, input.signal ? { signal: input.signal } : {});
1848
1962
  const response = await this.transport.raw(`/v1/workspaces/${encodeURIComponent(workspaceId)}/agent/message`, {
1849
1963
  method: "POST",
1850
1964
  ...input.signal ? { signal: input.signal } : {},
@@ -1871,7 +1985,7 @@ var AgentApi = class {
1871
1985
  }
1872
1986
  };
1873
1987
  //#endregion
1874
- //#region src/common.ts
1988
+ //#region src/transport/common.ts
1875
1989
  function queryString(values) {
1876
1990
  const params = new URLSearchParams();
1877
1991
  for (const [key, value] of Object.entries(values)) if (value !== void 0) params.set(key, String(value));
@@ -1884,7 +1998,7 @@ function page(body) {
1884
1998
  };
1885
1999
  }
1886
2000
  //#endregion
1887
- //#region src/checkpoints.ts
2001
+ //#region src/resources/checkpoints/checkpoints.ts
1888
2002
  var CheckpointsApi = class {
1889
2003
  transport;
1890
2004
  constructor(transport) {
@@ -1932,7 +2046,7 @@ var OperationsApi = class {
1932
2046
  }
1933
2047
  };
1934
2048
  //#endregion
1935
- //#region src/conversations.ts
2049
+ //#region src/resources/conversations/conversations.ts
1936
2050
  var ConversationsApi = class {
1937
2051
  transport;
1938
2052
  constructor(transport) {
@@ -1946,7 +2060,7 @@ var ConversationsApi = class {
1946
2060
  }
1947
2061
  };
1948
2062
  //#endregion
1949
- //#region src/diagnostics.ts
2063
+ //#region src/resources/diagnostics/diagnostics.ts
1950
2064
  var WorkspaceCursorApi = class {
1951
2065
  transport;
1952
2066
  resource;
@@ -1979,7 +2093,69 @@ var OutputsApi = class extends WorkspaceCursorApi {
1979
2093
  }
1980
2094
  };
1981
2095
  //#endregion
1982
- //#region src/templates.ts
2096
+ //#region src/resources/keys/keys.ts
2097
+ var KeysApi = class {
2098
+ transport;
2099
+ constructor(transport) {
2100
+ this.transport = transport;
2101
+ }
2102
+ list(principalId, query = {}, options = {}) {
2103
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys?${queryString({
2104
+ limit: query.limit,
2105
+ cursor: query.cursor,
2106
+ request_id: query.requestId
2107
+ })}`, KeyListResponseSchema, options);
2108
+ }
2109
+ async *all(principalId, query = {}, options = {}) {
2110
+ let cursor;
2111
+ do {
2112
+ const page = await this.list(principalId, {
2113
+ ...query,
2114
+ cursor
2115
+ }, options);
2116
+ yield* page.items;
2117
+ cursor = page.next_cursor ?? void 0;
2118
+ } while (cursor);
2119
+ }
2120
+ issue(principalId, input, options = {}) {
2121
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys`, KeyIssueResponseSchema, {
2122
+ method: "POST",
2123
+ body: JSON.stringify(input),
2124
+ signal: options.signal
2125
+ });
2126
+ }
2127
+ revoke(principalId, keyId, options = {}) {
2128
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys/${encodeURIComponent(keyId)}`, z.object({ revoked: z.literal(true) }), {
2129
+ method: "DELETE",
2130
+ signal: options.signal
2131
+ });
2132
+ }
2133
+ revokeAll(principalId, options = {}) {
2134
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/keys`, z.object({ revoked: z.literal(true) }), {
2135
+ method: "DELETE",
2136
+ signal: options.signal
2137
+ });
2138
+ }
2139
+ };
2140
+ var RecoveryApi = class {
2141
+ transport;
2142
+ constructor(transport) {
2143
+ this.transport = transport;
2144
+ }
2145
+ purge(principalId, workspaceId, executionKey, options = {}) {
2146
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/workspaces/${encodeURIComponent(workspaceId)}/purge`, OperationResourceSchema, {
2147
+ method: "POST",
2148
+ headers: { "Idempotency-Key": executionKey },
2149
+ body: "{}",
2150
+ signal: options.signal
2151
+ });
2152
+ }
2153
+ operation(principalId, operationId, options = {}) {
2154
+ return this.transport.request(`/v1/principals/${encodeURIComponent(principalId)}/operations/${encodeURIComponent(operationId)}`, OperationResourceSchema, options);
2155
+ }
2156
+ };
2157
+ //#endregion
2158
+ //#region src/resources/templates/templates.ts
1983
2159
  var TemplatesApi = class {
1984
2160
  transport;
1985
2161
  constructor(transport) {
@@ -1996,7 +2172,7 @@ var TemplatesApi = class {
1996
2172
  }
1997
2173
  };
1998
2174
  //#endregion
1999
- //#region src/terminals.ts
2175
+ //#region src/resources/terminals/terminals.ts
2000
2176
  var TerminalConnection = class {
2001
2177
  socket;
2002
2178
  messageListeners = /* @__PURE__ */ new Set();
@@ -2098,7 +2274,109 @@ var TerminalsApi = class {
2098
2274
  }
2099
2275
  };
2100
2276
  //#endregion
2101
- //#region src/transport.ts
2277
+ //#region src/resources/workspaces/workspaces.ts
2278
+ const TERMINAL_WORKSPACE_STATES = new Set(TERMINAL_STATES);
2279
+ var WorkspacesApi = class {
2280
+ transport;
2281
+ constructor(transport) {
2282
+ this.transport = transport;
2283
+ }
2284
+ async list(query = {}, options = {}) {
2285
+ return page(await this.transport.request(`/v1/workspaces?${queryString({
2286
+ state: query.state,
2287
+ template: query.template,
2288
+ external_id: query.externalId,
2289
+ limit: query.limit ?? 50,
2290
+ cursor: query.cursor
2291
+ })}`, WorkspacePageSchema, options));
2292
+ }
2293
+ async *all(query = {}, options = {}) {
2294
+ let cursor;
2295
+ do {
2296
+ const current = await this.list({
2297
+ ...query,
2298
+ cursor
2299
+ }, options);
2300
+ for (const workspace of current.items) yield workspace;
2301
+ cursor = current.nextCursor ?? void 0;
2302
+ } while (cursor);
2303
+ }
2304
+ get(id, options = {}) {
2305
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}`, WorkspaceResourceSchema, options);
2306
+ }
2307
+ create(input, options = {}) {
2308
+ const body = {
2309
+ external_id: input.externalId,
2310
+ template: {
2311
+ name: input.templateName,
2312
+ ...input.templateVersion ? { version: input.templateVersion } : {}
2313
+ },
2314
+ ...input.launchInput ? { launch_input: input.launchInput } : {},
2315
+ ...input.metadata ? { metadata: input.metadata } : {},
2316
+ ...input.source ? { source: input.source } : {}
2317
+ };
2318
+ return this.transport.request("/v1/workspaces", WorkspaceResourceSchema, {
2319
+ method: "POST",
2320
+ signal: options.signal,
2321
+ headers: { "Idempotency-Key": input.idempotencyKey ?? input.externalId },
2322
+ body: JSON.stringify(body)
2323
+ });
2324
+ }
2325
+ cancel(id, options = {}) {
2326
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/cancel`, WorkspaceResourceSchema, {
2327
+ method: "POST",
2328
+ signal: options.signal
2329
+ });
2330
+ }
2331
+ change(id, after, wait, options = {}) {
2332
+ return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/changes?after=${after}&wait=${wait}`, WorkspaceChangeSchema, options);
2333
+ }
2334
+ async waitForReady(initial, timeoutMs, options = {}) {
2335
+ const deadline = Date.now() + timeoutMs;
2336
+ let workspace = initial;
2337
+ while (workspace.state !== "ready") {
2338
+ if (TERMINAL_STATES.includes(workspace.state)) throw new WorkspaceTerminalError(workspace);
2339
+ const remainingMs = deadline - Date.now();
2340
+ if (remainingMs <= 0) throw new Error(`workspace ${workspace.id} did not become ready within ${timeoutMs}ms`);
2341
+ workspace = (await this.change(workspace.id, workspace.change_cursor, Math.max(1, Math.min(30, Math.ceil(remainingMs / 1e3))), options)).workspace;
2342
+ options.onTick?.(workspace);
2343
+ }
2344
+ return workspace;
2345
+ }
2346
+ async waitForAgentInput(id, timeoutMs, options = {}) {
2347
+ const deadline = Date.now() + timeoutMs;
2348
+ let workspace = await this.get(id, options);
2349
+ while (workspace.agent_state !== "stable") {
2350
+ if (TERMINAL_STATES.includes(workspace.state)) throw new WorkspaceTerminalError(workspace);
2351
+ const remainingMs = deadline - Date.now();
2352
+ if (remainingMs <= 0) throw new AgentNotReadyError(workspace.id, workspace.agent_state, timeoutMs);
2353
+ workspace = (await this.change(workspace.id, workspace.change_cursor, Math.max(1, Math.min(30, Math.ceil(remainingMs / 1e3))), options)).workspace;
2354
+ }
2355
+ return workspace;
2356
+ }
2357
+ preserve(id, input, key, options = {}) {
2358
+ return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/preserve`, input, key, PreserveResponseSchema, options);
2359
+ }
2360
+ purge(id, key, options = {}) {
2361
+ return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/purge`, {}, key, OperationResourceSchema, options);
2362
+ }
2363
+ recreate(id, input, key, options = {}) {
2364
+ return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/recreate`, input, key, RestoreResponseSchema, options);
2365
+ }
2366
+ resume(id, input, key, options = {}) {
2367
+ return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/resume`, input, key, ResumeResponseSchema, options);
2368
+ }
2369
+ jsonOperation(path, input, key, schema, options) {
2370
+ return this.transport.request(path, schema, {
2371
+ method: "POST",
2372
+ signal: options.signal,
2373
+ headers: { "Idempotency-Key": key },
2374
+ body: JSON.stringify(input)
2375
+ });
2376
+ }
2377
+ };
2378
+ //#endregion
2379
+ //#region src/transport/transport.ts
2102
2380
  function baseUrlOf(value) {
2103
2381
  let url;
2104
2382
  try {
@@ -2215,94 +2493,6 @@ async function delay(milliseconds, signal) {
2215
2493
  });
2216
2494
  }
2217
2495
  //#endregion
2218
- //#region src/workspaces.ts
2219
- const TERMINAL_WORKSPACE_STATES = new Set(TERMINAL_STATES);
2220
- var WorkspacesApi = class {
2221
- transport;
2222
- constructor(transport) {
2223
- this.transport = transport;
2224
- }
2225
- async list(query = {}, options = {}) {
2226
- return page(await this.transport.request(`/v1/workspaces?${queryString({
2227
- state: query.state,
2228
- template: query.template,
2229
- external_id: query.externalId,
2230
- limit: query.limit ?? 50,
2231
- cursor: query.cursor
2232
- })}`, WorkspacePageSchema, options));
2233
- }
2234
- async *all(query = {}, options = {}) {
2235
- let cursor;
2236
- do {
2237
- const current = await this.list({
2238
- ...query,
2239
- cursor
2240
- }, options);
2241
- for (const workspace of current.items) yield workspace;
2242
- cursor = current.nextCursor ?? void 0;
2243
- } while (cursor);
2244
- }
2245
- get(id, options = {}) {
2246
- return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}`, WorkspaceResourceSchema, options);
2247
- }
2248
- create(input, options = {}) {
2249
- const body = {
2250
- external_id: input.externalId,
2251
- template: {
2252
- name: input.templateName,
2253
- ...input.templateVersion ? { version: input.templateVersion } : {}
2254
- },
2255
- ...input.launchInput ? { launch_input: input.launchInput } : {},
2256
- ...input.metadata ? { metadata: input.metadata } : {},
2257
- ...input.source ? { source: input.source } : {}
2258
- };
2259
- return this.transport.request("/v1/workspaces", WorkspaceResourceSchema, {
2260
- method: "POST",
2261
- signal: options.signal,
2262
- headers: { "Idempotency-Key": input.idempotencyKey ?? input.externalId },
2263
- body: JSON.stringify(body)
2264
- });
2265
- }
2266
- cancel(id, options = {}) {
2267
- return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/cancel`, WorkspaceResourceSchema, {
2268
- method: "POST",
2269
- signal: options.signal
2270
- });
2271
- }
2272
- change(id, after, wait, options = {}) {
2273
- return this.transport.request(`/v1/workspaces/${encodeURIComponent(id)}/changes?after=${after}&wait=${wait}`, WorkspaceChangeSchema, options);
2274
- }
2275
- async waitForReady(initial, timeoutMs, options = {}) {
2276
- const deadline = Date.now() + timeoutMs;
2277
- let workspace = initial;
2278
- while (workspace.state !== "ready") {
2279
- if (TERMINAL_STATES.includes(workspace.state)) throw new WorkspaceTerminalError(workspace);
2280
- const remainingMs = deadline - Date.now();
2281
- if (remainingMs <= 0) throw new Error(`workspace ${workspace.id} did not become ready within ${timeoutMs}ms`);
2282
- workspace = (await this.change(workspace.id, workspace.change_cursor, Math.max(1, Math.min(30, Math.ceil(remainingMs / 1e3))), options)).workspace;
2283
- options.onTick?.(workspace);
2284
- }
2285
- return workspace;
2286
- }
2287
- preserve(id, input, key, options = {}) {
2288
- return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/preserve`, input, key, PreserveResponseSchema, options);
2289
- }
2290
- recreate(id, input, key, options = {}) {
2291
- return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/recreate`, input, key, RestoreResponseSchema, options);
2292
- }
2293
- resume(id, input, key, options = {}) {
2294
- return this.jsonOperation(`/v1/workspaces/${encodeURIComponent(id)}/resume`, input, key, ResumeResponseSchema, options);
2295
- }
2296
- jsonOperation(path, input, key, schema, options) {
2297
- return this.transport.request(path, schema, {
2298
- method: "POST",
2299
- signal: options.signal,
2300
- headers: { "Idempotency-Key": key },
2301
- body: JSON.stringify(input)
2302
- });
2303
- }
2304
- };
2305
- //#endregion
2306
2496
  //#region src/client.ts
2307
2497
  var PocketCoderClient = class {
2308
2498
  transport;
@@ -2318,12 +2508,14 @@ var PocketCoderClient = class {
2318
2508
  outputs;
2319
2509
  administration;
2320
2510
  terminals;
2511
+ keys;
2512
+ recovery;
2321
2513
  constructor(config, fetchImpl = fetch) {
2322
2514
  this.transport = new PocketCoderTransport(config, fetchImpl);
2323
2515
  this.templates = new TemplatesApi(this.transport);
2324
2516
  this.workspaces = new WorkspacesApi(this.transport);
2325
2517
  this.attachments = new AttachmentsApi(this.transport);
2326
- this.agent = new AgentApi(this.transport);
2518
+ this.agent = new AgentApi(this.transport, this.workspaces);
2327
2519
  this.conversations = new ConversationsApi(this.transport);
2328
2520
  this.checkpoints = new CheckpointsApi(this.transport);
2329
2521
  this.operations = new OperationsApi(this.transport);
@@ -2332,13 +2524,15 @@ var PocketCoderClient = class {
2332
2524
  this.outputs = new OutputsApi(this.transport);
2333
2525
  this.administration = new AdministrationApi(this.transport);
2334
2526
  this.terminals = new TerminalsApi(this.transport);
2527
+ this.keys = new KeysApi(this.transport);
2528
+ this.recovery = new RecoveryApi(this.transport);
2335
2529
  }
2336
2530
  raw(path, init = {}) {
2337
2531
  return this.transport.raw(path, init);
2338
2532
  }
2339
2533
  };
2340
2534
  //#endregion
2341
- //#region src/workspace-turn-resolver.ts
2535
+ //#region src/resources/workspaces/workspace-turn-resolver.ts
2342
2536
  const ERROR_MESSAGES = {
2343
2537
  not_resumable: "workspace cannot be resumed",
2344
2538
  resume_handler_missing: "workspace resume handler is not configured",
@@ -2482,4 +2676,4 @@ var WorkspaceTurnResolver = class {
2482
2676
  }
2483
2677
  };
2484
2678
  //#endregion
2485
- export { AdministrationApi, AgentApi, AttachmentsApi, CheckpointsApi, ConversationGoneError, ConversationsApi, LogsApi, NetworkEventsApi, OperationsApi, OutputsApi, PocketCoderClient, PocketCoderError, TERMINAL_WORKSPACE_STATES, TemplatesApi, TerminalConnection, TerminalsApi, WorkspaceTerminalError, WorkspaceTurnResolutionError, WorkspaceTurnResolver, WorkspacesApi, isPocketCoderErrorCode, splitAttachmentManifest };
2679
+ 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 };