@usenaive-sdk/blueprints 0.3.0 → 0.5.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
@@ -8,6 +8,112 @@ function isId(prefix, value) {
8
8
  return SUFFIX_PATTERN.test(value.slice(head.length).toLowerCase());
9
9
  }
10
10
 
11
+ // ../core/src/errors.ts
12
+ var CODES = {
13
+ window_unavailable: ["invalid_request", 400],
14
+ validation_failed: ["invalid_request", 400],
15
+ unauthorized: ["authentication", 401],
16
+ forbidden: ["permission", 403],
17
+ not_found: ["not_found", 404],
18
+ budget_exceeded: ["quota", 402],
19
+ insufficient_credits: ["quota", 402],
20
+ /**
21
+ * The organization holds no active plan, so it may not be used. `402`, alongside the other two
22
+ * money codes, because the remedy is a payment — not `403`, which reads as "your credential is
23
+ * wrong" and would send a customer to rotate a perfectly good key. Distinct from
24
+ * `insufficient_credits`: that one means "fund the balance", this one means "start the plan".
25
+ */
26
+ subscription_required: ["quota", 402],
27
+ version_conflict: ["conflict", 409],
28
+ /**
29
+ * The name is taken by a resource another project owns, and the request did not ask to take it
30
+ * (ADR-0375). Nothing existing fits: `validation_failed` says the request is malformed and it is
31
+ * not, `forbidden` reads as "your credential is wrong" and sends a customer to rotate a good key,
32
+ * and `version_conflict` means optimistic concurrency.
33
+ */
34
+ name_conflict: ["conflict", 409],
35
+ /**
36
+ * A shared declaration moved underneath the applier: the install's `revision` is not the
37
+ * `expected_revision` the apply carried (`§29.6`). Not `version_conflict`, which is one
38
+ * immutable-versioned object's optimistic concurrency and whose remedy is to re-read that
39
+ * object — this one carries a set of drifted PATHS and its remedy is a merge, and a client that
40
+ * cannot tell them apart shows a config diff for a stale agent version.
41
+ */
42
+ revision_conflict: ["conflict", 409],
43
+ idempotency_conflict: ["conflict", 409],
44
+ /**
45
+ * A lifecycle action asked of an object whose status does not admit it (§43–§45): forming a
46
+ * company on an unverified verification, funding a `frozen` wallet, cancelling a card that is not
47
+ * `active`. Not `version_conflict` — nothing raced; the remedy is to read the status.
48
+ */
49
+ state_conflict: ["conflict", 409],
50
+ session_running: ["conflict", 409],
51
+ session_terminal: ["conflict", 409],
52
+ computer_unavailable: ["conflict", 409],
53
+ /**
54
+ * Two P2 additions (ADR-0118). Both mean "not an error — retry later", and no existing code
55
+ * carries that: `feature_not_configured` is permanent, `computer_unavailable` is about a
56
+ * sandbox, and `validation_failed` would tell a durable runtime to give up on something that
57
+ * will succeed once verification or carrier approval clears.
58
+ */
59
+ job_not_ready: ["conflict", 409],
60
+ compliance_pending: ["conflict", 409],
61
+ // core-gaps: contracts (`canonical-spec §32`, §37; ADR-0448)
62
+ /** `--app` omitted and the org has more than one fullstack app to resolve it to (§37.2). */
63
+ ambiguous_app: ["conflict", 409],
64
+ /** A saved browser login is held by a human-login flow for the next 15 minutes (§32.6). */
65
+ context_locked: ["conflict", 409],
66
+ rate_limited: ["rate_limit", 429],
67
+ feature_not_configured: ["not_implemented", 501],
68
+ internal_error: ["api_error", 500],
69
+ /**
70
+ * A configured upstream answered with a failure of its own — a 5xx, or a browser automation that
71
+ * did nothing (§32.5). Not `internal_error`: the remedy is a retry, and nothing of ours broke.
72
+ */
73
+ provider_error: ["api_error", 502]
74
+ };
75
+ var VettaError = class extends Error {
76
+ type;
77
+ code;
78
+ httpStatus;
79
+ param;
80
+ constructor(code, message3, param) {
81
+ super(message3);
82
+ this.name = "VettaError";
83
+ const [type, httpStatus] = CODES[code];
84
+ this.type = type;
85
+ this.code = code;
86
+ this.httpStatus = httpStatus;
87
+ this.param = param;
88
+ }
89
+ toEnvelope(requestId) {
90
+ return {
91
+ error: {
92
+ type: this.type,
93
+ code: this.code,
94
+ message: this.message,
95
+ request_id: requestId,
96
+ ...this.param === void 0 ? {} : { param: this.param }
97
+ }
98
+ };
99
+ }
100
+ };
101
+
102
+ // ../core/src/money.ts
103
+ var USD = 1e6;
104
+ function invalid(message3) {
105
+ throw new VettaError("validation_failed", message3);
106
+ }
107
+ function microUsd(value) {
108
+ if (!Number.isSafeInteger(value)) invalid(`micro-USD must be an integer within range, got ${value}`);
109
+ return value;
110
+ }
111
+ function decimalStringFromMicro(amount) {
112
+ const absolute = BigInt(Math.abs(amount));
113
+ const fraction = (absolute % BigInt(USD)).toString().padStart(6, "0").replace(/0+$/, "").padEnd(2, "0");
114
+ return `${amount < 0 ? "-" : ""}${absolute / BigInt(USD)}.${fraction}`;
115
+ }
116
+
11
117
  // ../core/src/session-token.ts
12
118
  var SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
13
119
  var encoder = new TextEncoder();
@@ -36,6 +142,8 @@ var AUDIT_ACTIONS_P1 = [
36
142
  "computer.paused",
37
143
  "computer.resumed",
38
144
  "computer.snapshot_created",
145
+ "computer.updated",
146
+ "browser.live_view_opened",
39
147
  "deployment.created",
40
148
  "deployment.updated",
41
149
  "deployment.deleted",
@@ -95,13 +203,64 @@ var AUDIT_ACTIONS_P3 = [
95
203
  "app.db_queried",
96
204
  /** A ticket was minted that lets one browser past an app's own operator gate (§29.7). */
97
205
  "app.entered",
98
- "blueprint.installed"
206
+ /** The dashboard password was read — the one audited reveal of an app secret (§29.7, ADR-0656). */
207
+ "app.access_read",
208
+ /** The dashboard password was regenerated (and a lost `DASHBOARD_TOKEN` copy repaired). */
209
+ "app.access_rotated",
210
+ /** A link was minted that serves one file's bytes to anyone holding it, for seven days (§40). */
211
+ "file.linked",
212
+ "blueprint.installed",
213
+ /** An install's answers were rewritten — what a template's crew is told about the business (§31.8). */
214
+ "blueprint.context_updated",
215
+ /** The one creator-program effect inside a tenant: a new organization attributed to a creator (§42.6). */
216
+ "referral.attributed"
217
+ ];
218
+ var AUDIT_ACTIONS_PRIMITIVES = [
219
+ "legal_verification.started",
220
+ "legal_member.completed",
221
+ "legal_member.link_resent",
222
+ "legal_company.created",
223
+ "legal_company.submitted",
224
+ "wallet.created",
225
+ "wallet.policy_updated",
226
+ "wallet.frozen",
227
+ "wallet.unfrozen",
228
+ "wallet.funded",
229
+ "wallet.transferred",
230
+ "wallet.swept",
231
+ "wallet.paid",
232
+ "card.issued",
233
+ "card.retried",
234
+ "card.cancelled",
235
+ "card.refunded",
236
+ /** The one read that files: a credential left the vault for a caller (§45.3). */
237
+ "card.credentials_revealed"
238
+ ];
239
+ var AUDIT_ACTIONS_CORE_GAPS = [
240
+ "browser.context_saved",
241
+ "browser.context_granted",
242
+ "browser.context_revoked",
243
+ "browser.credential_stored",
244
+ // core-gaps: U2-browser-api — §32.8's session events, audit-only when no session drove them.
245
+ "browser.session_opened",
246
+ "browser.session_closed",
247
+ "file.imported",
248
+ "app.db_rest",
249
+ "app.db_migrated",
250
+ "app.bucket_created",
251
+ "app.bucket_deleted",
252
+ "app.storage_put",
253
+ "app.storage_get",
254
+ // core-gaps: U9-storage
255
+ "app.storage_deleted"
99
256
  ];
100
257
  var AUDIT_ACTIONS = [
101
258
  ...AUDIT_ACTIONS_SHIPPED,
102
259
  ...AUDIT_ACTIONS_P1,
103
260
  ...AUDIT_ACTIONS_P2,
104
- ...AUDIT_ACTIONS_P3
261
+ ...AUDIT_ACTIONS_P3,
262
+ ...AUDIT_ACTIONS_PRIMITIVES,
263
+ ...AUDIT_ACTIONS_CORE_GAPS
105
264
  ];
106
265
 
107
266
  // ../core/src/schema/common.ts
@@ -119,7 +278,7 @@ var JsonSchemaDocument = z.record(z.string(), z.unknown()).refine((schema) => sc
119
278
  var WindowSchema = z.enum(["immediate", "priority", "loose"]);
120
279
  var TOKEN_TIERS = ["input", "cache_write", "cache_read", "output", "reasoning"];
121
280
  var TokenTierSchema = z.enum(TOKEN_TIERS);
122
- var TOOL_TIERS = ["search", "media", "social"];
281
+ var TOOL_TIERS = ["search", "media", "social", "browser", "storage"];
123
282
  var LINE_ITEM_TIERS = [...TOKEN_TIERS, "computer", ...TOOL_TIERS];
124
283
  var LineItemTierSchema = z.enum(LINE_ITEM_TIERS);
125
284
  var TokenUsageSchema = z.object({
@@ -142,84 +301,352 @@ var HARNESS_IDS = ["pi", "claude_code", "vetta", "hermes"];
142
301
  var HarnessIdSchema = z2.enum(HARNESS_IDS);
143
302
 
144
303
  // ../core/src/schema/agent.ts
304
+ import { z as z5 } from "zod";
305
+
306
+ // ../core/src/schema/browser.ts
307
+ import { z as z4 } from "zod";
308
+
309
+ // ../core/src/schema/org.ts
145
310
  import { z as z3 } from "zod";
146
- var ToolPermissionSchema = z3.enum(["allow", "ask", "deny"]);
147
- var ToolConfigSchema = z3.object({
311
+ var OrganizationSchema = z3.object({
312
+ id: idOf("org"),
313
+ object: z3.literal("organization"),
314
+ name: z3.string(),
315
+ balance_micro_usd: MicroUsdField,
316
+ created_at: Timestamp
317
+ });
318
+ var RoleSchema = z3.enum(["owner", "admin", "member"]);
319
+ var MemberSchema = z3.object({
320
+ id: z3.string(),
321
+ object: z3.literal("member"),
322
+ email: z3.email(),
323
+ role: RoleSchema,
324
+ invited_by: z3.string().nullable(),
325
+ created_at: Timestamp
326
+ });
327
+ var SCOPES = [
328
+ "agents:read",
329
+ "agents:write",
330
+ "sessions:read",
331
+ "sessions:write",
332
+ /**
333
+ * THE MODEL DOOR AND NOTHING ELSE. `/v1/proxy/*` used to take `sessions:write`, which also opens
334
+ * session create and cancel — so a credential handed to a customer's micro-VM, if it leaked, could
335
+ * start sessions for the whole organization. This scope buys exactly one capability, so a sandbox
336
+ * token can be minted with it alone.
337
+ *
338
+ * The proxy routes accept EITHER this or `sessions:write`: narrowing an already-published route
339
+ * would break every key that reaches it today (§17).
340
+ */
341
+ "proxy:write",
342
+ "computers:write",
343
+ "files:read",
344
+ "files:write",
345
+ "billing:read",
346
+ "billing:write",
347
+ "webhooks:*",
348
+ "audit:read",
349
+ "admin"
350
+ ];
351
+ var ScopeSchema = z3.enum(SCOPES);
352
+ var ApiKeySchema = z3.object({
353
+ id: idOf("key"),
354
+ object: z3.literal("api_key"),
355
+ name: z3.string(),
356
+ prefix: z3.string(),
357
+ scopes: z3.array(ScopeSchema),
358
+ created_by: PrincipalSchema,
359
+ last_used_at: Timestamp.nullable(),
360
+ created_at: Timestamp
361
+ });
362
+ var LedgerEntrySchema = z3.object({
363
+ id: idOf("led"),
364
+ type: z3.enum(["debit", "credit"]),
365
+ amount_micro_usd: MicroUsdField,
366
+ balance_after_micro_usd: MicroUsdField,
367
+ actor: PrincipalSchema,
368
+ session_id: idOf("ses").nullable(),
369
+ agent_id: idOf("agt").nullable(),
370
+ deployment_id: idOf("dep").nullable(),
371
+ line_items: z3.array(z3.object({ tier: LineItemTierSchema, amount_micro_usd: MicroUsdField })),
372
+ created_at: Timestamp
373
+ });
374
+ var TopupSchema = z3.object({
375
+ id: z3.string(),
376
+ object: z3.literal("topup"),
377
+ amount_micro_usd: MicroUsdField.positive(),
378
+ status: z3.enum(["pending", "requires_action", "succeeded", "failed"]),
379
+ action_url: z3.url().nullable(),
380
+ balance_after_micro_usd: MicroUsdField.nullable(),
381
+ created_at: Timestamp
382
+ });
383
+ var WebhookEndpointSchema = z3.object({
384
+ id: idOf("whk"),
385
+ object: z3.literal("webhook_endpoint"),
386
+ url: z3.url(),
387
+ events: z3.array(z3.string()),
148
388
  enabled: z3.boolean(),
149
- permission: ToolPermissionSchema,
150
- config: z3.record(z3.string(), z3.unknown()).optional()
389
+ created_at: Timestamp
390
+ });
391
+ var WebhookDeliverySchema = z3.object({
392
+ id: idOf("whd"),
393
+ event_id: idOf("evt"),
394
+ event_type: z3.string(),
395
+ status: z3.enum(["pending", "succeeded", "failed"]),
396
+ attempts: z3.number().int().nonnegative(),
397
+ response_status: z3.number().int().nullable(),
398
+ response_snippet: z3.string().nullable(),
399
+ delivered_at: Timestamp.nullable(),
400
+ next_retry_at: Timestamp.nullable()
401
+ });
402
+ var AuditEntrySchema = z3.object({
403
+ id: idOf("aud"),
404
+ object: z3.literal("audit_entry"),
405
+ actor: PrincipalSchema,
406
+ action: AuditActionSchema,
407
+ resource_type: z3.string(),
408
+ resource_id: z3.string(),
409
+ request_id: z3.string(),
410
+ metadata: Metadata,
411
+ created_at: Timestamp
412
+ });
413
+
414
+ // ../core/src/schema/browser.ts
415
+ var BROWSER_MAX_TIMEOUT_MINUTES = 360;
416
+ var BROWSER_REGIONS = ["us-west", "us-east", "eu-central", "ap-southeast"];
417
+ var BROWSER_OS = ["windows", "mac", "linux", "mobile", "tablet"];
418
+ var BROWSER_TOOL_ACTIONS = [
419
+ "goto",
420
+ "click",
421
+ "type",
422
+ "extract",
423
+ "act",
424
+ "observe",
425
+ "links",
426
+ "screenshot",
427
+ "login",
428
+ "signup",
429
+ "save_context"
430
+ ];
431
+ var BrowserOptionsSchema = z4.object({
432
+ allowed_domains: z4.array(z4.string().min(1)).min(1),
433
+ timeout_minutes: z4.number().int().min(1).max(BROWSER_MAX_TIMEOUT_MINUTES).default(15),
434
+ region: z4.enum(BROWSER_REGIONS).default("us-west"),
435
+ keep_alive: z4.boolean().default(false),
436
+ viewport: z4.object({ width: z4.number().int().positive(), height: z4.number().int().positive() }).optional(),
437
+ os: z4.enum(BROWSER_OS).optional(),
438
+ block_ads: z4.boolean().default(false),
439
+ solve_captchas: z4.boolean().default(true),
440
+ stealth: z4.boolean().default(false),
441
+ allow_writes: z4.boolean().default(false),
442
+ allow_extract: z4.boolean().default(false),
443
+ context_name: z4.string().min(1).max(64).optional(),
444
+ persist_context: z4.boolean().default(false),
445
+ human_login: z4.boolean().default(false),
446
+ proxy: z4.boolean().default(false),
447
+ ask_actions: z4.array(z4.enum(BROWSER_TOOL_ACTIONS)).default(["signup"])
448
+ }).refine((options) => !(options.proxy && options.allowed_domains.includes("*")), {
449
+ error: "a proxied browser needs a bounded allow-list, not *",
450
+ path: ["proxy"]
451
+ });
452
+ var BrowserSessionStatusSchema = z4.enum(["creating", "active", "closed", "error"]);
453
+ var BrowserSessionSchema = z4.object({
454
+ id: idOf("brw"),
455
+ object: z4.literal("browser_session"),
456
+ computer_id: idOf("cmp").nullable(),
457
+ /** The agent session that opened it, if an agent did; a REST caller's browser carries `null`. */
458
+ session_id: idOf("ses").nullable(),
459
+ status: BrowserSessionStatusSchema,
460
+ allowed_domains: z4.array(z4.string()),
461
+ region: z4.enum(BROWSER_REGIONS),
462
+ context_id: idOf("bct").nullable(),
463
+ context_backed: z4.boolean(),
464
+ timeout_at: Timestamp,
465
+ opened_at: Timestamp,
466
+ closed_at: Timestamp.nullable(),
467
+ created_at: Timestamp
468
+ });
469
+ var BrowserLiveViewSchema = z4.object({
470
+ object: z4.literal("browser_live_view"),
471
+ url: z4.url(),
472
+ expires_at: Timestamp
473
+ });
474
+ var BrowserContextSchema = z4.object({
475
+ id: idOf("bct"),
476
+ object: z4.literal("browser_context"),
477
+ identity_id: idOf("idn"),
478
+ name: z4.string(),
479
+ service: z4.string().nullable(),
480
+ created_at: Timestamp,
481
+ last_used_at: Timestamp.nullable()
482
+ });
483
+ var BrowserGranteeTypeSchema = z4.enum(["agent", "role"]);
484
+ var BrowserGrantSchema = z4.object({
485
+ id: idOf("bgr"),
486
+ object: z4.literal("browser_grant"),
487
+ context_id: idOf("bct"),
488
+ grantee_type: BrowserGranteeTypeSchema,
489
+ grantee_id: z4.union([idOf("agt"), RoleSchema]),
490
+ created_at: Timestamp
491
+ });
492
+ var SaveBrowserContextSchema = z4.object({
493
+ name: z4.string().min(1).max(64),
494
+ identity_id: idOf("idn"),
495
+ service: z4.string().min(1).optional()
496
+ });
497
+ var CreateBrowserGrantSchema = z4.object({
498
+ identity_id: idOf("idn"),
499
+ grantee_type: BrowserGranteeTypeSchema,
500
+ grantee_id: z4.union([idOf("agt"), RoleSchema])
501
+ });
502
+ var Selector = z4.string().min(1);
503
+ var Instruction = z4.string().min(1).max(4096);
504
+ var BrowserActionRequestSchema = z4.discriminatedUnion("action", [
505
+ z4.object({ action: z4.literal("goto"), url: z4.string().url() }),
506
+ z4.object({ action: z4.literal("click"), selector: Selector }),
507
+ z4.object({ action: z4.literal("type"), selector: Selector, text: z4.string() }),
508
+ z4.object({
509
+ action: z4.literal("extract"),
510
+ instruction: Instruction.optional(),
511
+ selector: Selector.optional(),
512
+ schema: z4.record(z4.string(), z4.unknown()).optional()
513
+ }),
514
+ z4.object({
515
+ action: z4.literal("act"),
516
+ instruction: Instruction,
517
+ variables: z4.record(z4.string(), z4.string()).optional()
518
+ }),
519
+ z4.object({ action: z4.literal("observe"), instruction: Instruction }),
520
+ z4.object({
521
+ action: z4.literal("links"),
522
+ contains: z4.string().optional(),
523
+ limit: z4.number().int().min(1).max(200).default(50)
524
+ }),
525
+ z4.object({ action: z4.literal("screenshot") })
526
+ ]);
527
+ var BROWSER_ACTIONS = BrowserActionRequestSchema.options.map(
528
+ (option) => option.shape.action.value
529
+ );
530
+ var BrowserActionResultSchema = z4.object({
531
+ object: z4.literal("browser_action"),
532
+ action: z4.enum(BROWSER_ACTIONS),
533
+ url: z4.string(),
534
+ text: z4.string().nullable(),
535
+ data: z4.unknown().nullable(),
536
+ links: z4.array(z4.object({ href: z4.string(), text: z4.string() })).nullable(),
537
+ elements: z4.array(z4.object({ selector: z4.string(), description: z4.string() })).nullable(),
538
+ file_id: idOf("fil").nullable(),
539
+ performed: z4.boolean()
540
+ });
541
+ var BrowserLoginSchema = z4.object({
542
+ service: z4.string().min(1),
543
+ url: z4.string().url(),
544
+ identity_id: idOf("idn")
545
+ });
546
+ var BrowserSignupSchema = BrowserLoginSchema.extend({ email: z4.string().email() });
547
+ var BrowserCredentialSchema = z4.object({
548
+ identity_id: idOf("idn"),
549
+ service: z4.string().min(1),
550
+ email: z4.string().email(),
551
+ password: z4.string().min(1),
552
+ username: z4.string().min(1).optional()
151
553
  });
152
- var ToolsetSchema = z3.object({
153
- default_config: z3.object({ permission: ToolPermissionSchema }),
154
- configs: z3.record(z3.string(), ToolConfigSchema)
554
+ var LiveViewSchema = z4.object({ url: z4.string().url(), expires_at: Timestamp });
555
+
556
+ // ../core/src/schema/agent.ts
557
+ var ToolPermissionSchema = z5.enum(["allow", "ask", "deny"]);
558
+ var ToolConfigSchema = z5.object({
559
+ enabled: z5.boolean(),
560
+ permission: ToolPermissionSchema,
561
+ config: z5.record(z5.string(), z5.unknown()).optional()
562
+ });
563
+ var ToolsetSchema = z5.object({
564
+ default_config: z5.object({ permission: ToolPermissionSchema }),
565
+ configs: z5.record(z5.string(), ToolConfigSchema)
566
+ }).superRefine((tools, ctx) => {
567
+ const config = tools.configs["browser"]?.config;
568
+ if (config === void 0) return;
569
+ for (const issue of BrowserOptionsSchema.safeParse(config).error?.issues ?? []) {
570
+ ctx.addIssue({ code: "custom", message: issue.message, path: ["configs", "browser", "config", ...issue.path] });
571
+ }
155
572
  });
156
- var BudgetSchema = z3.object({
573
+ var BudgetSchema = z5.object({
157
574
  cap_micro_usd: MicroUsdField.nonnegative(),
158
575
  max_task_micro_usd: MicroUsdField.nonnegative(),
159
- period: z3.enum(["day", "week", "month"])
576
+ period: z5.enum(["day", "week", "month"])
160
577
  });
161
- var SkillRefSchema = z3.string().regex(/^[a-z0-9][a-z0-9-]*(@\d+)?$/, {
162
- error: "expected `slug` or `slug@N`"
578
+ var SkillRefSchema = z5.string().regex(/^(naive\/)?[a-z0-9][a-z0-9-]*(@\d+)?$/, {
579
+ error: "expected `slug`, `slug@N`, `naive/slug` or `naive/slug@N`"
163
580
  });
164
- var McpServerSchema = z3.object({
165
- name: z3.string(),
166
- url: z3.url(),
167
- headers: z3.record(z3.string(), z3.string()).optional()
581
+ var McpServerSchema = z5.object({
582
+ name: z5.string(),
583
+ url: z5.url(),
584
+ headers: z5.record(z5.string(), z5.string()).optional()
168
585
  });
169
- var RosterEntrySchema = z3.union([
586
+ var RosterEntrySchema = z5.union([
170
587
  idOf("agt").transform((id) => ({ type: "agent", id })),
171
- z3.object({
172
- type: z3.literal("agent"),
588
+ z5.object({
589
+ type: z5.literal("agent"),
173
590
  id: idOf("agt"),
174
- version: z3.number().int().positive().optional()
591
+ version: z5.number().int().positive().optional()
175
592
  }),
176
- z3.object({ type: z3.literal("self") }),
177
- z3.object({ type: z3.literal("advisor"), model: z3.string().min(1) })
593
+ z5.object({ type: z5.literal("self") }),
594
+ z5.object({ type: z5.literal("advisor"), model: z5.string().min(1) })
178
595
  ]);
179
596
  var SINGLETON_ENTRIES = ["self", "advisor"];
180
- var MultiagentSchema = z3.object({
181
- type: z3.literal("coordinator"),
182
- agents: z3.array(RosterEntrySchema).min(1).max(20).refine(
597
+ var MultiagentSchema = z5.object({
598
+ type: z5.literal("coordinator"),
599
+ agents: z5.array(RosterEntrySchema).min(1).max(20).refine(
183
600
  (roster) => SINGLETON_ENTRIES.every((t) => roster.filter((e) => e.type === t).length <= 1),
184
601
  { error: "a roster carries at most one `self` and at most one `advisor` entry" }
185
602
  ),
186
- board: z3.boolean().optional()
603
+ board: z5.boolean().optional()
187
604
  });
188
- var MultiagentField = z3.union([z3.boolean(), MultiagentSchema, z3.null()]);
605
+ var MultiagentField = z5.union([z5.boolean(), MultiagentSchema, z5.null()]);
606
+ function normaliseMultiagent(value) {
607
+ if (value === true) return { type: "coordinator", agents: [], board: false };
608
+ if (value === false || value === null) return null;
609
+ return value;
610
+ }
611
+ var HandoffsField = z5.union([z5.boolean(), z5.literal("team"), z5.array(z5.string().min(1)).max(50)]);
612
+ var handoffsOf = (config) => config.handoffs ?? true;
613
+ var handoffsEnabled = (handoffs) => handoffs === true || handoffs === "team" || handoffs !== false && handoffs.length > 0;
189
614
  var agentConfigShape = {
190
- model: z3.string(),
615
+ model: z5.string(),
191
616
  window: WindowSchema,
192
- system: z3.string(),
617
+ system: z5.string(),
193
618
  tools: ToolsetSchema,
194
- mcp_servers: z3.array(McpServerSchema),
195
- skills: z3.array(SkillRefSchema),
619
+ mcp_servers: z5.array(McpServerSchema),
620
+ skills: z5.array(SkillRefSchema),
196
621
  multiagent: MultiagentField,
622
+ /** Optional on the type so a version minted before §28.15 still parses; read it through `handoffsOf`. */
623
+ handoffs: HandoffsField.optional(),
197
624
  budget: BudgetSchema,
198
625
  output_schema: JsonSchemaDocument.nullable(),
199
- structured_output_required: z3.boolean(),
200
- description: z3.string().nullable(),
626
+ structured_output_required: z5.boolean(),
627
+ description: z5.string().nullable(),
201
628
  metadata: Metadata
202
629
  };
203
- var AgentVersionSchema = z3.object({
204
- version: z3.number().int().positive(),
630
+ var AgentVersionSchema = z5.object({
631
+ version: z5.number().int().positive(),
205
632
  created_at: Timestamp,
206
633
  created_by: PrincipalSchema,
207
634
  ...agentConfigShape
208
635
  });
209
- var AgentSchema = z3.object({
636
+ var AgentSchema = z5.object({
210
637
  id: idOf("agt"),
211
- object: z3.literal("agent"),
212
- name: z3.string(),
638
+ object: z5.literal("agent"),
639
+ name: z5.string(),
213
640
  /** The agent loop this agent runs (§19). Chosen at create; the catalog lives in `harness.ts`. */
214
641
  harness: HarnessIdSchema,
215
- current_version: z3.number().int().positive(),
642
+ current_version: z5.number().int().positive(),
216
643
  created_at: Timestamp,
217
644
  ...agentConfigShape
218
645
  });
219
646
 
220
647
  // ../core/src/schema/session.ts
221
- import { z as z4 } from "zod";
222
- var SessionStatusSchema = z4.enum([
648
+ import { z as z6 } from "zod";
649
+ var SessionStatusSchema = z6.enum([
223
650
  "queued",
224
651
  "running",
225
652
  "idle",
@@ -227,7 +654,7 @@ var SessionStatusSchema = z4.enum([
227
654
  "failed",
228
655
  "cancelled"
229
656
  ]);
230
- var StopReasonSchema = z4.enum([
657
+ var StopReasonSchema = z6.enum([
231
658
  "end_turn",
232
659
  "awaiting_input",
233
660
  "awaiting_approval",
@@ -253,61 +680,61 @@ var StopReasonSchema = z4.enum([
253
680
  */
254
681
  "awaiting_answer"
255
682
  ]);
256
- var ThreadRoleSchema = z4.enum(["coordinator", "member"]);
257
- var MessageSchema = z4.object({
258
- role: z4.enum(["system", "user", "assistant", "tool"]),
259
- content: z4.string(),
260
- tool_call_id: z4.string().optional()
683
+ var ThreadRoleSchema = z6.enum(["coordinator", "member"]);
684
+ var MessageSchema = z6.object({
685
+ role: z6.enum(["system", "user", "assistant", "tool"]),
686
+ content: z6.string(),
687
+ tool_call_id: z6.string().optional()
261
688
  });
262
- var ToolResultSchema = z4.object({
263
- output: z4.unknown(),
264
- is_error: z4.boolean().optional(),
265
- content: z4.string().optional()
689
+ var ToolResultSchema = z6.object({
690
+ output: z6.unknown(),
691
+ is_error: z6.boolean().optional(),
692
+ content: z6.string().optional()
266
693
  });
267
694
  var QuestionFieldBase = {
268
- key: z4.string().min(1).max(64),
269
- label: z4.string().min(1).max(200),
270
- help: z4.string().max(500).optional()
695
+ key: z6.string().min(1).max(64),
696
+ label: z6.string().min(1).max(200),
697
+ help: z6.string().max(500).optional()
271
698
  };
272
- var QuestionFieldSchema = z4.discriminatedUnion("type", [
273
- z4.object({
699
+ var QuestionFieldSchema = z6.discriminatedUnion("type", [
700
+ z6.object({
274
701
  ...QuestionFieldBase,
275
- type: z4.literal("text"),
276
- placeholder: z4.string().max(200).optional()
702
+ type: z6.literal("text"),
703
+ placeholder: z6.string().max(200).optional()
277
704
  }),
278
- z4.object({
705
+ z6.object({
279
706
  ...QuestionFieldBase,
280
- type: z4.literal("choice"),
707
+ type: z6.literal("choice"),
281
708
  /**
282
709
  * Plain strings: the option is both the value and the label, because the answer is read back by
283
710
  * a model, which wants the words and not an id. Fewer than two is not a choice, and more than
284
711
  * twelve is a list — which belongs in the prompt.
285
712
  */
286
- options: z4.array(z4.string().min(1).max(200)).min(2).max(12),
287
- multiple: z4.boolean().default(false),
713
+ options: z6.array(z6.string().min(1).max(200)).min(2).max(12),
714
+ multiple: z6.boolean().default(false),
288
715
  /**
289
716
  * The free-text escape, and it defaults to **true** on purpose: the agent wrote the options and
290
717
  * the agent is the one that does not know the answer, so an operator must always be able to say
291
718
  * something it did not think of. A question that truly admits only its list opts out explicitly.
292
719
  */
293
- other: z4.boolean().default(true)
720
+ other: z6.boolean().default(true)
294
721
  })
295
722
  ]);
296
- var QuestionSchema = z4.object({
297
- prompt: z4.string().min(1).max(1e3),
298
- fields: z4.array(QuestionFieldSchema).min(1).max(3)
723
+ var QuestionSchema = z6.object({
724
+ prompt: z6.string().min(1).max(1e3),
725
+ fields: z6.array(QuestionFieldSchema).min(1).max(3)
299
726
  });
300
- var AnswersSchema = z4.record(
301
- z4.string().min(1).max(64),
302
- z4.union([z4.string().max(2e3), z4.array(z4.string().max(2e3)).min(1).max(12)])
727
+ var AnswersSchema = z6.record(
728
+ z6.string().min(1).max(64),
729
+ z6.union([z6.string().max(2e3), z6.array(z6.string().max(2e3)).min(1).max(12)])
303
730
  ).refine((answers) => Object.keys(answers).length <= 3, {
304
731
  message: "a question has at most 3 fields, so an answer has at most 3 keys"
305
732
  });
306
- var PendingActionSchema = z4.object({
307
- kind: z4.enum(["tool", "question"]).default("tool"),
308
- tool_call_id: z4.string(),
309
- name: z4.string(),
310
- args: z4.record(z4.string(), z4.unknown()),
733
+ var PendingActionSchema = z6.object({
734
+ kind: z6.enum(["tool", "question"]).default("tool"),
735
+ tool_call_id: z6.string(),
736
+ name: z6.string(),
737
+ args: z6.record(z6.string(), z6.unknown()),
311
738
  /**
312
739
  * Present iff `kind` is `"question"`, and it is the parse of `args` rather than a second source
313
740
  * of truth: `args` is what the model called and therefore what replays byte-identically, while
@@ -316,11 +743,11 @@ var PendingActionSchema = z4.object({
316
743
  */
317
744
  question: QuestionSchema.optional()
318
745
  });
319
- var SessionSchema = z4.object({
746
+ var SessionSchema = z6.object({
320
747
  id: idOf("ses"),
321
- object: z4.literal("session"),
748
+ object: z6.literal("session"),
322
749
  agent_id: idOf("agt"),
323
- agent_version: z4.number().int().positive(),
750
+ agent_version: z6.number().int().positive(),
324
751
  deployment_id: idOf("dep").nullable(),
325
752
  /**
326
753
  * The persona this session acts as (`canonical-spec §22`). Chosen per task from the personas the
@@ -349,38 +776,39 @@ var SessionSchema = z4.object({
349
776
  budget_micro_usd: MicroUsdField.nonnegative(),
350
777
  consumed_micro_usd: MicroUsdField.nonnegative(),
351
778
  token_usage: TokenUsageSchema,
352
- pending_actions: z4.array(PendingActionSchema),
779
+ pending_actions: z6.array(PendingActionSchema),
353
780
  output_schema: JsonSchemaDocument.nullable(),
354
- structured_output: z4.record(z4.string(), z4.unknown()).nullable(),
355
- last_seq: z4.number().int().nonnegative(),
781
+ structured_output: z6.record(z6.string(), z6.unknown()).nullable(),
782
+ last_seq: z6.number().int().nonnegative(),
356
783
  metadata: Metadata,
357
784
  started_at: Timestamp.nullable(),
358
785
  ended_at: Timestamp.nullable(),
359
786
  created_at: Timestamp
360
787
  });
361
- var SendMessageSchema = z4.object({
362
- message: z4.string(),
363
- interrupt: z4.boolean().optional(),
364
- idempotency_key: z4.string().optional()
788
+ var SendMessageSchema = z6.object({
789
+ message: z6.string(),
790
+ interrupt: z6.boolean().optional(),
791
+ idempotency_key: z6.string().optional()
365
792
  });
366
- var SendAcceptedSchema = z4.object({
367
- session_id: z4.string(),
793
+ var SendAcceptedSchema = z6.object({
794
+ session_id: z6.string(),
368
795
  status: SessionStatusSchema,
369
- accepted_seq: z4.number().int().nonnegative()
796
+ accepted_seq: z6.number().int().nonnegative()
370
797
  });
371
- var ToolConfirmationSchema = z4.object({
372
- tool_call_id: z4.string(),
373
- decision: z4.enum(["allow", "deny"]),
374
- reason: z4.string().optional()
798
+ var ToolConfirmationSchema = z6.object({
799
+ tool_call_id: z6.string(),
800
+ decision: z6.enum(["allow", "deny"]),
801
+ reason: z6.string().optional(),
802
+ scope: z6.enum(["call", "session"]).default("call")
375
803
  });
376
- var SessionAnswerSchema = z4.object({
377
- tool_call_id: z4.string(),
378
- answers: z4.record(z4.string(), z4.union([z4.string(), z4.array(z4.string())]))
804
+ var SessionAnswerSchema = z6.object({
805
+ tool_call_id: z6.string(),
806
+ answers: z6.record(z6.string(), z6.union([z6.string(), z6.array(z6.string())]))
379
807
  });
380
808
 
381
809
  // ../core/src/schema/event.ts
382
- import { z as z5 } from "zod";
383
- var EventTypeSchema = z5.enum([
810
+ import { z as z7 } from "zod";
811
+ var EventTypeSchema = z7.enum([
384
812
  "message.delta",
385
813
  "message.completed",
386
814
  "tool.started",
@@ -400,57 +828,81 @@ var EventTypeSchema = z5.enum([
400
828
  "thread.created",
401
829
  "thread.running",
402
830
  "thread.idle",
403
- "thread.terminated"
831
+ "thread.terminated",
832
+ // core-gaps: contracts (`canonical-spec §32.8`, §33.5, §36.3, §37.3, §38.3)
833
+ "browser.session.opened",
834
+ "browser.session.closed",
835
+ "browser.context.saved",
836
+ "browser.context.granted",
837
+ "browser.context.revoked",
838
+ "media.job.queued",
839
+ "media.job.completed",
840
+ "media.job.failed",
841
+ "file.imported",
842
+ "app.db.migrated",
843
+ "app.storage.bucket.created",
844
+ "app.storage.bucket.deleted",
845
+ // identity money (`canonical-spec §44.3`, §45.3). Every one of these is driven over REST, which
846
+ // has no session stream, so the org's webhook subscriptions are the only push a customer gets.
847
+ "wallet.created",
848
+ "wallet.policy.updated",
849
+ "wallet.transaction.confirmed",
850
+ "wallet.swept",
851
+ "card.issued",
852
+ "card.failed",
853
+ "card.cancelled",
854
+ "card.refunded",
855
+ "card.credentials_revealed"
404
856
  ]);
405
- var EventSchema = z5.object({
857
+ var EventSchema = z7.object({
406
858
  id: idOf("evt"),
407
- seq: z5.number().int().positive(),
859
+ seq: z7.number().int().positive(),
408
860
  type: EventTypeSchema,
409
861
  session_id: idOf("ses"),
410
- data: z5.record(z5.string(), z5.unknown()),
862
+ data: z7.record(z7.string(), z7.unknown()),
411
863
  created_at: Timestamp
412
864
  });
413
- var ThreadEventDataSchema = z5.looseObject({ thread_id: idOf("ses") });
414
- var SessionUsageDataSchema = z5.object({
865
+ var ThreadEventDataSchema = z7.looseObject({ thread_id: idOf("ses") });
866
+ var SessionUsageDataSchema = z7.object({
415
867
  token_usage: TokenUsageSchema,
416
868
  consumed_micro_usd: MicroUsdField.nonnegative(),
417
- active_seconds: z5.number().nonnegative(),
418
- tool_calls: z5.number().int().nonnegative()
869
+ active_seconds: z7.number().nonnegative(),
870
+ tool_calls: z7.number().int().nonnegative()
419
871
  });
420
872
 
421
873
  // ../core/src/schema/board.ts
422
- import { z as z6 } from "zod";
874
+ import { z as z8 } from "zod";
423
875
  var BOARD_STATUSES = ["todo", "doing", "blocked", "done"];
424
- var BoardStatusSchema = z6.enum(BOARD_STATUSES);
425
- var BoardCountsSchema = z6.object({
426
- todo: z6.number().int().nonnegative(),
427
- doing: z6.number().int().nonnegative(),
428
- blocked: z6.number().int().nonnegative(),
429
- done: z6.number().int().nonnegative()
430
- });
431
- var BoardSchema = z6.object({
876
+ var BoardStatusSchema = z8.enum(BOARD_STATUSES);
877
+ var BoardCountsSchema = z8.object({
878
+ todo: z8.number().int().nonnegative(),
879
+ doing: z8.number().int().nonnegative(),
880
+ blocked: z8.number().int().nonnegative(),
881
+ done: z8.number().int().nonnegative()
882
+ });
883
+ var BoardSchema = z8.object({
432
884
  id: idOf("brd"),
433
- object: z6.literal("board"),
885
+ object: z8.literal("board"),
434
886
  agent_id: idOf("agt"),
435
887
  cards: BoardCountsSchema,
436
888
  created_at: Timestamp
437
889
  });
438
- var BoardCardSchema = z6.object({
890
+ var BoardCardSchema = z8.object({
439
891
  id: idOf("crd"),
440
- object: z6.literal("board_card"),
892
+ object: z8.literal("board_card"),
441
893
  board_id: idOf("brd"),
442
- title: z6.string().min(1),
443
- body: z6.string(),
894
+ title: z8.string().min(1),
895
+ body: z8.string(),
444
896
  status: BoardStatusSchema,
445
- assignee: z6.string().nullable(),
897
+ assignee: z8.string().nullable(),
446
898
  /** The cards this one waits on. A `blocked` card with an empty list is stuck on the world. */
447
- blocked_by: z6.array(idOf("crd")),
899
+ blocked_by: z8.array(idOf("crd")),
448
900
  created_at: Timestamp,
449
901
  updated_at: Timestamp
450
902
  });
451
- var BoardCommentSchema = z6.object({
903
+ var BoardCommentSchema = z8.object({
452
904
  id: idOf("bcm"),
453
- object: z6.literal("board_comment"),
905
+ object: z8.literal("board_comment"),
454
906
  card_id: idOf("crd"),
455
907
  /**
456
908
  * Who wrote it, as an id the server derived — never a name the caller supplied. A member's
@@ -458,105 +910,221 @@ var BoardCommentSchema = z6.object({
458
910
  * `key_` principal. The prefix is what lets a reader tell an agent's comment from a person's,
459
911
  * which is the distinction a free-text author field could not make and could be used to forge.
460
912
  */
461
- author: z6.string(),
462
- body: z6.string(),
913
+ author: z8.string(),
914
+ body: z8.string(),
463
915
  created_at: Timestamp
464
916
  });
465
917
 
466
918
  // ../core/src/schema/identity.ts
467
- import { z as z7 } from "zod";
919
+ import { z as z9 } from "zod";
468
920
  var IDENTITY_NAME_MAX = 128;
469
921
  var IDENTITY_DESCRIPTION_MAX = 1024;
470
- var IdentitySchema = z7.object({
922
+ var IdentitySchema = z9.object({
471
923
  id: idOf("idn"),
472
- object: z7.literal("identity"),
924
+ object: z9.literal("identity"),
473
925
  /** Human-facing persona name. Surfaced to the model so it acts in character. */
474
- name: z7.string().min(1).max(IDENTITY_NAME_MAX),
926
+ name: z9.string().min(1).max(IDENTITY_NAME_MAX),
475
927
  /** What the persona is for. The model reads it to choose tone, scope and endpoint. */
476
- description: z7.string().max(IDENTITY_DESCRIPTION_MAX).nullable(),
928
+ description: z9.string().max(IDENTITY_DESCRIPTION_MAX).nullable(),
477
929
  /** Inbox addresses (`localpart@domain`) this identity owns. */
478
- emails: z7.array(z7.string()),
930
+ emails: z9.array(z9.string()),
479
931
  /** Provisioned numbers in E.164. */
480
- phones: z7.array(z7.string()),
932
+ phones: z9.array(z9.string()),
481
933
  /** Verified web domains reachable through this identity's inboxes. */
482
- domains: z7.array(z7.string()),
934
+ domains: z9.array(z9.string()),
483
935
  /** `conn_` ids of authorized third-party connections. */
484
- connections: z7.array(z7.string()),
936
+ connections: z9.array(z9.string()),
485
937
  metadata: Metadata,
486
938
  created_at: Timestamp,
487
939
  updated_at: Timestamp
488
940
  });
489
941
 
490
942
  // ../core/src/schema/resource.ts
491
- import { z as z8 } from "zod";
492
- var ComputerStatusSchema = z8.enum(["creating", "running", "paused", "failed", "destroyed"]);
493
- var VolumeSchema = z8.object({
943
+ import { z as z11 } from "zod";
944
+
945
+ // ../core/src/schema/files-library.ts
946
+ import { z as z10 } from "zod";
947
+ var FILE_KINDS = ["image", "video", "audio", "document", "other"];
948
+ var FileKindSchema = z10.enum(FILE_KINDS);
949
+ var FILE_SOURCE_TYPES = [
950
+ "upload",
951
+ "url_import",
952
+ "image_generation",
953
+ "video_generation",
954
+ "video_clipping",
955
+ "screenshot",
956
+ "speech_generation"
957
+ ];
958
+ var FileSourceTypeSchema = z10.enum(FILE_SOURCE_TYPES);
959
+ var FileSourceSchema = z10.object({
960
+ type: FileSourceTypeSchema,
961
+ job_id: z10.union([idOf("med"), idOf("clj")]).nullable(),
962
+ url: z10.string().url().nullable()
963
+ });
964
+ var FileTagsSchema = z10.array(z10.string().min(1).max(64)).max(32);
965
+ var FileLibraryFieldsSchema = z10.object({
966
+ title: z10.string().max(256).nullable().default(null),
967
+ description: z10.string().max(4096).nullable().default(null),
968
+ tags: FileTagsSchema.default([]),
969
+ kind: FileKindSchema.default("other"),
970
+ source: FileSourceSchema.nullable().default(null),
971
+ thumbnail_file_id: idOf("fil").nullable().default(null),
972
+ width: z10.number().int().positive().nullable().default(null),
973
+ height: z10.number().int().positive().nullable().default(null),
974
+ duration_seconds: z10.number().nonnegative().nullable().default(null)
975
+ });
976
+ var UpdateFileSchema = z10.object({
977
+ title: z10.string().max(256).nullable().optional(),
978
+ description: z10.string().max(4096).nullable().optional(),
979
+ tags: FileTagsSchema.optional()
980
+ });
981
+ var ImportFileSchema = z10.object({
982
+ url: z10.string().url().startsWith("http"),
983
+ title: z10.string().max(256).optional(),
984
+ description: z10.string().max(4096).optional(),
985
+ tags: FileTagsSchema.optional()
986
+ });
987
+
988
+ // ../core/src/schema/resource.ts
989
+ var ComputerStatusSchema = z11.enum(["creating", "running", "paused", "failed", "destroyed"]);
990
+ var VolumeSchema = z11.object({
494
991
  id: idOf("vol"),
495
- size_gb: z8.number().int().positive(),
496
- mount_path: z8.string()
992
+ size_gb: z11.number().int().positive(),
993
+ mount_path: z11.string()
497
994
  });
498
- var ComputerSchema = z8.object({
995
+ var ComputerSchema = z11.object({
499
996
  id: idOf("cmp"),
500
- object: z8.literal("computer"),
501
- name: z8.string(),
502
- vcpu: z8.number().int().positive(),
503
- memory_mb: z8.number().int().positive(),
504
- disk_gb: z8.number().int().positive(),
997
+ object: z11.literal("computer"),
998
+ name: z11.string(),
999
+ vcpu: z11.number().int().positive(),
1000
+ memory_mb: z11.number().int().positive(),
1001
+ disk_gb: z11.number().int().positive(),
505
1002
  status: ComputerStatusSchema,
506
- browser: z8.object({ allowed_domains: z8.array(z8.string()) }).nullable(),
1003
+ // core-gaps: U2-browser-api — the whole option object (§32.2); `{ allowed_domains }` still parses.
1004
+ browser: BrowserOptionsSchema.nullable(),
507
1005
  volume: VolumeSchema.nullable(),
508
1006
  snapshot_id: idOf("snp").nullable(),
509
1007
  created_at: Timestamp
510
1008
  });
511
- var SnapshotSchema = z8.object({
1009
+ var SnapshotSchema = z11.object({
512
1010
  snapshot_id: idOf("snp"),
513
1011
  computer_id: idOf("cmp"),
514
1012
  created_at: Timestamp
515
1013
  });
516
- var SkillSchema = z8.object({
1014
+ var ComputerProbeSchema = z11.object({
1015
+ sampled_at: Timestamp,
1016
+ uptime_seconds: z11.number().int().nonnegative(),
1017
+ processes: z11.number().int().nonnegative(),
1018
+ load_1m: z11.number().nonnegative(),
1019
+ memory_used_mb: z11.number().int().nonnegative(),
1020
+ memory_total_mb: z11.number().int().positive(),
1021
+ disk_used_gb: z11.number().nonnegative(),
1022
+ disk_total_gb: z11.number().positive()
1023
+ });
1024
+ var ComputerInspectSchema = z11.object({
1025
+ object: z11.literal("computer_inspect"),
1026
+ id: idOf("cmp"),
1027
+ status: ComputerStatusSchema,
1028
+ last_active_at: Timestamp.nullable(),
1029
+ /** Where the provider's file API is rooted (§41.1); `null` when it does not say. */
1030
+ home: z11.string().min(1).nullable(),
1031
+ probe: ComputerProbeSchema.nullable()
1032
+ });
1033
+ var COMMAND_OUTPUT_CAP = 65536;
1034
+ var ComputerCommandSchema = z11.object({
1035
+ id: idOf("cmd"),
1036
+ object: z11.literal("computer_command"),
1037
+ computer_id: idOf("cmp"),
1038
+ command: z11.string(),
1039
+ exit_code: z11.number().int().nullable(),
1040
+ timed_out: z11.boolean(),
1041
+ duration_ms: z11.number().int().nonnegative(),
1042
+ stdout: z11.string().max(COMMAND_OUTPUT_CAP),
1043
+ stderr: z11.string().max(COMMAND_OUTPUT_CAP),
1044
+ truncated: z11.boolean(),
1045
+ actor: PrincipalSchema,
1046
+ created_at: Timestamp
1047
+ });
1048
+ var ExecStreamFrameSchema = z11.union([
1049
+ z11.object({ stdout: z11.string() }),
1050
+ z11.object({ stderr: z11.string() }),
1051
+ z11.object({
1052
+ exit: z11.object({
1053
+ exit_code: z11.number().int().nullable(),
1054
+ timed_out: z11.boolean(),
1055
+ duration_ms: z11.number().int().nonnegative(),
1056
+ command_id: idOf("cmd")
1057
+ })
1058
+ })
1059
+ ]);
1060
+ var FileEntryTypeSchema = z11.enum(["file", "directory", "symlink", "other"]);
1061
+ var FileEntrySchema = z11.object({
1062
+ name: z11.string().min(1),
1063
+ path: z11.string().min(1),
1064
+ type: FileEntryTypeSchema,
1065
+ size_bytes: z11.number().int().nonnegative(),
1066
+ /** POSIX `st_mode`. */
1067
+ mode: z11.number().int().nonnegative(),
1068
+ modified_at: Timestamp.nullable()
1069
+ });
1070
+ var ComputerUpdateSchema = z11.strictObject({ name: z11.string().min(1).max(200) });
1071
+ var SkillSchema = z11.object({
517
1072
  id: idOf("skl"),
518
- object: z8.literal("skill"),
519
- name: z8.string(),
520
- description: z8.string().nullable(),
521
- latest_version: z8.number().int().positive(),
1073
+ object: z11.literal("skill"),
1074
+ name: z11.string(),
1075
+ description: z11.string().nullable(),
1076
+ latest_version: z11.number().int().positive(),
522
1077
  created_at: Timestamp
523
1078
  });
524
- var ResolvedSkillRefSchema = z8.object({
1079
+ var ResolvedSkillRefSchema = z11.object({
525
1080
  /** The reference exactly as the agent version wrote it: `slug` or `slug@N`. */
526
- ref: z8.string().min(1),
527
- slug: z8.string().min(1),
528
- version: z8.number().int().positive(),
529
- description: z8.string(),
530
- storage_key: z8.string().min(1)
531
- });
532
- var SkillVersionSchema = z8.object({
533
- version: z8.number().int().positive(),
534
- sha256: z8.string().regex(/^[0-9a-f]{64}$/),
535
- size_bytes: z8.number().int().nonnegative(),
1081
+ ref: z11.string().min(1),
1082
+ slug: z11.string().min(1),
1083
+ version: z11.number().int().positive(),
1084
+ description: z11.string(),
1085
+ storage_key: z11.string().min(1)
1086
+ });
1087
+ var SkillVersionSchema = z11.object({
1088
+ version: z11.number().int().positive(),
1089
+ sha256: z11.string().regex(/^[0-9a-f]{64}$/),
1090
+ size_bytes: z11.number().int().nonnegative(),
536
1091
  created_at: Timestamp
537
1092
  });
538
- var FileSchema = z8.object({
1093
+ var SkillCatalogEntrySchema = z11.object({
1094
+ object: z11.literal("skill_catalog_entry"),
1095
+ slug: z11.string().regex(/^[a-z0-9][a-z0-9-]*$/),
1096
+ version: z11.number().int().positive(),
1097
+ name: z11.string().min(1),
1098
+ description: z11.string(),
1099
+ tags: z11.array(z11.string().min(1)),
1100
+ content_hash: z11.string().regex(/^[0-9a-f]{64}$/),
1101
+ published_at: Timestamp
1102
+ });
1103
+ var SkillCatalogVersionSchema = SkillCatalogEntrySchema.extend({ content: z11.string() });
1104
+ var FileSchema = z11.object({
539
1105
  id: idOf("fil"),
540
- object: z8.literal("file"),
541
- name: z8.string(),
542
- content_type: z8.string(),
543
- size_bytes: z8.number().int().nonnegative(),
544
- scope: z8.enum(["session", "published"]),
1106
+ object: z11.literal("file"),
1107
+ name: z11.string(),
1108
+ content_type: z11.string(),
1109
+ size_bytes: z11.number().int().nonnegative(),
1110
+ scope: z11.enum(["session", "published"]),
545
1111
  session_id: idOf("ses").nullable(),
546
- sha256: z8.string().regex(/^[0-9a-f]{64}$/),
547
- created_at: Timestamp
1112
+ sha256: z11.string().regex(/^[0-9a-f]{64}$/),
1113
+ created_at: Timestamp,
1114
+ // The asset-library fields (`canonical-spec §36.1`), all nullable or defaulted.
1115
+ ...FileLibraryFieldsSchema.shape
548
1116
  });
549
- var ScheduleStatusSchema = z8.enum(["scheduled", "paused", "unscheduled"]);
550
- var DeploymentSchema = z8.object({
1117
+ var ScheduleStatusSchema = z11.enum(["scheduled", "paused", "unscheduled"]);
1118
+ var DeploymentSchema = z11.object({
551
1119
  id: idOf("dep"),
552
- object: z8.literal("deployment"),
1120
+ object: z11.literal("deployment"),
553
1121
  agent_id: idOf("agt"),
554
1122
  /** A pin. `null` tracks the agent's `current_version` on every fire (§12). */
555
- agent_version: z8.number().int().positive().nullable(),
556
- cron: z8.string(),
1123
+ agent_version: z11.number().int().positive().nullable(),
1124
+ cron: z11.string(),
557
1125
  /** IANA zone the `cron` is read in. "0 9 * * 1" means 09:00 *here*, across DST (ADR-0074). */
558
- timezone: z8.string(),
559
- input_template: z8.string(),
1126
+ timezone: z11.string(),
1127
+ input_template: z11.string(),
560
1128
  budget_micro_usd: MicroUsdField.nonnegative(),
561
1129
  window: WindowSchema,
562
1130
  /**
@@ -566,189 +1134,84 @@ var DeploymentSchema = z8.object({
566
1134
  * those endpoints, and a signed delivery over cleartext is not a delivery. Until the push was
567
1135
  * wired this field went nowhere, so nothing can be relying on an `http://` value (ADR-0161).
568
1136
  */
569
- on_idle: z8.url().startsWith("https://", { error: "a webhook URL must be https" }).nullable(),
1137
+ on_idle: z11.url().startsWith("https://", { error: "a webhook URL must be https" }).nullable(),
570
1138
  /**
571
1139
  * The persona every fire speaks as, or `null` to run as nobody (§22). Written as an `idn_` id
572
1140
  * **or** the persona's name, the way a session names one, and always echoed as the id: a fire at
573
1141
  * 3am must not be silenced by someone renaming the persona at noon.
574
1142
  */
575
1143
  identity: idOf("idn").nullable(),
576
- enabled: z8.boolean(),
1144
+ enabled: z11.boolean(),
577
1145
  /** Derived per deploy, never stored — the row cannot know whether a clock is watching it. */
578
1146
  schedule_status: ScheduleStatusSchema,
579
1147
  /** The next fire, or `null` when nothing will fire it (`schedule_status` says which). */
580
1148
  next_run_at: Timestamp.nullable(),
581
1149
  created_at: Timestamp
582
1150
  });
583
- var DeploymentRunSchema = z8.object({
1151
+ var DeploymentRunSchema = z11.object({
584
1152
  id: idOf("run"),
585
1153
  session_id: idOf("ses"),
586
1154
  status: SessionStatusSchema,
587
1155
  started_at: Timestamp.nullable()
588
1156
  });
589
1157
 
590
- // ../core/src/schema/org.ts
591
- import { z as z9 } from "zod";
592
- var OrganizationSchema = z9.object({
593
- id: idOf("org"),
594
- object: z9.literal("organization"),
595
- name: z9.string(),
596
- balance_micro_usd: MicroUsdField,
597
- created_at: Timestamp
598
- });
599
- var RoleSchema = z9.enum(["owner", "admin", "member"]);
600
- var MemberSchema = z9.object({
601
- id: z9.string(),
602
- object: z9.literal("member"),
603
- email: z9.email(),
604
- role: RoleSchema,
605
- invited_by: z9.string().nullable(),
606
- created_at: Timestamp
607
- });
608
- var SCOPES = [
609
- "agents:read",
610
- "agents:write",
611
- "sessions:read",
612
- "sessions:write",
613
- /**
614
- * THE MODEL DOOR AND NOTHING ELSE. `/v1/proxy/*` used to take `sessions:write`, which also opens
615
- * session create and cancel — so a credential handed to a customer's micro-VM, if it leaked, could
616
- * start sessions for the whole organization. This scope buys exactly one capability, so a sandbox
617
- * token can be minted with it alone.
618
- *
619
- * The proxy routes accept EITHER this or `sessions:write`: narrowing an already-published route
620
- * would break every key that reaches it today (§17).
621
- */
622
- "proxy:write",
623
- "computers:write",
624
- "files:read",
625
- "files:write",
626
- "billing:read",
627
- "billing:write",
628
- "webhooks:*",
629
- "audit:read",
630
- "admin"
631
- ];
632
- var ScopeSchema = z9.enum(SCOPES);
633
- var ApiKeySchema = z9.object({
634
- id: idOf("key"),
635
- object: z9.literal("api_key"),
636
- name: z9.string(),
637
- prefix: z9.string(),
638
- scopes: z9.array(ScopeSchema),
639
- created_by: PrincipalSchema,
640
- last_used_at: Timestamp.nullable(),
641
- created_at: Timestamp
642
- });
643
- var LedgerEntrySchema = z9.object({
644
- id: idOf("led"),
645
- type: z9.enum(["debit", "credit"]),
646
- amount_micro_usd: MicroUsdField,
647
- balance_after_micro_usd: MicroUsdField,
648
- actor: PrincipalSchema,
649
- session_id: idOf("ses").nullable(),
650
- agent_id: idOf("agt").nullable(),
651
- deployment_id: idOf("dep").nullable(),
652
- line_items: z9.array(z9.object({ tier: LineItemTierSchema, amount_micro_usd: MicroUsdField })),
653
- created_at: Timestamp
654
- });
655
- var TopupSchema = z9.object({
656
- id: z9.string(),
657
- object: z9.literal("topup"),
658
- amount_micro_usd: MicroUsdField.positive(),
659
- status: z9.enum(["pending", "requires_action", "succeeded", "failed"]),
660
- action_url: z9.url().nullable(),
661
- balance_after_micro_usd: MicroUsdField.nullable(),
662
- created_at: Timestamp
663
- });
664
- var WebhookEndpointSchema = z9.object({
665
- id: idOf("whk"),
666
- object: z9.literal("webhook_endpoint"),
667
- url: z9.url(),
668
- events: z9.array(z9.string()),
669
- enabled: z9.boolean(),
670
- created_at: Timestamp
671
- });
672
- var WebhookDeliverySchema = z9.object({
673
- id: idOf("whd"),
674
- event_id: idOf("evt"),
675
- event_type: z9.string(),
676
- status: z9.enum(["pending", "succeeded", "failed"]),
677
- attempts: z9.number().int().nonnegative(),
678
- response_status: z9.number().int().nullable(),
679
- response_snippet: z9.string().nullable(),
680
- delivered_at: Timestamp.nullable(),
681
- next_retry_at: Timestamp.nullable()
682
- });
683
- var AuditEntrySchema = z9.object({
684
- id: idOf("aud"),
685
- object: z9.literal("audit_entry"),
686
- actor: PrincipalSchema,
687
- action: AuditActionSchema,
688
- resource_type: z9.string(),
689
- resource_id: z9.string(),
690
- request_id: z9.string(),
691
- metadata: Metadata,
692
- created_at: Timestamp
693
- });
694
-
695
1158
  // ../core/src/schema/comms.ts
696
- import { z as z10 } from "zod";
697
- var DomainKindSchema = z10.enum(["system", "external", "purchased"]);
698
- var DomainStatusSchema = z10.enum(["pending_dns", "active", "failed"]);
699
- var DomainDnsStatusSchema = z10.enum(["pending_verification", "provisioned", "failed"]);
700
- var DomainInboundStatusSchema = z10.enum(["pending", "verified", "failed"]);
701
- var DomainAppConnectStatusSchema = z10.enum(["pending", "connected", "failed"]);
702
- var DnsRecordSchema = z10.object({
703
- type: z10.enum(["TXT", "CNAME", "MX"]),
704
- host: z10.string(),
705
- value: z10.string(),
706
- priority: z10.number().int().nullable(),
1159
+ import { z as z12 } from "zod";
1160
+ var DomainKindSchema = z12.enum(["system", "external", "purchased"]);
1161
+ var DomainStatusSchema = z12.enum(["pending_dns", "active", "failed"]);
1162
+ var DomainDnsStatusSchema = z12.enum(["pending_verification", "provisioned", "failed"]);
1163
+ var DomainInboundStatusSchema = z12.enum(["pending", "verified", "failed"]);
1164
+ var DomainAppConnectStatusSchema = z12.enum(["pending", "connected", "failed"]);
1165
+ var DnsRecordSchema = z12.object({
1166
+ type: z12.enum(["TXT", "CNAME", "MX"]),
1167
+ host: z12.string(),
1168
+ value: z12.string(),
1169
+ priority: z12.number().int().nullable(),
707
1170
  /** Vetta owns this record: `_dmarc`, DKIM, inbound MX. The agent may read it, never write it. */
708
- managed: z10.boolean()
1171
+ managed: z12.boolean()
709
1172
  });
710
- var DomainSchema = z10.object({
1173
+ var DomainSchema = z12.object({
711
1174
  id: idOf("dom"),
712
- object: z10.literal("domain"),
1175
+ object: z12.literal("domain"),
713
1176
  /** A domain belongs to the **organization**, not to one identity: two identities share one. */
714
- name: z10.string(),
1177
+ name: z12.string(),
715
1178
  kind: DomainKindSchema,
716
1179
  status: DomainStatusSchema,
717
1180
  dns_status: DomainDnsStatusSchema,
718
1181
  inbound_status: DomainInboundStatusSchema,
719
1182
  app_connect_status: DomainAppConnectStatusSchema,
720
- records: z10.array(DnsRecordSchema),
1183
+ records: z12.array(DnsRecordSchema),
721
1184
  last_verified_at: Timestamp.nullable(),
722
1185
  created_at: Timestamp
723
1186
  });
724
- var EmailInboxSchema = z10.object({
1187
+ var EmailInboxSchema = z12.object({
725
1188
  id: idOf("eml"),
726
- object: z10.literal("email_inbox"),
1189
+ object: z12.literal("email_inbox"),
727
1190
  identity_id: idOf("idn"),
728
1191
  domain_id: idOf("dom"),
729
- address: z10.string(),
730
- localpart: z10.string(),
1192
+ address: z12.string(),
1193
+ localpart: z12.string(),
731
1194
  /** Inbound routing keys off this: mail to the address wakes exactly this agent. */
732
1195
  owning_agent_id: idOf("agt"),
733
- status: z10.enum(["active", "disabled"]),
1196
+ status: z12.enum(["active", "disabled"]),
734
1197
  created_at: Timestamp
735
1198
  });
736
- var PhoneTierSchema = z10.enum(["sole_prop", "standard"]);
737
- var BrandStatusSchema = z10.enum(["none", "pending_verification", "verified", "rejected"]);
738
- var CampaignStatusSchema = z10.enum(["none", "pending", "approved", "rejected"]);
739
- var PhoneNumberSchema = z10.object({
1199
+ var PhoneTierSchema = z12.enum(["sole_prop", "standard"]);
1200
+ var BrandStatusSchema = z12.enum(["none", "pending_verification", "verified", "rejected"]);
1201
+ var CampaignStatusSchema = z12.enum(["none", "pending", "approved", "rejected"]);
1202
+ var PhoneNumberSchema = z12.object({
740
1203
  id: idOf("pho"),
741
- object: z10.literal("phone_number"),
1204
+ object: z12.literal("phone_number"),
742
1205
  identity_id: idOf("idn"),
743
- e164: z10.string(),
744
- country: z10.string(),
745
- capabilities: z10.object({
1206
+ e164: z12.string(),
1207
+ country: z12.string(),
1208
+ capabilities: z12.object({
746
1209
  /** Live the moment the number is bought — inbound never waits on carrier registration. */
747
- sms_inbound: z10.boolean(),
1210
+ sms_inbound: z12.boolean(),
748
1211
  /** True **iff** `campaign_status === "approved"`. Nothing else may set it. */
749
- sms_outbound: z10.boolean(),
1212
+ sms_outbound: z12.boolean(),
750
1213
  /** Voice is not a P2 capability; the field exists so its absence is explicit. */
751
- voice: z10.literal(false)
1214
+ voice: z12.literal(false)
752
1215
  }),
753
1216
  tier: PhoneTierSchema,
754
1217
  brand_status: BrandStatusSchema,
@@ -756,80 +1219,96 @@ var PhoneNumberSchema = z10.object({
756
1219
  provisioned_at: Timestamp.nullable(),
757
1220
  created_at: Timestamp
758
1221
  });
759
- var InboundMessageSchema = z10.object({
1222
+ var InboundMessageSchema = z12.object({
760
1223
  id: idOf("inb"),
761
- object: z10.literal("inbound_message"),
762
- channel: z10.enum(["email", "sms"]),
1224
+ object: z12.literal("inbound_message"),
1225
+ channel: z12.enum(["email", "sms"]),
763
1226
  identity_id: idOf("idn"),
764
1227
  /** The `eml_`/`pho_` endpoint it arrived at — the persona is decided by *where*, not by *who*. */
765
- endpoint_id: z10.string(),
766
- from: z10.string(),
767
- to: z10.string(),
768
- subject: z10.string().nullable(),
769
- text: z10.string(),
1228
+ endpoint_id: z12.string(),
1229
+ from: z12.string(),
1230
+ to: z12.string(),
1231
+ subject: z12.string().nullable(),
1232
+ text: z12.string(),
770
1233
  /** SPF/DKIM/DMARC for mail, carrier origin for SMS. Kept for the audit of why it was accepted. */
771
- auth: z10.record(z10.string(), z10.string()),
1234
+ auth: z12.record(z12.string(), z12.string()),
772
1235
  /** The session this message woke. Null until the wake seam lands (see ADR-0120). */
773
1236
  session_id: idOf("ses").nullable(),
774
1237
  received_at: Timestamp
775
1238
  });
776
1239
 
777
1240
  // ../core/src/schema/app.ts
778
- import { z as z11 } from "zod";
779
- var AppTypeSchema = z11.enum(["frontend_only", "fullstack"]);
780
- var AppStatusSchema = z11.enum(["provisioning", "active", "error"]);
781
- var AppMcpPathSchema = z11.string().regex(/^\/\S*$/, "must be an absolute path beginning with /").max(128);
782
- var AppSchema = z11.object({
1241
+ import { z as z13 } from "zod";
1242
+ var AppTypeSchema = z13.enum(["frontend_only", "fullstack"]);
1243
+ var AppStatusSchema = z13.enum(["provisioning", "active", "error"]);
1244
+ var AppMcpPathSchema = z13.string().regex(/^\/\S*$/, "must be an absolute path beginning with /").max(128);
1245
+ var AppSchema = z13.object({
783
1246
  id: idOf("app"),
784
- object: z11.literal("app"),
785
- name: z11.string(),
786
- description: z11.string().nullable(),
1247
+ object: z13.literal("app"),
1248
+ name: z13.string(),
1249
+ description: z13.string().nullable(),
787
1250
  type: AppTypeSchema,
788
1251
  /**
789
1252
  * The customer's own project name that made this app, or `null` for one made outside a project
790
1253
  * (ADR-0375). Customer-visible, unlike the provider handles: it is the word the customer typed,
791
1254
  * and the refusal that guards a name quotes it.
792
1255
  */
793
- project: z11.string().nullable(),
1256
+ project: z13.string().nullable(),
794
1257
  status: AppStatusSchema,
795
1258
  /** The address the app answers on — platform hostname until a custom domain connects. */
796
- url: z11.string().nullable(),
1259
+ url: z13.string().nullable(),
797
1260
  /** White-label words when `status` is `error`; never the provider's. */
798
- error: z11.string().nullable(),
1261
+ error: z13.string().nullable(),
799
1262
  /** Where the app serves MCP, or `null`. The bearer token for it is a server column. */
800
1263
  mcp: AppMcpPathSchema.nullable(),
1264
+ /**
1265
+ * Is this a gated blueprint dashboard — one the Studio opens through `/apps/{id}/open` rather
1266
+ * than linking its `url` (`§29.1`)? True when the platform holds the app's `DASHBOARD_TOKEN`
1267
+ * or a secret of that name exists on the app; derived on read, never stored.
1268
+ */
1269
+ dashboard: z13.boolean(),
801
1270
  created_at: Timestamp
802
1271
  });
803
- var AppDeploymentStatusSchema = z11.enum(["queued", "building", "ready", "error"]);
804
- var AppDeploymentSchema = z11.object({
1272
+ var AppDeploymentStatusSchema = z13.enum(["queued", "building", "ready", "error"]);
1273
+ var AppDeploymentSchema = z13.object({
805
1274
  id: idOf("apd"),
806
- object: z11.literal("app_deployment"),
1275
+ object: z13.literal("app_deployment"),
807
1276
  app_id: idOf("app"),
808
1277
  status: AppDeploymentStatusSchema,
809
- url: z11.string().nullable(),
810
- error: z11.string().nullable(),
1278
+ url: z13.string().nullable(),
1279
+ error: z13.string().nullable(),
811
1280
  /** The client's opaque digest of the files, echoed verbatim so it can skip an unchanged deploy. */
812
- content_hash: z11.string().nullable(),
1281
+ content_hash: z13.string().nullable(),
813
1282
  created_at: Timestamp
814
1283
  });
815
- var AppSecretSchema = z11.object({
816
- object: z11.literal("app_secret"),
817
- name: z11.string(),
1284
+ var AppDeploymentFilesSchema = z13.object({
1285
+ object: z13.literal("app_deployment_files"),
1286
+ deployment_id: idOf("apd"),
1287
+ files: z13.record(z13.string().min(1), z13.string())
1288
+ });
1289
+ var AppSecretSchema = z13.object({
1290
+ object: z13.literal("app_secret"),
1291
+ name: z13.string(),
818
1292
  /** The client's opaque digest of the value, echoed verbatim so it can skip an unchanged write. */
819
- value_hash: z11.string().nullable(),
1293
+ value_hash: z13.string().nullable(),
820
1294
  created_at: Timestamp
821
1295
  });
822
- var AppEntrySchema = z11.object({
823
- object: z11.literal("app_entry"),
824
- url: z11.string(),
825
- ticket: z11.string()
1296
+ var AppEntrySchema = z13.object({
1297
+ object: z13.literal("app_entry"),
1298
+ url: z13.string(),
1299
+ ticket: z13.string()
1300
+ });
1301
+ var AppAccessSchema = z13.object({
1302
+ object: z13.literal("app_access"),
1303
+ url: z13.string().nullable(),
1304
+ password: z13.string().nullable()
826
1305
  });
827
- var AppDomainStatusSchema = z11.enum(["pending", "connected", "failed"]);
828
- var AppDomainSchema = z11.object({
829
- object: z11.literal("app_domain"),
1306
+ var AppDomainStatusSchema = z13.enum(["pending", "connected", "failed"]);
1307
+ var AppDomainSchema = z13.object({
1308
+ object: z13.literal("app_domain"),
830
1309
  app_id: idOf("app"),
831
1310
  domain_id: idOf("dom"),
832
- domain: z11.string(),
1311
+ domain: z13.string(),
833
1312
  status: AppDomainStatusSchema,
834
1313
  created_at: Timestamp
835
1314
  });
@@ -837,52 +1316,58 @@ var ENV_NAME = /^[A-Z_][A-Z0-9_]*$/;
837
1316
  var APP_NAME = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
838
1317
 
839
1318
  // ../core/src/schema/install.ts
840
- import { z as z12 } from "zod";
841
- var BlueprintInstallSourceSchema = z12.enum(["studio", "cli"]);
842
- var ResourceActionSchema = z12.enum(["created", "updated", "unchanged", "deleted", "refused"]);
843
- var ResourceReportSchema = z12.object({
844
- name: z12.string().min(1),
1319
+ import { z as z14 } from "zod";
1320
+ var BlueprintInstallSourceSchema = z14.enum(["studio", "cli"]);
1321
+ var BlueprintInstallStatusSchema = z14.enum(["pending", "applied", "failed"]);
1322
+ var ResourceActionSchema = z14.enum(["created", "updated", "unchanged", "deleted", "refused"]);
1323
+ var ResourceReportSchema = z14.object({
1324
+ name: z14.string().min(1),
845
1325
  action: ResourceActionSchema,
846
1326
  /** The `agt_`/`app_`/`ses_`… the line resolved to. Absent on a refusal that never got an id. */
847
- id: z12.string().optional(),
1327
+ id: z14.string().optional(),
848
1328
  /** An app's hostname once its build went live; `null` while it has none. Apps only. */
849
- url: z12.string().nullable().optional(),
850
- reason: z12.string().optional()
851
- });
852
- var BlueprintReportSchema = z12.object({
853
- skills: z12.array(ResourceReportSchema),
854
- identities: z12.array(ResourceReportSchema),
855
- vaults: z12.array(ResourceReportSchema),
856
- apps: z12.array(ResourceReportSchema),
857
- agents: z12.array(ResourceReportSchema),
858
- schedules: z12.array(ResourceReportSchema),
859
- intake: z12.array(ResourceReportSchema),
860
- skipped: z12.array(z12.string())
861
- });
862
- var BlueprintInstallSchema = z12.object({
1329
+ url: z14.string().nullable().optional(),
1330
+ reason: z14.string().optional()
1331
+ });
1332
+ var BlueprintReportSchema = z14.object({
1333
+ skills: z14.array(ResourceReportSchema),
1334
+ identities: z14.array(ResourceReportSchema),
1335
+ vaults: z14.array(ResourceReportSchema),
1336
+ apps: z14.array(ResourceReportSchema),
1337
+ agents: z14.array(ResourceReportSchema),
1338
+ schedules: z14.array(ResourceReportSchema),
1339
+ intake: z14.array(ResourceReportSchema),
1340
+ skipped: z14.array(z14.string()),
1341
+ deselected: z14.array(z14.string()).default([])
1342
+ });
1343
+ var BlueprintSelectionSchema = z14.object({
1344
+ agents: z14.array(z14.string().min(1)),
1345
+ apps: z14.array(z14.string().min(1))
1346
+ });
1347
+ var BlueprintInstallSchema = z14.object({
863
1348
  id: idOf("bpi"),
864
- object: z12.literal("blueprint_install"),
1349
+ object: z14.literal("blueprint_install"),
865
1350
  /** The customer's own `defineProject({ name })` — the same word `app.project` stamps (§29.2). */
866
- project: z12.string().min(1),
1351
+ project: z14.string().min(1),
867
1352
  /** The pair the declaration resolved to; `null` for a declaration that names no blueprint. */
868
- blueprint: z12.string().nullable(),
869
- template: z12.string().nullable(),
1353
+ blueprint: z14.string().nullable(),
1354
+ template: z14.string().nullable(),
870
1355
  /**
871
1356
  * Applies, not versions of anything: 1 on the first apply, +1 on each one after, whichever writer
872
1357
  * made it. It is not an agent version and it never rolls back.
873
1358
  */
874
- revision: z12.number().int().positive(),
1359
+ revision: z14.number().int().positive(),
875
1360
  /**
876
1361
  * The blueprint artifact version this came from, or `null` when it was applied from a working
877
1362
  * tree — an operator editing a clone has no version to name.
878
1363
  */
879
- artifact_version: z12.string().nullable(),
1364
+ artifact_version: z14.string().nullable(),
880
1365
  /**
881
1366
  * The setup answers this project was configured with (§7.1 fields, keyed by their `key`), so a
882
1367
  * re-apply does not re-ask and both writers configure from the same answers. Configuration only:
883
1368
  * a secret is a vault credential (§23) or an app secret (§29.2), and neither is ever an answer.
884
1369
  */
885
- answers: z12.record(z12.string(), z12.unknown()),
1370
+ answers: z14.record(z14.string(), z14.unknown()),
886
1371
  /**
887
1372
  * NO `declaration` ON THE WIRE, and that is the reconciliation of §29.6 with §31.1.
888
1373
  *
@@ -909,95 +1394,147 @@ var BlueprintInstallSchema = z12.object({
909
1394
  * a person needs after the screen that showed it is gone.
910
1395
  */
911
1396
  report: BlueprintReportSchema.nullable(),
1397
+ status: BlueprintInstallStatusSchema,
1398
+ /** Why a `failed` run threw, in one sentence; `null` on every other row. Never a stack or a key. */
1399
+ failure: z14.string().nullable(),
1400
+ /**
1401
+ * The `selection` the apply carried, or `null` — every row written before §31.2 and every apply
1402
+ * that omitted it, both meaning "everything". Defaulted so those rows parse unchanged.
1403
+ */
1404
+ selection: BlueprintSelectionSchema.nullable().default(null),
1405
+ /** When the answers were last edited through `PATCH …/installs/{id}` (§31.8); `null` if never. */
1406
+ context_updated_at: Timestamp.nullable().default(null),
912
1407
  applied_at: Timestamp,
913
1408
  created_at: Timestamp
914
1409
  });
1410
+ var ProjectContextAnswerSchema = z14.object({
1411
+ key: z14.string().min(1),
1412
+ label: z14.string(),
1413
+ value: z14.union([z14.string(), z14.array(z14.string())])
1414
+ });
1415
+ var ProjectContextSchema = z14.object({
1416
+ object: z14.literal("project_context"),
1417
+ project: z14.string().min(1),
1418
+ blueprint: z14.string().nullable(),
1419
+ template: z14.string().nullable(),
1420
+ artifact_version: z14.string().nullable(),
1421
+ answers: z14.array(ProjectContextAnswerSchema),
1422
+ apps: z14.array(z14.object({ name: z14.string().min(1), url: z14.string().nullable() })),
1423
+ agents: z14.array(z14.object({ name: z14.string().min(1), role: z14.string().nullable() })),
1424
+ /** `context_updated_at` once the answers have been edited, `applied_at` until then. */
1425
+ updated_at: Timestamp
1426
+ });
915
1427
 
916
1428
  // ../core/src/schema/blueprint.ts
917
- import { z as z13 } from "zod";
918
- var BlueprintScheduleSchema = z13.object({
919
- agent: z13.string().min(1),
920
- cron: z13.string().min(1),
921
- timezone: z13.string().nullable(),
1429
+ import { z as z15 } from "zod";
1430
+ var BlueprintScheduleSchema = z15.object({
1431
+ agent: z15.string().min(1),
1432
+ cron: z15.string().min(1),
1433
+ timezone: z15.string().nullable(),
922
1434
  /** The message each fire opens its session with — the sentence worth printing. */
923
- input: z13.string().min(1),
1435
+ input: z15.string().min(1),
924
1436
  budget_micro_usd: MicroUsdField.nonnegative()
925
1437
  });
926
- var BlueprintCrewSchema = z13.object({
927
- name: z13.string().min(1),
928
- description: z13.string().nullable(),
929
- model: z13.string().min(1),
930
- briefed: z13.boolean()
931
- });
932
- var BlueprintAppSchema = z13.object({
933
- name: z13.string().min(1),
1438
+ var BlueprintBudgetSchema = BlueprintScheduleSchema.pick({ agent: true, cron: true, budget_micro_usd: true });
1439
+ var BlueprintCrewSchema = z15.object({
1440
+ name: z15.string().min(1),
1441
+ description: z15.string().nullable(),
1442
+ model: z15.string().min(1),
1443
+ briefed: z15.boolean(),
1444
+ intake: z15.string().nullable().optional(),
1445
+ /** What the first session may spend: the intake's own cap, else the agent's (§31.4). Set iff `briefed`. */
1446
+ intake_micro_usd: MicroUsdField.nonnegative().optional(),
1447
+ role: z15.string().min(1).optional(),
1448
+ skills: z15.array(SkillRefSchema).optional(),
1449
+ tools: z15.array(z15.string().min(1)).optional(),
1450
+ required: z15.boolean().optional()
1451
+ });
1452
+ var BlueprintAppSchema = z15.object({
1453
+ name: z15.string().min(1),
934
1454
  type: AppTypeSchema,
935
- description: z13.string().nullable()
936
- });
937
- var ContentHashSchema = z13.string().regex(/^[0-9a-f]{64}$/, "a lowercase sha-256 hex digest");
938
- var BlueprintTreeSchema = z13.object({
1455
+ description: z15.string().nullable(),
1456
+ mcp: z15.string().nullable().optional(),
1457
+ tools: z15.array(z15.string().min(1)).optional(),
1458
+ /** Whether an install may deselect it (§31.2). Optional: the rollout argument above. */
1459
+ required: z15.boolean().optional()
1460
+ });
1461
+ var ContentHashSchema = z15.string().regex(/^[0-9a-f]{64}$/, "a lowercase sha-256 hex digest");
1462
+ var BlueprintTreeSchema = z15.object({
939
1463
  /** `apps[].name` in the declaration — the same name §31.5 prints and the install ensures by. */
940
- app: z13.string().min(1),
1464
+ app: z15.string().min(1),
941
1465
  content_hash: ContentHashSchema
942
1466
  });
943
- var BlueprintArtifactSchema = z13.object({
1467
+ var BlueprintArtifactSchema = z15.object({
944
1468
  id: idOf("bpa"),
945
- object: z13.literal("blueprint_artifact"),
1469
+ object: z15.literal("blueprint_artifact"),
946
1470
  /** The machine — its repository, screens and `/api/*`. */
947
- blueprint: z13.string().min(1),
1471
+ blueprint: z15.string().min(1),
948
1472
  /** Which of that blueprint's templates provides the crew. */
949
- template: z13.string().min(1),
1473
+ template: z15.string().min(1),
950
1474
  /** The blueprint repository's own released version. Stored, never parsed (ADR-0401). */
951
- version: z13.string().min(1),
1475
+ version: z15.string().min(1),
952
1476
  /**
953
1477
  * The built trees, one per deployable app, ordered as the declaration orders its apps. Never
954
1478
  * empty: an artifact that built nothing is not an artifact, and the column behind it says so
955
1479
  * (migration 0040) exactly as the single `content_hash` it replaces was `not null`.
956
1480
  */
957
- trees: z13.array(BlueprintTreeSchema).min(1),
1481
+ trees: z15.array(BlueprintTreeSchema).min(1),
958
1482
  /**
959
1483
  * The setup questions, asked by the studio **before** provisioning. `QuestionField` (§7.1) is
960
1484
  * reused rather than restated: a setup question and a question a parked agent asks are the same
961
1485
  * object at different times, so one component renders both.
962
1486
  */
963
- questions: z13.array(QuestionFieldSchema),
964
- schedules: z13.array(BlueprintScheduleSchema),
1487
+ questions: z15.array(QuestionFieldSchema),
1488
+ schedules: z15.array(BlueprintScheduleSchema),
965
1489
  /** Who the crew is, and what it deploys — §31.5. Derived from `declaration`, never stored twice. */
966
- agents: z13.array(BlueprintCrewSchema),
967
- apps: z13.array(BlueprintAppSchema),
1490
+ agents: z15.array(BlueprintCrewSchema),
1491
+ apps: z15.array(BlueprintAppSchema),
1492
+ /**
1493
+ * The agents a template creates per client the operator signs — printed, never provisioned by an
1494
+ * apply (§31.1). Absent on rows published before WS12 v2, hence optional.
1495
+ */
1496
+ crew_per_client: z15.array(BlueprintCrewSchema).optional(),
1497
+ /**
1498
+ * The creator template a row was published from (§42.8), `null` on every first-party row.
1499
+ * `creator_name` is the public display name the landing page credits. The API sends all three
1500
+ * on every row; they are optional here only so an artifact typed before §42 still reads.
1501
+ */
1502
+ creator_template_id: idOf("ctp").nullish(),
1503
+ creator_template_version: z15.number().int().positive().nullish(),
1504
+ creator_name: z15.string().min(1).nullish(),
968
1505
  published_at: Timestamp
969
1506
  });
970
1507
 
971
1508
  // ../core/src/schema/vault.ts
972
- import { z as z14 } from "zod";
973
- var VaultCredentialKindSchema = z14.enum(["env_var", "static_bearer", "mcp_oauth"]);
974
- var VaultSchema = z14.object({
1509
+ import { z as z16 } from "zod";
1510
+ var VaultCredentialKindSchema = z16.enum(["env_var", "static_bearer", "mcp_oauth"]);
1511
+ var VaultSchema = z16.object({
975
1512
  id: idOf("vlt"),
976
- object: z14.literal("vault"),
1513
+ object: z16.literal("vault"),
977
1514
  identity_id: idOf("idn").nullable(),
978
- display_name: z14.string().min(1).max(128),
979
- credential_count: z14.number().int().nonnegative(),
1515
+ display_name: z16.string().min(1).max(128),
1516
+ credential_count: z16.number().int().nonnegative(),
980
1517
  created_at: Timestamp
981
1518
  });
982
- var VaultCredentialSchema = z14.object({
1519
+ var VaultCredentialSchema = z16.object({
983
1520
  id: idOf("vcr"),
984
- object: z14.literal("vault_credential"),
1521
+ object: z16.literal("vault_credential"),
985
1522
  vault_id: idOf("vlt"),
986
1523
  kind: VaultCredentialKindSchema,
987
1524
  /** For `env_var`, the environment variable name. For the MCP kinds, a display label. */
988
- key: z14.string().min(1).max(128),
1525
+ key: z16.string().min(1).max(128),
989
1526
  /** The exact destination host an `env_var` credential is bound to. Never a wildcard. */
990
- host: z14.string().nullable(),
1527
+ host: z16.string().nullable(),
991
1528
  /** The exact MCP server URL an `mcp_*` token is matched to. */
992
- mcp_server_url: z14.url().nullable(),
993
- placeholder_ref: z14.string(),
1529
+ mcp_server_url: z16.url().nullable(),
1530
+ placeholder_ref: z16.string(),
994
1531
  expires_at: Timestamp.nullable(),
995
1532
  last_injected_at: Timestamp.nullable(),
996
1533
  created_at: Timestamp
997
1534
  });
998
1535
 
999
1536
  // ../core/src/schema/social.ts
1000
- import { z as z15 } from "zod";
1537
+ import { z as z17 } from "zod";
1001
1538
  var SOCIAL_PLATFORMS = [
1002
1539
  "bluesky",
1003
1540
  "facebook",
@@ -1019,19 +1556,27 @@ var SOCIAL_TEXT_PLATFORMS = [
1019
1556
  "threads",
1020
1557
  "x"
1021
1558
  ];
1022
- var SocialPlatformSchema = z15.enum(SOCIAL_PLATFORMS);
1023
- var SocialTextPlatformSchema = z15.enum(SOCIAL_TEXT_PLATFORMS);
1024
- var SocialAccountSchema = z15.object({
1025
- id: z15.string().min(1),
1026
- object: z15.literal("social_account"),
1559
+ var SOCIAL_MEDIA_PLATFORMS = ["instagram", "tiktok", "youtube"];
1560
+ var SOCIAL_VISIBILITIES = ["public", "unlisted", "private"];
1561
+ var SOCIAL_VISIBILITY_PLATFORMS = ["mastodon", "youtube"];
1562
+ var SocialVisibilitySchema = z17.enum(SOCIAL_VISIBILITIES);
1563
+ var SOCIAL_VISIBILITY_RULE = `visibility is ${SOCIAL_VISIBILITIES.join(", ")} and only ${SOCIAL_VISIBILITY_PLATFORMS.join(" and ")} accept it; omit it and the post publishes at the account's own default, which on a video channel is normally public`;
1564
+ var SocialPlatformSchema = z17.enum(SOCIAL_PLATFORMS);
1565
+ var SocialTextPlatformSchema = z17.enum(SOCIAL_TEXT_PLATFORMS);
1566
+ var isText = (platform) => SOCIAL_TEXT_PLATFORMS.includes(platform);
1567
+ var isMedia = (platform) => SOCIAL_MEDIA_PLATFORMS.includes(platform);
1568
+ var SOCIAL_PLATFORM_RULE = `${SOCIAL_TEXT_PLATFORMS.join(", ")} take a text-only post; ${SOCIAL_MEDIA_PLATFORMS.join(", ")} may be named only when the post carries media_urls or file_ids; ${SOCIAL_PLATFORMS.filter((platform) => !isText(platform) && !isMedia(platform)).join(", ")} cannot be posted to`;
1569
+ var SocialAccountSchema = z17.object({
1570
+ id: z17.string().min(1),
1571
+ object: z17.literal("social_account"),
1027
1572
  identity_id: idOf("idn"),
1028
1573
  platform: SocialPlatformSchema,
1029
- username: z15.string().nullable(),
1030
- display_name: z15.string().nullable(),
1031
- avatar_url: z15.url().nullable(),
1574
+ username: z17.string().nullable(),
1575
+ display_name: z17.string().nullable(),
1576
+ avatar_url: z17.url().nullable(),
1032
1577
  connected_at: Timestamp.nullable()
1033
1578
  });
1034
- var SocialPostStatusSchema = z15.enum([
1579
+ var SocialPostStatusSchema = z17.enum([
1035
1580
  "draft",
1036
1581
  "scheduled",
1037
1582
  "processing",
@@ -1039,51 +1584,953 @@ var SocialPostStatusSchema = z15.enum([
1039
1584
  "failed",
1040
1585
  "deleted"
1041
1586
  ]);
1042
- var SocialPostSchema = z15.object({
1043
- id: z15.string().min(1),
1044
- object: z15.literal("social_post"),
1587
+ var SocialPostSchema = z17.object({
1588
+ id: z17.string().min(1),
1589
+ object: z17.literal("social_post"),
1045
1590
  identity_id: idOf("idn"),
1046
- title: z15.string(),
1047
- platforms: z15.array(SocialTextPlatformSchema),
1591
+ title: z17.string(),
1592
+ platforms: z17.array(SocialPlatformSchema),
1048
1593
  status: SocialPostStatusSchema,
1049
1594
  scheduled_at: Timestamp.nullable(),
1050
1595
  posted_at: Timestamp.nullable(),
1051
- error: z15.string().nullable()
1596
+ error: z17.string().nullable()
1052
1597
  });
1053
- var SocialUploadSchema = z15.object({
1054
- id: z15.string().min(1),
1055
- object: z15.literal("social_upload"),
1598
+ var SocialUploadSchema = z17.object({
1599
+ id: z17.string().min(1),
1600
+ object: z17.literal("social_upload"),
1056
1601
  identity_id: idOf("idn"),
1057
- type: z15.enum(["image", "video", "document"]),
1058
- url: z15.url().nullable()
1602
+ type: z17.enum(["image", "video", "document"]),
1603
+ url: z17.url().nullable()
1059
1604
  });
1060
- var SocialAnalyticsSchema = z15.object({
1061
- object: z15.literal("social_analytics"),
1605
+ var SocialAnalyticsSchema = z17.object({
1606
+ object: z17.literal("social_analytics"),
1062
1607
  identity_id: idOf("idn"),
1063
- resource_id: z15.string().min(1),
1608
+ resource_id: z17.string().min(1),
1064
1609
  platform: SocialPlatformSchema.nullable(),
1065
- data: z15.unknown()
1610
+ data: z17.unknown()
1066
1611
  });
1067
- var SocialCommentSchema = z15.object({
1068
- id: z15.string().min(1),
1069
- object: z15.literal("social_comment"),
1612
+ var SocialCommentSchema = z17.object({
1613
+ id: z17.string().min(1),
1614
+ object: z17.literal("social_comment"),
1070
1615
  identity_id: idOf("idn"),
1071
- post_id: z15.string().min(1),
1072
- platforms: z15.array(SocialPlatformSchema),
1616
+ post_id: z17.string().min(1),
1617
+ platforms: z17.array(SocialPlatformSchema),
1073
1618
  status: SocialPostStatusSchema,
1074
1619
  posted_at: Timestamp.nullable(),
1075
- data: z15.unknown()
1620
+ data: z17.unknown()
1621
+ });
1622
+
1623
+ // ../core/src/schema/handoff.ts
1624
+ import { z as z18 } from "zod";
1625
+ var HandoffRequestSchema = z18.object({
1626
+ agent: z18.string().min(1),
1627
+ message: z18.string().min(1),
1628
+ handoff_key: z18.string().min(1).max(128).optional(),
1629
+ budget_micro_usd: z18.number().int().positive().optional()
1630
+ });
1631
+ var HandoffSchema = z18.object({
1632
+ object: z18.literal("handoff"),
1633
+ session_id: idOf("ses"),
1634
+ status: z18.enum(["queued", "already_running"]),
1635
+ handoff_key: z18.string().nullable()
1636
+ });
1637
+ var SessionHandoffSchema = z18.object({
1638
+ from_session: idOf("ses"),
1639
+ from_agent: idOf("agt"),
1640
+ key: z18.string().nullable(),
1641
+ depth: z18.number().int().positive()
1642
+ });
1643
+
1644
+ // ../core/src/schema/media.ts
1645
+ import { z as z19 } from "zod";
1646
+ var MEDIA_JOB_KINDS = ["video", "clip", "transcription"];
1647
+ var MediaJobKindSchema = z19.enum(MEDIA_JOB_KINDS);
1648
+ var MediaJobStatusSchema = z19.enum(["queued", "processing", "completed", "failed"]);
1649
+ var PublicUrl = z19.string().url().startsWith("http");
1650
+ var Score = z19.number().int().min(0).max(100);
1651
+ var Seconds = z19.number().nonnegative();
1652
+ var VideoResultSchema = z19.object({
1653
+ files: z19.array(
1654
+ z19.object({
1655
+ file_id: idOf("fil"),
1656
+ content_type: z19.string(),
1657
+ width: z19.number().int().positive().nullable(),
1658
+ height: z19.number().int().positive().nullable(),
1659
+ duration_seconds: Seconds.nullable()
1660
+ })
1661
+ )
1662
+ });
1663
+ var ViralityScoreSchema = z19.object({
1664
+ total: Score,
1665
+ shareability: Score,
1666
+ hook_strength: Score,
1667
+ story_quality: Score,
1668
+ emotional_impact: Score
1669
+ });
1670
+ var ClipResultSchema = z19.object({
1671
+ clips: z19.array(
1672
+ z19.object({
1673
+ file_id: idOf("fil"),
1674
+ title: z19.string(),
1675
+ start_seconds: Seconds,
1676
+ end_seconds: Seconds,
1677
+ duration_seconds: Seconds,
1678
+ virality: ViralityScoreSchema
1679
+ })
1680
+ )
1681
+ });
1682
+ var TranscriptSchema = z19.object({
1683
+ text: z19.string(),
1684
+ language: z19.string().nullable(),
1685
+ duration_seconds: Seconds.nullable(),
1686
+ segments: z19.array(z19.object({ start_seconds: Seconds, end_seconds: Seconds, text: z19.string() }))
1687
+ });
1688
+ var MediaJobResultSchema = z19.union([VideoResultSchema, ClipResultSchema, TranscriptSchema]);
1689
+ var MediaJobSchema = z19.object({
1690
+ id: idOf("med"),
1691
+ object: z19.literal("media_job"),
1692
+ kind: MediaJobKindSchema,
1693
+ status: MediaJobStatusSchema,
1694
+ model: z19.string().nullable(),
1695
+ input: z19.record(z19.string(), z19.unknown()),
1696
+ result: MediaJobResultSchema.nullable(),
1697
+ error: z19.object({ code: z19.string(), message: z19.string() }).nullable(),
1698
+ cost_micro_usd: MicroUsdField.nullable(),
1699
+ session_id: idOf("ses").nullable(),
1700
+ created_at: Timestamp,
1701
+ completed_at: Timestamp.nullable()
1702
+ });
1703
+ var ImageRequestSchema = z19.object({
1704
+ model: z19.string().min(1).optional(),
1705
+ prompt: z19.string().min(1),
1706
+ image_urls: z19.array(PublicUrl).max(4).optional(),
1707
+ aspect_ratio: z19.string().optional(),
1708
+ seed: z19.number().int().optional(),
1709
+ n: z19.number().int().min(1).max(4).default(1)
1710
+ });
1711
+ var VideoRequestSchema = z19.object({
1712
+ model: z19.string().min(1),
1713
+ prompt: z19.string().min(1),
1714
+ image_urls: z19.array(PublicUrl).max(1).optional(),
1715
+ aspect_ratio: z19.string().optional(),
1716
+ seed: z19.number().int().optional(),
1717
+ seconds: z19.number().int().min(1).max(60).optional()
1718
+ });
1719
+ var ImageGenerationSchema = z19.object({
1720
+ object: z19.literal("image_generation"),
1721
+ model: z19.string(),
1722
+ files: z19.array(FileSchema),
1723
+ cost_micro_usd: MicroUsdField
1724
+ });
1725
+ var ClipLength = z19.number().int().min(5).max(180);
1726
+ var ClipRequestSchema = z19.object({
1727
+ video_url: PublicUrl,
1728
+ aspect_ratio: z19.enum(["9:16", "16:9"]).default("9:16"),
1729
+ remove_silence: z19.boolean().default(false),
1730
+ caption_preset: z19.enum(["none", "clean", "bold", "karaoke"]).default("none"),
1731
+ language: z19.string().min(2).optional(),
1732
+ min_seconds: ClipLength.default(15),
1733
+ max_seconds: ClipLength.default(60),
1734
+ title: z19.string().min(1).optional()
1735
+ });
1736
+ var AudioFormatSchema = z19.enum(["mp3", "wav", "ogg"]);
1737
+ var TranscriptionRequestSchema = z19.object({
1738
+ model: z19.string().min(1).optional(),
1739
+ file_id: idOf("fil").optional(),
1740
+ url: PublicUrl.optional(),
1741
+ language: z19.string().min(2).optional(),
1742
+ timestamps: z19.boolean().default(true)
1743
+ }).refine((request) => request.file_id === void 0 !== (request.url === void 0), {
1744
+ error: "exactly one of file_id or url",
1745
+ path: ["file_id"]
1746
+ });
1747
+ var SpeechRequestSchema = z19.object({
1748
+ model: z19.string().min(1).optional(),
1749
+ text: z19.string().min(1).max(4096),
1750
+ voice: z19.string().min(1).optional(),
1751
+ format: AudioFormatSchema.default("mp3"),
1752
+ speed: z19.number().min(0.5).max(2).optional()
1753
+ });
1754
+ var SpeechToSpeechRequestSchema = z19.object({
1755
+ model: z19.string().min(1).optional(),
1756
+ file_id: idOf("fil"),
1757
+ voice: z19.string().min(1).optional(),
1758
+ format: AudioFormatSchema.default("mp3")
1759
+ });
1760
+ var SpeechGenerationSchema = z19.object({
1761
+ object: z19.literal("speech_generation"),
1762
+ model: z19.string(),
1763
+ file: FileSchema,
1764
+ duration_seconds: Seconds.nullable(),
1765
+ cost_micro_usd: MicroUsdField
1766
+ });
1767
+
1768
+ // ../core/src/schema/app-data.ts
1769
+ import { z as z20 } from "zod";
1770
+ var DbTableSchema = z20.object({
1771
+ object: z20.literal("db_table"),
1772
+ name: z20.string(),
1773
+ schema: z20.string().default("public"),
1774
+ rows: z20.number().int().nonnegative().nullable(),
1775
+ size_bytes: z20.number().int().nonnegative().nullable()
1776
+ });
1777
+ var DbMigrationSchema = z20.object({
1778
+ id: idOf("dbm"),
1779
+ object: z20.literal("db_migration"),
1780
+ name: z20.string(),
1781
+ sha256: z20.string().regex(/^[0-9a-f]{64}$/),
1782
+ applied_at: Timestamp,
1783
+ statements: z20.number().int().nonnegative()
1784
+ });
1785
+ var ApplyDbMigrationSchema = z20.object({
1786
+ name: z20.string().min(1).max(128).regex(/^[a-z0-9][a-z0-9_-]*$/),
1787
+ sql: z20.string().min(1)
1788
+ });
1789
+ var DB_REST_METHODS = ["GET", "POST", "PATCH", "PUT", "DELETE", "HEAD"];
1790
+ var DbRestMethodSchema = z20.enum(DB_REST_METHODS);
1791
+ var BUCKET_NAME = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
1792
+ var BucketSchema = z20.object({
1793
+ object: z20.literal("bucket"),
1794
+ name: z20.string().regex(BUCKET_NAME),
1795
+ public: z20.boolean(),
1796
+ created_at: Timestamp
1797
+ });
1798
+ var CreateBucketSchema = z20.object({
1799
+ name: z20.string().regex(BUCKET_NAME, { error: "3\u201363 lowercase letters, digits and hyphens" }),
1800
+ public: z20.boolean().default(false)
1801
+ });
1802
+ var StorageObjectSchema = z20.object({
1803
+ object: z20.literal("storage_object"),
1804
+ bucket: z20.string(),
1805
+ key: z20.string().min(1),
1806
+ size_bytes: z20.number().int().nonnegative(),
1807
+ content_type: z20.string(),
1808
+ etag: z20.string().nullable(),
1809
+ updated_at: Timestamp
1810
+ });
1811
+ var SignedUrlMethodSchema = z20.enum(["PUT", "GET"]);
1812
+ var SignedUrlSchema = z20.object({
1813
+ object: z20.literal("signed_url"),
1814
+ url: z20.string().url(),
1815
+ method: SignedUrlMethodSchema,
1816
+ expires_at: Timestamp
1817
+ });
1818
+ var CreateUploadUrlSchema = z20.object({
1819
+ content_type: z20.string().min(1),
1820
+ expires_seconds: z20.number().int().min(60).max(3600).default(900)
1821
+ });
1822
+
1823
+ // ../core/src/schema/stock-photo.ts
1824
+ import { z as z21 } from "zod";
1825
+ var STOCK_PHOTO_ORIENTATIONS = ["landscape", "portrait", "square"];
1826
+ var STOCK_PHOTO_SIZES = ["small", "medium", "large"];
1827
+ var StockPhotoQuerySchema = z21.object({
1828
+ query: z21.string().min(1).max(200),
1829
+ count: z21.coerce.number().int().min(1).max(30).default(10),
1830
+ orientation: z21.enum(STOCK_PHOTO_ORIENTATIONS).optional(),
1831
+ color: z21.string().min(1).max(32).optional(),
1832
+ size: z21.enum(STOCK_PHOTO_SIZES).optional()
1833
+ });
1834
+ var StockPhotoSchema = z21.object({
1835
+ object: z21.literal("stock_photo"),
1836
+ id: z21.string().min(1),
1837
+ width: z21.number().int().positive(),
1838
+ height: z21.number().int().positive(),
1839
+ url: z21.string().url(),
1840
+ preview_url: z21.string().url(),
1841
+ alt: z21.string().nullable(),
1842
+ photographer: z21.string().nullable(),
1843
+ attribution_url: z21.string().url().nullable(),
1844
+ dominant_color: z21.string().nullable()
1845
+ });
1846
+
1847
+ // ../core/src/schema/creator.ts
1848
+ import { z as z22 } from "zod";
1849
+ var CreatorCodeSchema = z22.string().regex(/^[a-z0-9]{3,24}$/, "3 to 24 lowercase letters or digits");
1850
+ var PayoutMethodSchema = z22.enum(["wise", "paypal"]);
1851
+ var PayoutDetailsSchema = z22.object({
1852
+ method: PayoutMethodSchema,
1853
+ account: z22.string().min(1).max(320)
1854
+ });
1855
+ var CreatorStatusSchema = z22.enum(["active", "suspended"]);
1856
+ var CreatorSchema = z22.object({
1857
+ id: idOf("crt"),
1858
+ object: z22.literal("creator"),
1859
+ email: z22.email(),
1860
+ display_name: z22.string().min(1).max(80),
1861
+ code: CreatorCodeSchema,
1862
+ status: CreatorStatusSchema,
1863
+ payout: PayoutDetailsSchema.nullable(),
1864
+ clicks: z22.number().int().nonnegative(),
1865
+ created_at: Timestamp,
1866
+ updated_at: Timestamp
1867
+ });
1868
+ var IdentityCreatorSchema = CreatorSchema.pick({
1869
+ id: true,
1870
+ status: true
1871
+ });
1872
+ var CreatorApplicationStatusSchema = z22.enum([
1873
+ "pending",
1874
+ "approved",
1875
+ "rejected"
1876
+ ]);
1877
+ var CreatorApplicationSchema = z22.object({
1878
+ id: idOf("cra"),
1879
+ object: z22.literal("creator_application"),
1880
+ email: z22.email(),
1881
+ display_name: z22.string().min(1).max(80),
1882
+ pitch: z22.string().min(1).max(2e3),
1883
+ links: z22.array(z22.url({ protocol: /^https$/ })).max(5),
1884
+ status: CreatorApplicationStatusSchema,
1885
+ note: z22.string().nullable(),
1886
+ creator_id: idOf("crt").nullable(),
1887
+ created_at: Timestamp,
1888
+ decided_at: Timestamp.nullable()
1889
+ });
1890
+ var ReferralStatusSchema = z22.enum(["pending", "active", "expired"]);
1891
+ var ReferralSchema = z22.object({
1892
+ id: idOf("crf"),
1893
+ object: z22.literal("referral"),
1894
+ creator_id: idOf("crt"),
1895
+ organization_id: idOf("org"),
1896
+ status: ReferralStatusSchema,
1897
+ attributed_at: Timestamp,
1898
+ first_paid_at: Timestamp.nullable(),
1899
+ commission_until: Timestamp.nullable()
1900
+ });
1901
+ var CommissionKindSchema = z22.enum(["referral", "template"]);
1902
+ var CommissionStatusSchema = z22.enum([
1903
+ "pending",
1904
+ "available",
1905
+ "claimed",
1906
+ "paid"
1907
+ ]);
1908
+ var CommissionSchema = z22.object({
1909
+ id: idOf("crc"),
1910
+ object: z22.literal("commission"),
1911
+ creator_id: idOf("crt"),
1912
+ kind: CommissionKindSchema,
1913
+ referral_id: idOf("crf").nullable(),
1914
+ install_id: idOf("bpi").nullable(),
1915
+ creator_template_id: idOf("ctp").nullable(),
1916
+ base_micro_usd: MicroUsdField.nonnegative(),
1917
+ amount_micro_usd: MicroUsdField.nonnegative(),
1918
+ status: CommissionStatusSchema,
1919
+ accrued_at: Timestamp,
1920
+ available_at: Timestamp,
1921
+ payout_id: idOf("crp").nullable()
1922
+ });
1923
+ var PayoutStatusSchema = z22.enum(["requested", "paid", "rejected"]);
1924
+ var PayoutSchema = z22.object({
1925
+ id: idOf("crp"),
1926
+ object: z22.literal("payout"),
1927
+ creator_id: idOf("crt"),
1928
+ amount_micro_usd: MicroUsdField.positive(),
1929
+ currency: z22.literal("USD"),
1930
+ method: PayoutMethodSchema,
1931
+ account: z22.string().min(1),
1932
+ status: PayoutStatusSchema,
1933
+ reference: z22.string().nullable(),
1934
+ note: z22.string().nullable(),
1935
+ requested_at: Timestamp,
1936
+ decided_at: Timestamp.nullable()
1937
+ });
1938
+ var CREATOR_PROGRAM = {
1939
+ referral_percent: 20,
1940
+ template_percent: 5,
1941
+ commission_months: 12,
1942
+ hold_days: 30,
1943
+ attribution_days: 30,
1944
+ minimum_payout_micro_usd: 2e7
1945
+ };
1946
+ var CreatorTemplateSlugSchema = z22.string().max(32).regex(
1947
+ /^[a-z0-9]+(-[a-z0-9]+)*$/,
1948
+ "lowercase letters, digits and single dashes"
1949
+ );
1950
+ var CreatorSkillSchema = z22.object({
1951
+ slug: z22.string().regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters, digits and dashes"),
1952
+ body: z22.string().min(1).max(64 * 1024)
1953
+ });
1954
+ var CreatorDeclarationSchema = z22.object({
1955
+ agents: z22.array(z22.unknown()),
1956
+ questions: z22.array(z22.unknown()),
1957
+ skills: z22.array(CreatorSkillSchema).max(16)
1958
+ });
1959
+ var CreatorTemplateVersionStatusSchema = z22.enum([
1960
+ "in_review",
1961
+ "approved",
1962
+ "changes_requested"
1963
+ ]);
1964
+ var VersionNumber = z22.number().int().positive();
1965
+ var CreatorTemplateSchema = z22.object({
1966
+ id: idOf("ctp"),
1967
+ object: z22.literal("creator_template"),
1968
+ creator_id: idOf("crt"),
1969
+ blueprint: z22.string().min(1),
1970
+ base_template: z22.string().min(1),
1971
+ slug: CreatorTemplateSlugSchema,
1972
+ template: z22.string().min(1).max(64),
1973
+ name: z22.string().min(1).max(80),
1974
+ description: z22.string().max(2e3),
1975
+ declaration: CreatorDeclarationSchema,
1976
+ published_version: VersionNumber.nullable(),
1977
+ latest_version: z22.object({
1978
+ version: VersionNumber,
1979
+ status: CreatorTemplateVersionStatusSchema
1980
+ }).nullable(),
1981
+ created_at: Timestamp,
1982
+ updated_at: Timestamp
1983
+ });
1984
+ var CreatorTemplateVersionSchema = z22.object({
1985
+ object: z22.literal("creator_template_version"),
1986
+ template_id: idOf("ctp"),
1987
+ version: VersionNumber,
1988
+ declaration: CreatorDeclarationSchema,
1989
+ status: CreatorTemplateVersionStatusSchema,
1990
+ note: z22.string().nullable(),
1991
+ artifact_id: idOf("bpa").nullable(),
1992
+ submitted_at: Timestamp,
1993
+ decided_at: Timestamp.nullable()
1994
+ });
1995
+ var CreatorBalanceSchema = z22.object({
1996
+ object: z22.literal("creator_balance"),
1997
+ pending_micro_usd: MicroUsdField.nonnegative(),
1998
+ available_micro_usd: MicroUsdField.nonnegative(),
1999
+ claimed_micro_usd: MicroUsdField.nonnegative(),
2000
+ paid_micro_usd: MicroUsdField.nonnegative(),
2001
+ minimum_payout_micro_usd: z22.literal(CREATOR_PROGRAM.minimum_payout_micro_usd)
2002
+ });
2003
+ var ReferralClickSchema = z22.object({
2004
+ object: z22.literal("referral_click"),
2005
+ code: CreatorCodeSchema,
2006
+ creator: z22.object({ display_name: z22.string().min(1) }),
2007
+ expires_at: Timestamp
2008
+ });
2009
+ var Day = z22.iso.date();
2010
+ var CreatorAnalyticsSchema = z22.object({
2011
+ object: z22.literal("creator_analytics"),
2012
+ days: z22.number().int().min(1).max(90),
2013
+ from: Day,
2014
+ to: Day,
2015
+ clicks: z22.number().int().nonnegative(),
2016
+ referrals: z22.number().int().nonnegative(),
2017
+ installs: z22.number().int().nonnegative(),
2018
+ active_installs: z22.number().int().nonnegative(),
2019
+ usage_by_day: z22.array(
2020
+ z22.object({ day: Day, debit_micro_usd: MicroUsdField.nonnegative() })
2021
+ ),
2022
+ commission_micro_usd: z22.object({
2023
+ pending: MicroUsdField.nonnegative(),
2024
+ available: MicroUsdField.nonnegative(),
2025
+ claimed: MicroUsdField.nonnegative(),
2026
+ paid: MicroUsdField.nonnegative()
2027
+ }),
2028
+ commissions_by_template: z22.array(
2029
+ z22.object({
2030
+ creator_template_id: idOf("ctp"),
2031
+ amount_micro_usd: MicroUsdField.nonnegative()
2032
+ })
2033
+ ),
2034
+ /** Every install covered, newest first: the day it was created and the day its twelve months end. */
2035
+ install_windows: z22.array(z22.object({ install_id: idOf("bpi"), from: Day, until: Day }))
2036
+ });
2037
+
2038
+ // ../core/src/schema/legal.ts
2039
+ import { z as z23 } from "zod";
2040
+ var LEGAL_MEMBER_STATUSES = [
2041
+ "pending",
2042
+ "link_ready",
2043
+ "link_sent",
2044
+ "in_progress",
2045
+ "pending_review",
2046
+ "verified",
2047
+ "failed"
2048
+ ];
2049
+ var LegalMemberStatusSchema = z23.enum(LEGAL_MEMBER_STATUSES);
2050
+ var LEGAL_VERIFICATION_STATUSES = ["in_progress", "pending_review", "verified", "failed"];
2051
+ var LegalVerificationStatusSchema = z23.enum(LEGAL_VERIFICATION_STATUSES);
2052
+ var LEGAL_COMPANY_STATUSES = ["awaiting_payment", "paid", "submitted", "formed", "failed"];
2053
+ var LegalCompanyStatusSchema = z23.enum(LEGAL_COMPANY_STATUSES);
2054
+ var LEGAL_DOCUMENT_KINDS = ["articles", "ein_letter", "operating_agreement", "other"];
2055
+ var LegalDocumentKindSchema = z23.enum(LEGAL_DOCUMENT_KINDS);
2056
+ var LegalMemberRoleSchema = z23.enum(["primary", "secondary"]);
2057
+ var LegalEinStatusSchema = z23.enum(["none", "pending", "issued"]);
2058
+ var LegalRequiredActionKindSchema = z23.enum(["signature", "information", "payment", "other"]);
2059
+ var US_STATES = [
2060
+ "AL",
2061
+ "AK",
2062
+ "AZ",
2063
+ "AR",
2064
+ "CA",
2065
+ "CO",
2066
+ "CT",
2067
+ "DE",
2068
+ "DC",
2069
+ "FL",
2070
+ "GA",
2071
+ "HI",
2072
+ "ID",
2073
+ "IL",
2074
+ "IN",
2075
+ "IA",
2076
+ "KS",
2077
+ "KY",
2078
+ "LA",
2079
+ "ME",
2080
+ "MD",
2081
+ "MA",
2082
+ "MI",
2083
+ "MN",
2084
+ "MS",
2085
+ "MO",
2086
+ "MT",
2087
+ "NE",
2088
+ "NV",
2089
+ "NH",
2090
+ "NJ",
2091
+ "NM",
2092
+ "NY",
2093
+ "NC",
2094
+ "ND",
2095
+ "OH",
2096
+ "OK",
2097
+ "OR",
2098
+ "PA",
2099
+ "RI",
2100
+ "SC",
2101
+ "SD",
2102
+ "TN",
2103
+ "TX",
2104
+ "UT",
2105
+ "VT",
2106
+ "VA",
2107
+ "WA",
2108
+ "WV",
2109
+ "WI",
2110
+ "WY"
2111
+ ];
2112
+ var UsStateSchema = z23.enum(US_STATES);
2113
+ var LegalMemberInputSchema = z23.object({
2114
+ first_name: z23.string().min(1).max(128),
2115
+ last_name: z23.string().min(1).max(128),
2116
+ email: z23.string().min(1).max(320),
2117
+ role: LegalMemberRoleSchema,
2118
+ is_responsible_party: z23.boolean(),
2119
+ ownership_percent: z23.number().int()
2120
+ });
2121
+ var LegalMemberSchema = LegalMemberInputSchema.extend({
2122
+ id: idOf("lgm"),
2123
+ object: z23.literal("legal_member"),
2124
+ verification_id: idOf("lgv"),
2125
+ status: LegalMemberStatusSchema,
2126
+ link_url: z23.url().nullable(),
2127
+ link_expires_at: Timestamp.nullable(),
2128
+ completed_at: Timestamp.nullable()
2129
+ });
2130
+ var LegalVerificationSchema = z23.object({
2131
+ id: idOf("lgv"),
2132
+ object: z23.literal("legal_verification"),
2133
+ identity_id: idOf("idn"),
2134
+ status: LegalVerificationStatusSchema,
2135
+ members: z23.array(LegalMemberSchema),
2136
+ ready_for_formation: z23.boolean(),
2137
+ started_at: Timestamp,
2138
+ completed_at: Timestamp.nullable(),
2139
+ created_at: Timestamp,
2140
+ updated_at: Timestamp
2141
+ });
2142
+ var StartLegalVerificationSchema = z23.object({ members: z23.array(LegalMemberInputSchema) });
2143
+ var CompleteLegalMemberSchema = z23.object({ token: z23.string().min(1) });
2144
+ var LegalNameOptionSchema = z23.object({ name: z23.string().min(1).max(200), ending: z23.literal("LLC") });
2145
+ var LegalMailingAddressSchema = z23.object({
2146
+ line1: z23.string().min(1).max(200),
2147
+ line2: z23.string().max(200).nullable(),
2148
+ city: z23.string().min(1).max(100),
2149
+ state: UsStateSchema,
2150
+ postal_code: z23.string().min(1).max(16),
2151
+ country: z23.literal("US")
2152
+ });
2153
+ var LegalRequiredActionSchema = z23.object({
2154
+ kind: LegalRequiredActionKindSchema,
2155
+ description: z23.string(),
2156
+ url: z23.url().nullable()
2157
+ });
2158
+ var LEGAL_DESCRIPTION_MAX = 50;
2159
+ var CreateLegalCompanySchema = z23.object({
2160
+ verification_id: idOf("lgv"),
2161
+ state: UsStateSchema,
2162
+ name_options: z23.array(z23.string().min(1).max(200)).min(1).max(3),
2163
+ naics_code: z23.string().regex(/^\d{2,6}$/),
2164
+ description: z23.string().min(1).max(LEGAL_DESCRIPTION_MAX),
2165
+ mailing_address: LegalMailingAddressSchema
2166
+ });
2167
+ var LegalCompanySchema = CreateLegalCompanySchema.omit({ name_options: true }).extend({
2168
+ id: idOf("lgc"),
2169
+ object: z23.literal("legal_company"),
2170
+ identity_id: idOf("idn"),
2171
+ status: LegalCompanyStatusSchema,
2172
+ entity_type: z23.literal("llc"),
2173
+ name_options: z23.array(LegalNameOptionSchema),
2174
+ legal_name: z23.string().nullable(),
2175
+ ein_status: LegalEinStatusSchema,
2176
+ price_micro_usd: MicroUsdField,
2177
+ required_actions: z23.array(LegalRequiredActionSchema),
2178
+ failure_reason: z23.string().max(256).nullable(),
2179
+ submitted_at: Timestamp.nullable(),
2180
+ formed_at: Timestamp.nullable(),
2181
+ created_at: Timestamp,
2182
+ updated_at: Timestamp
2183
+ });
2184
+ var LegalDocumentSchema = z23.object({
2185
+ id: idOf("lgd"),
2186
+ object: z23.literal("legal_document"),
2187
+ company_id: idOf("lgc"),
2188
+ kind: LegalDocumentKindSchema,
2189
+ name: z23.string(),
2190
+ created_at: Timestamp
2191
+ });
2192
+ var LegalDocumentLinkSchema = z23.object({
2193
+ object: z23.literal("legal_document_link"),
2194
+ url: z23.url(),
2195
+ expires_at: Timestamp
2196
+ });
2197
+ var NaicsCodeSchema = z23.object({
2198
+ object: z23.literal("naics_code"),
2199
+ code: z23.string().regex(/^\d{2,6}$/),
2200
+ title: z23.string()
2201
+ });
2202
+ var LegalStateSchema = z23.object({
2203
+ object: z23.literal("legal_state"),
2204
+ code: UsStateSchema,
2205
+ name: z23.string(),
2206
+ formation_fee_micro_usd: MicroUsdField
2207
+ });
2208
+ var LegalWebhookEventSchema = z23.object({
2209
+ provider_session_ref: z23.string().min(1),
2210
+ status: z23.enum(["verified", "failed", "pending_review"]),
2211
+ metadata: Metadata
2212
+ });
2213
+ var LLC_ENDING = /\s*(?:,\s*)?(?:l\.?l\.?c\.?|limited\s+liability\s+company)\s*$/i;
2214
+ function canonicalName(raw) {
2215
+ const name = raw.replace(LLC_ENDING, "").trim();
2216
+ if (name.length === 0) throw new VettaError("validation_failed", "A company name is required", "name_options");
2217
+ return { name, ending: "LLC" };
2218
+ }
2219
+
2220
+ // ../core/src/schema/wallet.ts
2221
+ import { z as z24 } from "zod";
2222
+ var WALLET_NETWORKS = ["eip155:84532", "eip155:8453"];
2223
+ var WalletNetworkSchema = z24.enum(WALLET_NETWORKS);
2224
+ var DEFAULT_WALLET_NETWORK = "eip155:84532";
2225
+ var WalletSelectSchema = z24.object({ network: WalletNetworkSchema.optional() });
2226
+ var WALLET_STATUSES = ["active", "frozen", "swept"];
2227
+ var WalletStatusSchema = z24.enum(WALLET_STATUSES);
2228
+ var WALLET_TRANSACTION_KINDS = ["fund", "transfer", "pay", "sweep"];
2229
+ var WalletTransactionKindSchema = z24.enum(WALLET_TRANSACTION_KINDS);
2230
+ var WALLET_TRANSACTION_STATUSES = ["pending", "confirmed", "failed"];
2231
+ var WalletTransactionStatusSchema = z24.enum(WALLET_TRANSACTION_STATUSES);
2232
+ var WalletDirectionSchema = z24.enum(["in", "out"]);
2233
+ var WalletFundSourceSchema = z24.enum(["faucet", "treasury"]);
2234
+ var EvmAddressSchema = z24.string().regex(/^0x[0-9a-fA-F]{40}$/, "expected a 0x address");
2235
+ var TxHashSchema = z24.string().regex(/^0x[0-9a-fA-F]{64}$/, "expected a 0x transaction hash");
2236
+ var WalletPolicySchema = z24.object({
2237
+ per_tx_cap_micro_usd: MicroUsdField.nonnegative(),
2238
+ daily_cap_micro_usd: MicroUsdField.positive().nullable(),
2239
+ low_balance_micro_usd: MicroUsdField.nonnegative().nullable()
2240
+ });
2241
+ var WalletSchema = z24.object({
2242
+ id: idOf("wlt"),
2243
+ object: z24.literal("wallet"),
2244
+ identity_id: idOf("idn"),
2245
+ status: WalletStatusSchema,
2246
+ network: WalletNetworkSchema,
2247
+ address: EvmAddressSchema,
2248
+ asset: z24.literal("usdc"),
2249
+ balance_micro_usd: MicroUsdField,
2250
+ policy: WalletPolicySchema,
2251
+ spent_today_micro_usd: MicroUsdField,
2252
+ created_at: Timestamp,
2253
+ updated_at: Timestamp
2254
+ });
2255
+ var CreateWalletSchema = z24.object({
2256
+ network: WalletNetworkSchema.optional(),
2257
+ policy: WalletPolicySchema.partial().optional()
2258
+ });
2259
+ var UpdateWalletPolicySchema = WalletPolicySchema.partial();
2260
+ var FundWalletSchema = z24.object({
2261
+ amount_micro_usd: MicroUsdField.positive(),
2262
+ source: WalletFundSourceSchema
2263
+ });
2264
+ var TransferWalletSchema = z24.object({
2265
+ to: z24.union([EvmAddressSchema, idOf("idn")]),
2266
+ amount_micro_usd: MicroUsdField.positive(),
2267
+ purpose: z24.string().min(1).max(256)
2268
+ });
2269
+ var SweepWalletSchema = z24.object({ to: EvmAddressSchema.optional() });
2270
+ var WalletTransactionSchema = z24.object({
2271
+ id: idOf("wtx"),
2272
+ object: z24.literal("wallet_transaction"),
2273
+ wallet_id: idOf("wlt"),
2274
+ kind: WalletTransactionKindSchema,
2275
+ direction: WalletDirectionSchema,
2276
+ amount_micro_usd: MicroUsdField,
2277
+ counterparty: z24.string().nullable(),
2278
+ origin: z24.string().nullable(),
2279
+ purpose: z24.string().nullable(),
2280
+ tx_hash: TxHashSchema.nullable(),
2281
+ session_id: idOf("ses").nullable(),
2282
+ status: WalletTransactionStatusSchema,
2283
+ created_at: Timestamp
2284
+ });
2285
+ var ListWalletTransactionsSchema = z24.object({
2286
+ kind: WalletTransactionKindSchema.optional(),
2287
+ direction: WalletDirectionSchema.optional(),
2288
+ origin: z24.string().optional(),
2289
+ since: Timestamp.optional()
2290
+ });
2291
+ var HttpMethodSchema = z24.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]);
2292
+ var WalletQuoteRequestSchema = z24.object({ url: z24.url(), method: HttpMethodSchema.optional() });
2293
+ var WalletQuoteSchema = z24.object({
2294
+ object: z24.literal("wallet_quote"),
2295
+ url: z24.url(),
2296
+ payable: z24.boolean(),
2297
+ network: WalletNetworkSchema.nullable(),
2298
+ asset: z24.literal("usdc").nullable(),
2299
+ amount_micro_usd: MicroUsdField.nullable(),
2300
+ pay_to: EvmAddressSchema.nullable(),
2301
+ description: z24.string().nullable(),
2302
+ max_timeout_seconds: z24.number().int().nullable()
2303
+ });
2304
+ var WalletPayRequestSchema = WalletQuoteRequestSchema.extend({
2305
+ max_amount_micro_usd: MicroUsdField.positive().optional()
2306
+ });
2307
+ var WALLET_RESPONSE_BODY_MAX = 64 * 1024;
2308
+ var WalletPaymentSchema = z24.object({
2309
+ object: z24.literal("wallet_payment"),
2310
+ paid: z24.boolean(),
2311
+ transaction_id: idOf("wtx").nullable(),
2312
+ amount_micro_usd: MicroUsdField.nullable(),
2313
+ response_status: z24.number().int(),
2314
+ response_body: z24.string().max(WALLET_RESPONSE_BODY_MAX)
2315
+ });
2316
+
2317
+ // ../core/src/schema/cards.ts
2318
+ import { z as z25 } from "zod";
2319
+ var CARD_STATUSES = ["pending_payment", "issuing", "active", "failed", "cancelled", "refunded"];
2320
+ var CardStatusSchema = z25.enum(CARD_STATUSES);
2321
+ var CARD_MIN_LOAD_MICRO_USD = 1e6;
2322
+ var CARD_MAX_LOAD_MICRO_USD = 15e7;
2323
+ var CARD_LOAD_GRANULARITY_MICRO_USD = 1e4;
2324
+ var CardSchema = z25.object({
2325
+ id: idOf("vcd"),
2326
+ object: z25.literal("card"),
2327
+ identity_id: idOf("idn"),
2328
+ kind: z25.literal("prepaid"),
2329
+ status: CardStatusSchema,
2330
+ label: z25.string().min(1).max(128),
2331
+ brand: z25.string().nullable(),
2332
+ last4: z25.string().regex(/^\d{4}$/).nullable(),
2333
+ load_micro_usd: MicroUsdField,
2334
+ spent_micro_usd: MicroUsdField,
2335
+ currency: z25.literal("usd"),
2336
+ expires_at: Timestamp.nullable(),
2337
+ failure_reason: z25.string().max(256).nullable(),
2338
+ created_at: Timestamp,
2339
+ updated_at: Timestamp
2340
+ });
2341
+ var IssueCardSchema = z25.object({
2342
+ label: CardSchema.shape.label,
2343
+ load_micro_usd: MicroUsdField.min(CARD_MIN_LOAD_MICRO_USD).max(CARD_MAX_LOAD_MICRO_USD).multipleOf(CARD_LOAD_GRANULARITY_MICRO_USD)
2344
+ });
2345
+ var CardQuoteRequestSchema = IssueCardSchema.pick({ load_micro_usd: true });
2346
+ var CardQuoteSchema = z25.object({
2347
+ object: z25.literal("card_quote"),
2348
+ load_micro_usd: MicroUsdField,
2349
+ price_micro_usd: MicroUsdField,
2350
+ brand: z25.string().nullable(),
2351
+ min_load_micro_usd: MicroUsdField,
2352
+ max_load_micro_usd: MicroUsdField
2353
+ });
2354
+ var CardCredentialsSchema = z25.object({
2355
+ object: z25.literal("card_credentials"),
2356
+ card_id: idOf("vcd"),
2357
+ number: z25.string().min(1),
2358
+ pin: z25.string().nullable(),
2359
+ cvv: z25.string().nullable(),
2360
+ expiry: z25.string().nullable(),
2361
+ url: z25.url().nullable(),
2362
+ instructions: z25.string().nullable(),
2363
+ revealed_at: Timestamp
2364
+ });
2365
+ var CardTransactionSchema = z25.object({
2366
+ id: idOf("vct"),
2367
+ object: z25.literal("card_transaction"),
2368
+ card_id: idOf("vcd"),
2369
+ amount_micro_usd: MicroUsdField,
2370
+ merchant: z25.string().nullable(),
2371
+ description: z25.string().nullable(),
2372
+ session_id: idOf("ses").nullable(),
2373
+ metadata: Metadata,
2374
+ created_at: Timestamp
2375
+ });
2376
+ var LogCardTransactionSchema = z25.object({
2377
+ amount_micro_usd: MicroUsdField.positive(),
2378
+ merchant: z25.string().max(256).optional(),
2379
+ description: z25.string().max(1024).optional(),
2380
+ metadata: Metadata.optional()
2381
+ });
2382
+ var ListCardsSchema = z25.object({ status: CardStatusSchema.optional() });
2383
+
2384
+ // src/define.ts
2385
+ import { z as z28 } from "zod";
2386
+
2387
+ // src/roster.ts
2388
+ import { z as z27 } from "zod";
2389
+
2390
+ // src/report.ts
2391
+ import { ApiError, send } from "@usenaive-sdk/vetta";
2392
+ import { z as z26 } from "zod";
2393
+ function message(error) {
2394
+ return error instanceof Error ? error.message : String(error);
2395
+ }
2396
+ async function line(name, work) {
2397
+ try {
2398
+ return await work();
2399
+ } catch (error) {
2400
+ return { name, action: "refused", reason: message(error) };
2401
+ }
2402
+ }
2403
+ var AT_ONCE = 6;
2404
+ async function eachInFlight(items, work) {
2405
+ const out = [];
2406
+ for (let at = 0; at < items.length; at += AT_ONCE) {
2407
+ out.push(...await Promise.all(items.slice(at, at + AT_ONCE).map(work)));
2408
+ }
2409
+ return out;
2410
+ }
2411
+ async function attempt(into, name, work) {
2412
+ const one = await line(name, work);
2413
+ into.push(one);
2414
+ return one;
2415
+ }
2416
+ var canonical = (value) => {
2417
+ if (Array.isArray(value)) return value.map(canonical);
2418
+ if (value === null || typeof value !== "object") return value;
2419
+ return Object.fromEntries(
2420
+ Object.keys(value).sort().map((key) => [key, canonical(value[key])])
2421
+ );
2422
+ };
2423
+ var changed = (declared, live) => declared !== void 0 && JSON.stringify(canonical(declared)) !== JSON.stringify(canonical(live));
2424
+ async function absent(work) {
2425
+ try {
2426
+ return await work;
2427
+ } catch (error) {
2428
+ if (error instanceof ApiError && error.status === 404) return null;
2429
+ throw error;
2430
+ }
2431
+ }
2432
+ var PageShape = z26.object({
2433
+ data: z26.array(z26.unknown()),
2434
+ has_more: z26.boolean(),
2435
+ next_cursor: z26.string().nullable()
1076
2436
  });
2437
+ function rowLabel(row) {
2438
+ const { object, id } = row ?? {};
2439
+ const words = [object, id].filter((one) => typeof one === "string");
2440
+ return words.length > 0 ? words.join(" ") : "a row";
2441
+ }
2442
+ var why = (error) => error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
2443
+ async function listAll(config, path, item, skipped, query = {}) {
2444
+ const rows = [];
2445
+ let after;
2446
+ for (; ; ) {
2447
+ const response = await send(config, { method: "GET", path, query: { ...query, limit: 100, after } }, "application/json");
2448
+ const text = await response.text();
2449
+ if (!response.ok) throw new Error(`could not list ${path} (HTTP ${response.status})`);
2450
+ const body = PageShape.parse(JSON.parse(text));
2451
+ for (const row of body.data) {
2452
+ const parsed = item.safeParse(row);
2453
+ if (parsed.success) rows.push(parsed.data);
2454
+ else skipped.push(`${rowLabel(row)} in ${path} could not be read and was skipped: ${why(parsed.error)}`);
2455
+ }
2456
+ if (!body.has_more || body.next_cursor === null) return rows;
2457
+ after = body.next_cursor;
2458
+ }
2459
+ }
2460
+ async function tombstone(name, current, remove, dryRun) {
2461
+ if (!current) return { name, action: "unchanged", reason: "already absent" };
2462
+ if (!dryRun) await remove(current.id);
2463
+ return { name, action: "deleted", id: current.id };
2464
+ }
1077
2465
 
1078
- // src/define.ts
1079
- import { z as z16 } from "zod";
2466
+ // src/roster.ts
2467
+ var NAME = z27.string().min(1);
2468
+ var RosterNameSchema = z27.union([
2469
+ NAME.transform((name) => ({ type: "agent", name })),
2470
+ z27.object({ type: z27.literal("agent"), name: NAME, version: z27.number().int().positive().optional() }),
2471
+ z27.object({ type: z27.literal("self") }),
2472
+ z27.object({ type: z27.literal("advisor"), model: z27.string().min(1) })
2473
+ ]);
2474
+ var DeclMultiagentField = z27.union([
2475
+ z27.boolean(),
2476
+ z27.object({ ...MultiagentSchema.shape, agents: z27.array(RosterNameSchema).min(1).max(20) }),
2477
+ z27.null()
2478
+ ]);
2479
+ var hasRoster = (value) => typeof value === "object" && value !== null;
2480
+ var rosterNames = (roster) => roster.agents.flatMap((entry) => entry.type === "agent" ? [entry.name] : []);
2481
+ var teamWithoutRoster = (value) => hasRoster(value) ? void 0 : value;
2482
+ function unpinned(desired, live) {
2483
+ return {
2484
+ ...live,
2485
+ agents: live.agents.map((entry, i) => {
2486
+ const want = desired.agents[i];
2487
+ const pinned = want !== void 0 && "version" in want && want.version !== void 0;
2488
+ return entry.type === "agent" && !pinned ? { type: "agent", id: entry.id } : entry;
2489
+ })
2490
+ };
2491
+ }
2492
+ async function patchRoster(client, decl, roster, existing, ids, dryRun, lines) {
2493
+ const line2 = lines.find((one) => one.name === decl.name);
2494
+ if (line2 === void 0 || line2.action === "refused") return;
2495
+ const updated = () => {
2496
+ if (line2.action === "unchanged") line2.action = "updated";
2497
+ };
2498
+ const missing = rosterNames(roster).find((name) => !ids.has(name));
2499
+ if (missing !== void 0) {
2500
+ if (dryRun && lines.some((one) => one.name === missing && one.action === "created")) return updated();
2501
+ line2.action = "refused";
2502
+ line2.reason = `multiagent roster needs agent "${missing}", which was not provisioned in this run`;
2503
+ return;
2504
+ }
2505
+ const desired = {
2506
+ ...roster,
2507
+ agents: roster.agents.map(
2508
+ (entry) => entry.type === "agent" ? { type: "agent", id: ids.get(entry.name), ..."version" in entry && entry.version !== void 0 ? { version: entry.version } : {} } : entry
2509
+ )
2510
+ };
2511
+ const current = existing.find((agent) => agent.name === decl.name);
2512
+ const live = current === void 0 ? null : normaliseMultiagent(current.multiagent);
2513
+ if (live !== null && !changed(desired, unpinned(desired, live))) return;
2514
+ if (!dryRun && line2.id !== void 0) {
2515
+ try {
2516
+ await client.agents.update(line2.id, { multiagent: desired });
2517
+ } catch (error) {
2518
+ line2.action = "refused";
2519
+ line2.reason = error instanceof Error ? error.message : String(error);
2520
+ return;
2521
+ }
2522
+ }
2523
+ updated();
2524
+ }
1080
2525
 
1081
2526
  // src/template.ts
1082
2527
  var BLUEPRINTS = {
1083
2528
  /** Clients, pipeline, deliverables, calendar. */
1084
2529
  agency: { templates: ["blank", "seo-geo"], default: "blank" },
1085
2530
  /** Post queue, accounts, calendar, analytics. */
1086
- media: { templates: ["faceless", "clipping"] }
2531
+ media: { templates: ["faceless", "clipping"] },
2532
+ /** Pages, posts, forms, analytics — a published site with a crew behind it (`usenaive/web-blueprint`). */
2533
+ web: { templates: ["publisher"], default: "publisher" }
1087
2534
  };
1088
2535
  var list = (names) => names.join(", ");
1089
2536
  function chooseTemplate(blueprint, template) {
@@ -1115,47 +2562,73 @@ function chooseTemplate(blueprint, template) {
1115
2562
  var BlueprintRefusal = class extends Error {
1116
2563
  name = "BlueprintRefusal";
1117
2564
  };
1118
- var EnvValueSchema = z16.union([
1119
- z16.string(),
1120
- z16.object({ from_env: z16.string().min(1) }),
1121
- z16.object({ generate: z16.literal(true) })
2565
+ var EnvValueSchema = z28.union([
2566
+ z28.string(),
2567
+ z28.object({ from_env: z28.string().min(1) }),
2568
+ z28.object({ generate: z28.literal(true) })
1122
2569
  ]);
1123
- var AppDeclSchema = z16.object({
2570
+ var AppDeclSchema = z28.object({
1124
2571
  /** Becomes a hostname, so the grammar is the platform's `APP_NAME` (a DNS label). */
1125
- name: z16.string().regex(APP_NAME, "a lowercase DNS label, 63 characters or fewer"),
2572
+ name: z28.string().regex(APP_NAME, "a lowercase DNS label, 63 characters or fewer"),
1126
2573
  type: AppTypeSchema,
1127
- description: z16.string().optional(),
2574
+ description: z28.string().optional(),
1128
2575
  /** A directory whose files are collected and shipped as the app's file-map deployment. */
1129
- deploy_dir: z16.string().min(1).optional(),
2576
+ deploy_dir: z28.string().min(1).optional(),
1130
2577
  /** The path the app itself serves MCP on (§29.5); `fullstack` only. Absent means none. */
1131
2578
  mcp: AppMcpPathSchema.optional(),
1132
2579
  /** Written as the app's write-only secrets (§29.2) on every apply; names follow the platform's `ENV_NAME`. */
1133
- env: z16.record(z16.string().regex(ENV_NAME), EnvValueSchema, { error: "keyed by environment variable name (A-Z, 0-9, _)" }).optional()
1134
- });
1135
- var ScheduleDeclSchema = z16.object({
1136
- cron: z16.string().min(1),
1137
- timezone: z16.string().min(1).optional(),
2580
+ env: z28.record(z28.string().regex(ENV_NAME), EnvValueSchema, { error: "keyed by environment variable name (A-Z, 0-9, _)" }).optional(),
2581
+ /** An install may not leave this app out of its `selection` (§31.2). Absent means it may. */
2582
+ required: z28.boolean().optional(),
2583
+ // core-gaps: U8-database
2584
+ /** SQL files applied in order to the app's database after it is provisioned (§37); `fullstack` only. */
2585
+ migrations: z28.array(z28.string().min(1)).optional(),
2586
+ // core-gaps: storage-stock
2587
+ /** Buckets the app's object store must hold (§38.2); `fullstack` only. `public` defaults to false. */
2588
+ buckets: z28.array(CreateBucketSchema).optional()
2589
+ });
2590
+ var ScheduleDeclSchema = z28.object({
2591
+ cron: z28.string().min(1),
2592
+ timezone: z28.string().min(1).optional(),
1138
2593
  /** The message every fire opens its session with (`input_template` on the wire). */
1139
- input: z16.string().min(1),
2594
+ input: z28.string().min(1),
1140
2595
  budget_micro_usd: MicroUsdField.nonnegative(),
1141
- identity: z16.string().min(1).optional(),
1142
- enabled: z16.boolean().optional()
2596
+ identity: z28.string().min(1).optional(),
2597
+ enabled: z28.boolean().optional()
1143
2598
  });
1144
- var AgentDeclSchema = z16.object({
1145
- name: z16.string().min(1),
1146
- model: z16.string().min(1),
2599
+ var ToolsDeclSchema = ToolsetSchema;
2600
+ var AgentDeclSchema = z28.object({
2601
+ name: z28.string().min(1),
2602
+ model: z28.string().min(1),
1147
2603
  budget: BudgetSchema,
1148
- description: z16.string().optional(),
1149
- system: z16.string().optional(),
1150
- tools: ToolsetSchema.optional(),
1151
- skills: z16.array(SkillRefSchema).optional(),
1152
- mcp_servers: z16.array(McpServerSchema).optional(),
2604
+ description: z28.string().optional(),
2605
+ /** Two or three words the studio prints beside the name (§31.5) — "Head of content", not a sentence. */
2606
+ role: z28.string().min(1).optional(),
2607
+ /** An install may not leave this agent out of its `selection` (§31.2). Absent means it may. */
2608
+ required: z28.boolean().optional(),
2609
+ system: z28.string().optional(),
2610
+ tools: ToolsDeclSchema.optional(),
2611
+ skills: z28.array(SkillRefSchema).optional(),
2612
+ mcp_servers: z28.array(McpServerSchema).optional(),
2613
+ /** The rest of the runtime's agent config (§31.6), each the core schema; absent means the route's default and never drift. */
2614
+ window: WindowSchema.optional(),
2615
+ /** A roster names crew members by declared *name*; `up` resolves them to `agt_` ids in a second pass (`roster.ts`). */
2616
+ multiagent: DeclMultiagentField.optional(),
2617
+ output_schema: JsonSchemaDocument.nullable().optional(),
2618
+ structured_output_required: z28.boolean().optional(),
2619
+ metadata: Metadata.optional(),
1153
2620
  /** Declared app *names*; `up` resolves them to `app_` ids in `tools.configs.apps` (§29.3). */
1154
- allowed_apps: z16.array(z16.string()).optional(),
2621
+ allowed_apps: z28.array(z28.string()).optional(),
1155
2622
  /** A declared identity *name*; `up` attaches it after the agent upsert (§22). */
1156
- identity: z16.string().min(1).optional(),
2623
+ identity: z28.string().min(1).optional(),
2624
+ /**
2625
+ * Who this agent may `send_to_agent` (§31.7, §28.12): `true` (anyone in the organization),
2626
+ * `false` (nobody), `"team"` (its `multiagent` roster), or declared agent *names*, checked
2627
+ * against the roster here. Absent means the route's default, `true`, and is never drift.
2628
+ */
2629
+ handoffs: HandoffsField.optional(),
1157
2630
  /** The agent's deployments as a complete set: declared → owned, and the rest are pruned (ADR-0355). */
1158
- schedules: z16.array(ScheduleDeclSchema).optional(),
2631
+ schedules: z28.array(ScheduleDeclSchema).optional(),
1159
2632
  /**
1160
2633
  * The first thing this agent reads, once, on the apply that **creates** it (`canonical-spec
1161
2634
  * §31.4`). It is what turns a provisioned crew into a crew that has started working.
@@ -1168,43 +2641,58 @@ var AgentDeclSchema = z16.object({
1168
2641
  * repeatedly and unattended while an intake spends once, at an apply a person is watching. Absent
1169
2642
  * means the agent's own `budget` governs.
1170
2643
  */
1171
- intake: z16.object({ message: z16.string().min(1), budget_micro_usd: MicroUsdField.nonnegative().optional() }).optional()
2644
+ intake: z28.object({ message: z28.string().min(1), budget_micro_usd: MicroUsdField.nonnegative().optional() }).optional()
1172
2645
  });
1173
- var CredentialDeclSchema = z16.object({
2646
+ var CredentialDeclSchema = z28.object({
1174
2647
  kind: VaultCredentialKindSchema,
1175
- key: z16.string().min(1).max(128),
2648
+ key: z28.string().min(1).max(128),
1176
2649
  /** The one exact destination host an `env_var` is bound to. */
1177
- host: z16.string().min(1).optional(),
2650
+ host: z28.string().min(1).optional(),
1178
2651
  /** The MCP server URL a `static_bearer` / `mcp_oauth` token is matched to. */
1179
- mcp_server_url: z16.url().optional(),
1180
- value: z16.object({ from_env: z16.string().min(1) })
2652
+ mcp_server_url: z28.url().optional(),
2653
+ value: z28.object({ from_env: z28.string().min(1) })
1181
2654
  });
1182
- var VaultDeclSchema = z16.object({
2655
+ var VaultDeclSchema = z28.object({
1183
2656
  name: VaultSchema.shape.display_name,
1184
- identity: z16.string().min(1).optional(),
1185
- credentials: z16.array(CredentialDeclSchema)
2657
+ identity: z28.string().min(1).optional(),
2658
+ credentials: z28.array(CredentialDeclSchema)
1186
2659
  });
1187
- var SkillDeclSchema = z16.object({
1188
- slug: z16.string().regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters, digits and dashes"),
1189
- file: z16.string().min(1)
2660
+ var SkillDeclSchema = z28.object({
2661
+ slug: z28.string().regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters, digits and dashes"),
2662
+ file: z28.string().min(1)
1190
2663
  });
1191
- var IdentityDeclSchema = IdentitySchema.pick({ name: true, description: true }).partial({
2664
+ var LegalDeclSchema = CreateLegalCompanySchema.pick({
2665
+ state: true,
2666
+ name_options: true,
2667
+ naics_code: true,
1192
2668
  description: true
1193
- });
1194
- var RemovedSchema = z16.object({
1195
- apps: z16.array(z16.string().min(1)).default([]),
1196
- agents: z16.array(z16.string().min(1)).default([]),
1197
- skills: z16.array(z16.string().min(1)).default([]),
1198
- identities: z16.array(z16.string().min(1)).default([]),
1199
- vaults: z16.array(z16.string().min(1)).default([])
1200
- });
1201
- var KeptSchema = z16.object({ agents: z16.array(z16.string().min(1)).default([]) });
1202
- var ProjectSchema = z16.object({
1203
- name: z16.string().min(1),
2669
+ }).extend({ label: z28.string().min(1).max(128) });
2670
+ var WalletDeclSchema = CreateWalletSchema.or(
2671
+ z28.array(CreateWalletSchema).refine(
2672
+ (list2) => new Set(list2.map((w) => w.network ?? DEFAULT_WALLET_NETWORK)).size === list2.length,
2673
+ { message: "one wallet per network" }
2674
+ )
2675
+ );
2676
+ var CardDeclSchema = IssueCardSchema;
2677
+ var IdentityDeclSchema = IdentitySchema.pick({ name: true, description: true }).partial({ description: true }).extend({
2678
+ legal: z28.array(LegalDeclSchema).optional(),
2679
+ wallet: WalletDeclSchema.optional(),
2680
+ cards: z28.array(CardDeclSchema).optional()
2681
+ });
2682
+ var RemovedSchema = z28.object({
2683
+ apps: z28.array(z28.string().min(1)).default([]),
2684
+ agents: z28.array(z28.string().min(1)).default([]),
2685
+ skills: z28.array(z28.string().min(1)).default([]),
2686
+ identities: z28.array(z28.string().min(1)).default([]),
2687
+ vaults: z28.array(z28.string().min(1)).default([])
2688
+ });
2689
+ var KeptSchema = z28.object({ agents: z28.array(z28.string().min(1)).default([]) });
2690
+ var ProjectSchema = z28.object({
2691
+ name: z28.string().min(1),
1204
2692
  /** The machine this project runs: its repo, its screens, its `/api/*` (`template.ts`). */
1205
- blueprint: z16.string().min(1).optional(),
2693
+ blueprint: z28.string().min(1).optional(),
1206
2694
  /** Which of that blueprint's templates provides the crew; a blueprint with a default may omit it. */
1207
- template: z16.string().min(1).optional(),
2695
+ template: z28.string().min(1).optional(),
1208
2696
  /**
1209
2697
  * What a person is asked BEFORE the crew is provisioned (`canonical-spec §31.2`).
1210
2698
  *
@@ -1216,12 +2704,19 @@ var ProjectSchema = z16.object({
1216
2704
  *
1217
2705
  * **Configuration only.** A secret is a vault credential or an app secret; neither is an answer.
1218
2706
  */
1219
- questions: z16.array(QuestionFieldSchema).default([]),
1220
- skills: z16.array(SkillDeclSchema).default([]),
1221
- identities: z16.array(IdentityDeclSchema).default([]),
1222
- vaults: z16.array(VaultDeclSchema).default([]),
1223
- apps: z16.array(AppDeclSchema).default([]),
1224
- agents: z16.array(AgentDeclSchema).default([]),
2707
+ questions: z28.array(QuestionFieldSchema).default([]),
2708
+ skills: z28.array(SkillDeclSchema).default([]),
2709
+ identities: z28.array(IdentityDeclSchema).default([]),
2710
+ vaults: z28.array(VaultDeclSchema).default([]),
2711
+ apps: z28.array(AppDeclSchema).default([]),
2712
+ agents: z28.array(AgentDeclSchema).default([]),
2713
+ /**
2714
+ * The agents this template creates PER CLIENT the operator signs (`canonical-spec §31.1`) —
2715
+ * printed on the artifact so a person sees the whole company, never provisioned by an apply,
2716
+ * because the apply does not know the clients yet. Names only: a model and a budget are the
2717
+ * crew's business at the moment a client is signed, not the declaration's.
2718
+ */
2719
+ crew_per_client: z28.array(z28.object({ name: z28.string().min(1), role: z28.string().min(1).optional(), description: z28.string().optional() })).optional(),
1225
2720
  removed: RemovedSchema.default({ apps: [], agents: [], skills: [], identities: [], vaults: [] }),
1226
2721
  kept: KeptSchema.default({ agents: [] })
1227
2722
  });
@@ -1252,11 +2747,19 @@ function applyChoice(config) {
1252
2747
  function parseProject(input) {
1253
2748
  const parsed = ProjectSchema.safeParse(input);
1254
2749
  if (!parsed.success) {
1255
- const lines = parsed.error.issues.map((i) => `${i.path.join(".") || "config"}: ${i.message}`);
2750
+ const unwrap = (i) => {
2751
+ if (i.code !== "invalid_union") return [i];
2752
+ const branch = i.errors.find((b) => !b.some((e) => e.code === "invalid_type" && e.path.length === 0));
2753
+ return branch ? branch.map((e) => ({ ...e, path: [...i.path, ...e.path] })) : [i];
2754
+ };
2755
+ const lines = parsed.error.issues.flatMap(unwrap).map((i) => `${i.path.join(".") || "config"}: ${i.message}`);
1256
2756
  refuse(`invalid project config \u2014 ${lines.join("; ")}`);
1257
2757
  }
1258
2758
  const config = parsed.data;
1259
2759
  applyChoice(config);
2760
+ if (config.template !== void 0 && config.questions.length > 3) {
2761
+ refuse(`template "${config.template}" asks ${config.questions.length} questions, but a template asks at most 3 before anything is provisioned \u2014 a fourth belongs to the crew's first conversation`);
2762
+ }
1260
2763
  assertUniqueNames("app", [...config.apps.map((a) => a.name), ...config.removed.apps]);
1261
2764
  assertUniqueNames("agent", [...config.agents.map((a) => a.name), ...config.removed.agents, ...config.kept.agents]);
1262
2765
  assertUniqueNames("skill", [...config.skills.map((s) => s.slug), ...config.removed.skills]);
@@ -1267,6 +2770,13 @@ function parseProject(input) {
1267
2770
  if (app.type === "frontend_only" && app.mcp !== void 0) {
1268
2771
  refuse(`app "${app.name}": mcp is set, but a frontend_only app has no server to answer on it \u2014 declare type "fullstack" or drop mcp`);
1269
2772
  }
2773
+ if (app.type === "frontend_only" && app.migrations !== void 0) {
2774
+ refuse(`app "${app.name}": migrations are set, but a frontend_only app has no database \u2014 declare type "fullstack" or drop migrations`);
2775
+ }
2776
+ if (app.type === "frontend_only" && app.buckets !== void 0) {
2777
+ refuse(`app "${app.name}": buckets are set, but a frontend_only app has no object store \u2014 declare type "fullstack" or drop buckets`);
2778
+ }
2779
+ assertUniqueNames("bucket", (app.buckets ?? []).map((bucket) => bucket.name));
1270
2780
  }
1271
2781
  const identityNames = new Set(config.identities.map((i) => i.name));
1272
2782
  for (const vault of config.vaults) {
@@ -1287,7 +2797,16 @@ function parseProject(input) {
1287
2797
  seen.add(at);
1288
2798
  }
1289
2799
  }
2800
+ const agentNames = /* @__PURE__ */ new Set([...config.agents.map((a) => a.name), ...config.kept.agents]);
1290
2801
  for (const agent of config.agents) {
2802
+ const rostered = /* @__PURE__ */ new Set();
2803
+ for (const name of hasRoster(agent.multiagent) ? rosterNames(agent.multiagent) : []) {
2804
+ if (name === agent.name) refuse(`agent "${agent.name}": multiagent roster names itself \u2014 a coordinator delegates to a copy of itself with { type: "self" }`);
2805
+ if (config.removed.agents.includes(name)) refuse(`agent "${agent.name}": multiagent roster names "${name}", which this config removes \u2014 drop it from the roster or keep the agent`);
2806
+ if (!agentNames.has(name)) refuse(`agent "${agent.name}": multiagent roster names "${name}", but no agent with that name is declared in this project`);
2807
+ if (rostered.has(name)) refuse(`agent "${agent.name}": multiagent roster names "${name}" twice \u2014 a team names each member once`);
2808
+ rostered.add(name);
2809
+ }
1291
2810
  for (const name of agent.allowed_apps ?? []) {
1292
2811
  if (!appNames.has(name)) {
1293
2812
  refuse(`agent "${agent.name}": allowed_apps names "${name}", but no app with that name is declared in this project`);
@@ -1296,6 +2815,17 @@ function parseProject(input) {
1296
2815
  if (agent.identity !== void 0 && !identityNames.has(agent.identity)) {
1297
2816
  refuse(`agent "${agent.name}": identity names "${agent.identity}", but no identity with that name is declared in this project`);
1298
2817
  }
2818
+ if (agent.handoffs === "team" && !hasRoster(agent.multiagent)) {
2819
+ refuse(`agent "${agent.name}": handoffs is "team", but the agent declares no multiagent roster \u2014 there is no team to hand to`);
2820
+ }
2821
+ const handoffs = /* @__PURE__ */ new Set();
2822
+ for (const name of Array.isArray(agent.handoffs) ? agent.handoffs : []) {
2823
+ if (name === agent.name) refuse(`agent "${agent.name}": handoffs names itself \u2014 an agent cannot send_to_agent its own seat`);
2824
+ if (config.removed.agents.includes(name)) refuse(`agent "${agent.name}": handoffs names "${name}", which this config removes \u2014 drop it from handoffs or keep the agent`);
2825
+ if (!agentNames.has(name)) refuse(`agent "${agent.name}": handoffs names "${name}", but no agent with that name is declared in this project`);
2826
+ if (handoffs.has(name)) refuse(`agent "${agent.name}": handoffs names "${name}" twice`);
2827
+ handoffs.add(name);
2828
+ }
1299
2829
  const crons = /* @__PURE__ */ new Set();
1300
2830
  for (const schedule of agent.schedules ?? []) {
1301
2831
  if (crons.has(schedule.cron)) {
@@ -1345,7 +2875,7 @@ function isMissingModule(error) {
1345
2875
  const code = error?.code;
1346
2876
  return code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND";
1347
2877
  }
1348
- function message(error) {
2878
+ function message2(error) {
1349
2879
  return error instanceof Error ? error.message : String(error);
1350
2880
  }
1351
2881
  function validate(path, raw) {
@@ -1369,7 +2899,7 @@ async function loadConfig(dir) {
1369
2899
  try {
1370
2900
  return validate(path, JSON.parse(readFileSync(path, "utf8")));
1371
2901
  } catch (error) {
1372
- if (error instanceof SyntaxError) return { ok: false, reason: "invalid", message: message(error) };
2902
+ if (error instanceof SyntaxError) return { ok: false, reason: "invalid", message: message2(error) };
1373
2903
  throw error;
1374
2904
  }
1375
2905
  }
@@ -1378,14 +2908,14 @@ async function loadConfig(dir) {
1378
2908
  const { createJiti } = await import("jiti");
1379
2909
  importFile = createJiti(pathToFileURL(path).href).import;
1380
2910
  } catch (error) {
1381
- return { ok: false, reason: "unloadable", message: message(error) };
2911
+ return { ok: false, reason: "unloadable", message: message2(error) };
1382
2912
  }
1383
2913
  let raw;
1384
2914
  try {
1385
2915
  const mod = await importFile(path);
1386
2916
  raw = mod?.default ?? mod;
1387
2917
  } catch (error) {
1388
- return { ok: false, reason: isMissingModule(error) ? "unloadable" : "invalid", message: message(error) };
2918
+ return { ok: false, reason: isMissingModule(error) ? "unloadable" : "invalid", message: message2(error) };
1389
2919
  }
1390
2920
  if (raw === null || typeof raw !== "object") {
1391
2921
  return {
@@ -1441,103 +2971,64 @@ function contentHash(files) {
1441
2971
 
1442
2972
  // src/up.ts
1443
2973
  import { createHash as createHash3 } from "node:crypto";
1444
- import { readFileSync as readFileSync3 } from "node:fs";
1445
- import { resolve as resolve3 } from "node:path";
2974
+ import { readFileSync as readFileSync4 } from "node:fs";
2975
+ import { resolve as resolve4 } from "node:path";
1446
2976
 
1447
- // src/report.ts
1448
- import { ApiError, send } from "@usenaive-sdk/vetta";
1449
- import { z as z17 } from "zod";
1450
- function message2(error) {
1451
- return error instanceof Error ? error.message : String(error);
1452
- }
1453
- async function attempt(into, name, work) {
1454
- let line;
1455
- try {
1456
- line = await work();
1457
- } catch (error) {
1458
- line = { name, action: "refused", reason: message2(error) };
1459
- }
1460
- into.push(line);
1461
- return line;
1462
- }
1463
- var canonical = (value) => {
1464
- if (Array.isArray(value)) return value.map(canonical);
1465
- if (value === null || typeof value !== "object") return value;
1466
- return Object.fromEntries(
1467
- Object.keys(value).sort().map((key) => [key, canonical(value[key])])
1468
- );
1469
- };
1470
- var changed = (declared, live) => declared !== void 0 && JSON.stringify(canonical(declared)) !== JSON.stringify(canonical(live));
1471
- async function absent(work) {
1472
- try {
1473
- return await work;
1474
- } catch (error) {
1475
- if (error instanceof ApiError && error.status === 404) return null;
1476
- throw error;
1477
- }
1478
- }
1479
- var PageShape = z17.object({
1480
- data: z17.array(z17.unknown()),
1481
- has_more: z17.boolean(),
1482
- next_cursor: z17.string().nullable()
1483
- });
1484
- function rowLabel(row) {
1485
- const { object, id } = row ?? {};
1486
- const words = [object, id].filter((one) => typeof one === "string");
1487
- return words.length > 0 ? words.join(" ") : "a row";
1488
- }
1489
- var why = (error) => error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
1490
- async function listAll(config, path, item, skipped, query = {}) {
1491
- const rows = [];
1492
- let after;
1493
- for (; ; ) {
1494
- const response = await send(config, { method: "GET", path, query: { ...query, limit: 100, after } }, "application/json");
1495
- const text = await response.text();
1496
- if (!response.ok) throw new Error(`could not list ${path} (HTTP ${response.status})`);
1497
- const body = PageShape.parse(JSON.parse(text));
1498
- for (const row of body.data) {
1499
- const parsed = item.safeParse(row);
1500
- if (parsed.success) rows.push(parsed.data);
1501
- else skipped.push(`${rowLabel(row)} in ${path} could not be read and was skipped: ${why(parsed.error)}`);
1502
- }
1503
- if (!body.has_more || body.next_cursor === null) return rows;
1504
- after = body.next_cursor;
1505
- }
1506
- }
1507
- async function tombstone(name, current, remove, dryRun) {
1508
- if (!current) return { name, action: "unchanged", reason: "already absent" };
1509
- if (!dryRun) await remove(current.id);
1510
- return { name, action: "deleted", id: current.id };
2977
+ // src/agents.ts
2978
+ var PROJECT_CONTEXT_PREAMBLE = "Before anything else, read the project context with the `project_context` tool. The answers in it are the client's own \u2014 never invent, assume or restate them from memory; what the context does not say, you ask. Use the context's own words for the company, its offer and its audience.";
2979
+ function desiredMetadata(decl, of, current) {
2980
+ if (of.template === void 0) return decl.metadata;
2981
+ return {
2982
+ ...current?.metadata,
2983
+ ...decl.metadata,
2984
+ project: of.project,
2985
+ ...of.blueprint !== void 0 ? { blueprint: of.blueprint } : {},
2986
+ template: of.template,
2987
+ group: of.template
2988
+ };
1511
2989
  }
2990
+ function desiredSystem(decl, of) {
2991
+ if (of.template === void 0) return decl.system;
2992
+ return decl.system === void 0 ? PROJECT_CONTEXT_PREAMBLE : `${PROJECT_CONTEXT_PREAMBLE}
1512
2993
 
1513
- // src/agents.ts
2994
+ ${decl.system}`;
2995
+ }
1514
2996
  function desiredTools(decl, current, ids) {
1515
- if (ids === void 0) return decl.tools;
2997
+ const handoff = decl.handoffs !== void 0 && handoffsEnabled(decl.handoffs);
2998
+ if (ids === void 0 && !handoff) return decl.tools;
1516
2999
  const base = decl.tools ?? current?.tools ?? { default_config: { permission: "allow" }, configs: {} };
1517
3000
  const appsTool = base.configs["apps"] ?? { enabled: true, permission: base.default_config.permission };
3001
+ const allowed = { enabled: true, permission: "allow" };
1518
3002
  return {
1519
3003
  default_config: base.default_config,
1520
3004
  configs: {
1521
3005
  ...base.configs,
1522
- apps: { ...appsTool, config: { ...appsTool.config, allowed_apps: ids } }
3006
+ ...ids !== void 0 ? { apps: { ...appsTool, config: { ...appsTool.config, allowed_apps: ids } } } : {},
3007
+ ...handoff ? { send_to_agent: allowed, list_agents: allowed } : {}
1523
3008
  }
1524
3009
  };
1525
3010
  }
1526
- async function createAgent(client, decl, tools, identityId) {
3011
+ async function createAgent(client, decl, tools, team, identityId, system, metadata) {
1527
3012
  const created = await client.agents.create({
1528
3013
  name: decl.name,
1529
3014
  model: decl.model,
1530
3015
  budget: decl.budget,
1531
3016
  ...decl.description !== void 0 ? { description: decl.description } : {},
1532
- ...decl.system !== void 0 ? { system: decl.system } : {},
3017
+ ...system !== void 0 ? { system } : {},
1533
3018
  ...decl.skills !== void 0 ? { skills: decl.skills } : {},
1534
3019
  ...decl.mcp_servers !== void 0 ? { mcp_servers: decl.mcp_servers } : {},
1535
- ...tools !== void 0 ? { tools } : {}
3020
+ ...tools !== void 0 ? { tools } : {},
3021
+ ...decl.window !== void 0 ? { window: decl.window } : {},
3022
+ ...team !== void 0 ? { multiagent: team } : {},
3023
+ ...decl.handoffs !== void 0 ? { handoffs: decl.handoffs } : {},
3024
+ ...decl.output_schema !== void 0 ? { output_schema: decl.output_schema } : {},
3025
+ ...decl.structured_output_required !== void 0 ? { structured_output_required: decl.structured_output_required } : {},
3026
+ ...metadata !== void 0 ? { metadata } : {}
1536
3027
  });
1537
3028
  if (identityId !== void 0) await client.identities.attach(created.id, identityId);
1538
3029
  return { name: decl.name, action: "created", id: created.id };
1539
3030
  }
1540
- async function upsertAgent(client, decl, existing, ids, dryRun, scoped) {
3031
+ async function upsertAgent(client, decl, existing, ids, dryRun, scoped, of) {
1541
3032
  const unresolved = (decl.allowed_apps ?? []).filter((name) => !ids.apps.has(name));
1542
3033
  const identityId = decl.identity === void 0 ? void 0 : ids.identities.get(decl.identity);
1543
3034
  const missing = unresolved.length > 0 ? `allowed_apps needs app "${unresolved[0]}"` : decl.identity !== void 0 && identityId === void 0 ? `identity needs "${decl.identity}"` : null;
@@ -1547,15 +3038,13 @@ async function upsertAgent(client, decl, existing, ids, dryRun, scoped) {
1547
3038
  const appIds = decl.allowed_apps !== void 0 ? decl.allowed_apps.flatMap((name) => ids.apps.get(name) ?? []) : scoped;
1548
3039
  const current = existing.find((agent) => agent.name === decl.name) ?? null;
1549
3040
  const tools = desiredTools(decl, current, appIds);
1550
- if (!current) return dryRun ? { name: decl.name, action: "created" } : createAgent(client, decl, tools, identityId);
1551
- const patch = {};
1552
- if (changed(decl.model, current.model)) patch.model = decl.model;
1553
- if (changed(decl.budget, current.budget)) patch.budget = decl.budget;
1554
- if (changed(decl.description, current.description)) patch.description = decl.description;
1555
- if (changed(decl.system, current.system)) patch.system = decl.system;
1556
- if (changed(decl.skills, current.skills)) patch.skills = decl.skills;
1557
- if (changed(decl.mcp_servers, current.mcp_servers)) patch.mcp_servers = decl.mcp_servers;
1558
- if (changed(tools, current.tools)) patch.tools = tools;
3041
+ const team = teamWithoutRoster(decl.multiagent);
3042
+ const system = desiredSystem(decl, of);
3043
+ const metadata = desiredMetadata(decl, of, current);
3044
+ if (!current) {
3045
+ return dryRun ? { name: decl.name, action: "created" } : createAgent(client, decl, tools, team, identityId, system, metadata);
3046
+ }
3047
+ const patch = drift(decl, current, tools, team, system, metadata);
1559
3048
  const held = identityId === void 0 ? [] : (await client.identities.held(current.id)).data;
1560
3049
  const grant = identityId === void 0 || held.some((one) => one.id === identityId) ? null : identityId;
1561
3050
  const drifted = Object.keys(patch).length > 0 || missing !== null || grant !== null;
@@ -1566,6 +3055,23 @@ async function upsertAgent(client, decl, existing, ids, dryRun, scoped) {
1566
3055
  }
1567
3056
  return { name: decl.name, action: "updated", id: current.id };
1568
3057
  }
3058
+ function drift(decl, current, tools, team, system, metadata) {
3059
+ const patch = {};
3060
+ if (changed(decl.model, current.model)) patch.model = decl.model;
3061
+ if (changed(decl.budget, current.budget)) patch.budget = decl.budget;
3062
+ if (changed(decl.description, current.description)) patch.description = decl.description;
3063
+ if (changed(system, current.system)) patch.system = system;
3064
+ if (changed(decl.skills, current.skills)) patch.skills = decl.skills;
3065
+ if (changed(decl.mcp_servers, current.mcp_servers)) patch.mcp_servers = decl.mcp_servers;
3066
+ if (changed(tools, current.tools)) patch.tools = tools;
3067
+ if (changed(decl.window, current.window)) patch.window = decl.window;
3068
+ if (team !== void 0 && changed(normaliseMultiagent(team), normaliseMultiagent(current.multiagent))) patch.multiagent = team;
3069
+ if (changed(decl.handoffs, handoffsOf(current))) patch.handoffs = decl.handoffs;
3070
+ if (changed(decl.output_schema, current.output_schema)) patch.output_schema = decl.output_schema;
3071
+ if (changed(decl.structured_output_required, current.structured_output_required)) patch.structured_output_required = decl.structured_output_required;
3072
+ if (changed(metadata, current.metadata)) patch.metadata = metadata;
3073
+ return patch;
3074
+ }
1569
3075
  async function openIntake(client, agent, decl, dryRun) {
1570
3076
  if (dryRun || agent.id === void 0) return { name: agent.name, action: "created" };
1571
3077
  const started = await client.sessions.create({
@@ -1606,10 +3112,15 @@ async function upsertSchedule(client, agent, decl, current, identityIds, dryRun)
1606
3112
  }
1607
3113
  async function reconcileSchedules(client, agent, declared, live, identityIds, dryRun, into) {
1608
3114
  const rows = live.filter((row) => row.agent_id === agent.id);
1609
- for (const decl of declared) {
1610
- const current = rows.find((row) => row.cron === decl.cron);
1611
- await attempt(into, `${agent.name} @ ${decl.cron}`, () => upsertSchedule(client, agent, decl, current, identityIds, dryRun));
1612
- }
3115
+ into.push(
3116
+ ...await eachInFlight(
3117
+ declared,
3118
+ (decl) => line(
3119
+ `${agent.name} @ ${decl.cron}`,
3120
+ () => upsertSchedule(client, agent, decl, rows.find((row) => row.cron === decl.cron), identityIds, dryRun)
3121
+ )
3122
+ )
3123
+ );
1613
3124
  for (const row of rows.filter((row2) => !declared.some((decl) => decl.cron === row2.cron))) {
1614
3125
  const name = `${agent.name} @ ${row.cron}`;
1615
3126
  await attempt(into, name, () => tombstone(name, row, (id) => client.deployments.delete(id), dryRun));
@@ -1618,7 +3129,59 @@ async function reconcileSchedules(client, agent, declared, live, identityIds, dr
1618
3129
 
1619
3130
  // src/apps.ts
1620
3131
  import { createHash as createHash2, randomBytes } from "node:crypto";
1621
- import { resolve as resolve2 } from "node:path";
3132
+ import { resolve as resolve3 } from "node:path";
3133
+
3134
+ // src/buckets.ts
3135
+ async function applyBuckets(client, appId, buckets) {
3136
+ const live = new Set((await client.storage.buckets.list({ app: appId })).data.map((row) => row.name));
3137
+ const missing = buckets.filter((bucket) => !live.has(bucket.name));
3138
+ for (const bucket of missing) await client.storage.buckets.create(bucket.name, { app: appId, public: bucket.public });
3139
+ return { fresh: missing.length, note: `${buckets.length} bucket(s), ${missing.length} new` };
3140
+ }
3141
+
3142
+ // src/migrations.ts
3143
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2 } from "node:fs";
3144
+ import { basename, dirname, extname, join as join3, resolve as resolve2 } from "node:path";
3145
+ function matcher(pattern) {
3146
+ const source = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("[^/]*");
3147
+ const regex = new RegExp(`^${source}$`);
3148
+ return (name) => regex.test(name);
3149
+ }
3150
+ function migrationFiles(patterns, dir) {
3151
+ const seen = /* @__PURE__ */ new Set();
3152
+ const files = [];
3153
+ for (const pattern of patterns) {
3154
+ const full = resolve2(dir, pattern);
3155
+ const paths = pattern.includes("*") ? readdirSync2(dirname(full), { withFileTypes: true }).filter((entry) => entry.isFile() && matcher(basename(full))(entry.name)).map((entry) => join3(dirname(full), entry.name)).sort() : [full];
3156
+ if (paths.length === 0) throw new Error(`migrations "${pattern}" matches no file under ${dir}`);
3157
+ for (const path of paths) {
3158
+ const name = basename(path, extname(path));
3159
+ if (seen.has(name)) continue;
3160
+ seen.add(name);
3161
+ files.push({ name, sql: readFileSync3(path, "utf8") });
3162
+ }
3163
+ }
3164
+ return files;
3165
+ }
3166
+ function declaredMigrations(decl, opts) {
3167
+ if (decl.migrations === void 0) return [];
3168
+ if (opts.trees === void 0) return migrationFiles(decl.migrations, opts.dir);
3169
+ const published = opts.migrations?.[decl.name];
3170
+ if (published === void 0) {
3171
+ throw new Error(
3172
+ `app "${decl.name}" declares migrations, but this apply was handed no published SQL for it. Republish the artifact, or apply them with \`naive up\` from a clone.`
3173
+ );
3174
+ }
3175
+ return published;
3176
+ }
3177
+ async function applyMigrations(client, appId, files) {
3178
+ const applied2 = new Set((await client.database.migrations({ app: appId })).map((row) => row.name));
3179
+ for (const file of files) await client.database.migrate(file.name, file.sql, { app: appId });
3180
+ const fresh = files.filter((file) => !applied2.has(file.name)).length;
3181
+ return { fresh, note: `${files.length} migration(s), ${fresh} new` };
3182
+ }
3183
+
3184
+ // src/apps.ts
1622
3185
  async function findApp(client, name, skipped) {
1623
3186
  const rows = await listAll(client.config, "/v1/apps", AppSchema, skipped, { name });
1624
3187
  return rows.find((app) => app.name === name) ?? null;
@@ -1634,9 +3197,16 @@ var PLATFORM_ENV = {
1634
3197
  api_url: "NAIVE_API_URL",
1635
3198
  /** The identity the install runs as, when the declaration declares exactly one. */
1636
3199
  identity_id: "NAIVE_IDENTITY_ID",
3200
+ /** The install's `project` — the row the app's own context lookup lands on (§31.8). */
3201
+ project: "NAIVE_PROJECT",
3202
+ /** The base URL of the Studio that installed the app, so it can send a signed-out visitor back. */
3203
+ studio_url: "NAIVE_STUDIO_URL",
3204
+ /** The app's own `app_…` id — the one known value learnt from the row, not from the apply. */
3205
+ app_id: "NAIVE_APP_ID",
1637
3206
  /** A scoped organization API key minted for the app that asks for it. */
1638
3207
  api_key: "NAIVE_API_KEY"
1639
3208
  };
3209
+ var DASHBOARD_PASSWORD = "DASHBOARD_PASSWORD";
1640
3210
  function planEnv(decl, opts) {
1641
3211
  if (decl.env === void 0) return [];
1642
3212
  const plan = /* @__PURE__ */ new Map();
@@ -1646,7 +3216,7 @@ function planEnv(decl, opts) {
1646
3216
  continue;
1647
3217
  }
1648
3218
  if ("generate" in value) {
1649
- plan.set(name, { name, invent: async () => generated() });
3219
+ plan.set(name, { name, invent: async () => name === DASHBOARD_PASSWORD ? generatedPassword() : generatedSecret() });
1650
3220
  continue;
1651
3221
  }
1652
3222
  const known = opts.platform?.known[value.from_env];
@@ -1682,7 +3252,31 @@ async function invent(one) {
1682
3252
  );
1683
3253
  }
1684
3254
  }
1685
- var generated = () => randomBytes(32).toString("base64url");
3255
+ var generatedSecret = () => randomBytes(32).toString("base64url");
3256
+ var PASSWORD_ALPHABET = "abcdefghjkmnpqrstuvwxyz23456789";
3257
+ var PASSWORD_GROUPS = 4;
3258
+ var PASSWORD_GROUP_LENGTH = 4;
3259
+ function generatedPassword() {
3260
+ const limit = 256 - 256 % PASSWORD_ALPHABET.length;
3261
+ const groups = [];
3262
+ let group = "";
3263
+ while (groups.length < PASSWORD_GROUPS) {
3264
+ for (const byte of randomBytes(PASSWORD_GROUPS * PASSWORD_GROUP_LENGTH * 2)) {
3265
+ if (byte >= limit) continue;
3266
+ group += PASSWORD_ALPHABET[byte % PASSWORD_ALPHABET.length];
3267
+ if (group.length === PASSWORD_GROUP_LENGTH) {
3268
+ groups.push(group);
3269
+ group = "";
3270
+ if (groups.length === PASSWORD_GROUPS) break;
3271
+ }
3272
+ }
3273
+ }
3274
+ return groups.join("-");
3275
+ }
3276
+ function withAppId(env, decl, opts, id) {
3277
+ if (decl.env === void 0 || opts.platform === void 0) return env;
3278
+ return { ...env, [PLATFORM_ENV.app_id]: id };
3279
+ }
1686
3280
  var EMPTY = /* @__PURE__ */ new Set();
1687
3281
  function secretHash(name, value) {
1688
3282
  return createHash2("sha256").update(`${name}\0`).update(value).digest("hex");
@@ -1696,13 +3290,14 @@ function builtTree(decl, opts) {
1696
3290
  `app "${decl.name}" declares deploy_dir "${decl.deploy_dir}", but this apply was handed no built tree for it` + (failed === void 0 ? "" : `: ${failed}`)
1697
3291
  );
1698
3292
  }
1699
- const files = collectFiles(hosted?.[decl.name] ?? resolve2(opts.dir, decl.deploy_dir));
3293
+ const files = collectFiles(hosted?.[decl.name] ?? resolve3(opts.dir, decl.deploy_dir));
1700
3294
  return { files, hash: contentHash(files) };
1701
3295
  }
1702
3296
  async function writeApp(client, id, env, tree) {
1703
- for (const [name, value] of Object.entries(env)) {
1704
- await client.apps.setSecret(id, { name, value, value_hash: secretHash(name, value) });
1705
- }
3297
+ await eachInFlight(
3298
+ Object.entries(env),
3299
+ ([name, value]) => client.apps.setSecret(id, { name, value, value_hash: secretHash(name, value) })
3300
+ );
1706
3301
  if (!tree) return;
1707
3302
  await client.apps.deploy(id, tree.files, tree.hash);
1708
3303
  await settleDeploy(client, id);
@@ -1713,10 +3308,10 @@ async function settleDeploy(client, id) {
1713
3308
  for (let poll = 0; poll < SETTLE_TRIES; poll++) {
1714
3309
  const newest = (await client.apps.deployments(id, { limit: 1 })).data[0];
1715
3310
  if (newest === void 0 || newest.status === "ready" || newest.status === "error") return;
1716
- await new Promise((resolve4) => setTimeout(resolve4, SETTLE_MS));
3311
+ await new Promise((resolve5) => setTimeout(resolve5, SETTLE_MS));
1717
3312
  }
1718
3313
  }
1719
- async function createApp(client, decl, env, tree, opts, appIds) {
3314
+ async function createApp(client, decl, env, tree, migrations, opts, appIds) {
1720
3315
  const created = await ensureApp(client, {
1721
3316
  name: decl.name,
1722
3317
  type: decl.type,
@@ -1725,14 +3320,26 @@ async function createApp(client, decl, env, tree, opts, appIds) {
1725
3320
  ...decl.mcp !== void 0 ? { mcp: decl.mcp } : {}
1726
3321
  });
1727
3322
  appIds.set(decl.name, created.id);
1728
- await writeApp(client, created.id, env, tree);
1729
- return { name: decl.name, action: "created", id: created.id, url: (await client.apps.get(created.id)).url };
3323
+ await writeApp(client, created.id, withAppId(env, decl, opts, created.id), tree);
3324
+ const notes = [];
3325
+ if (migrations.length > 0) notes.push((await applyMigrations(client, created.id, migrations)).note);
3326
+ if (decl.buckets !== void 0) notes.push((await applyBuckets(client, created.id, decl.buckets)).note);
3327
+ return {
3328
+ name: decl.name,
3329
+ action: "created",
3330
+ id: created.id,
3331
+ url: (await client.apps.get(created.id)).url,
3332
+ ...notes.length > 0 ? { reason: notes.join("; ") } : {}
3333
+ };
1730
3334
  }
1731
3335
  async function upsertApp(client, decl, opts, appIds) {
1732
3336
  const declared = planEnv(decl, opts);
1733
- const names = declared.map((one) => one.name);
3337
+ const writesAppId = decl.env !== void 0 && opts.platform !== void 0;
3338
+ const names = [...declared.map((one) => one.name), ...writesAppId ? [PLATFORM_ENV.app_id] : []];
1734
3339
  const note = (notes) => notes.length > 0 ? { reason: notes.join("; ") } : {};
1735
3340
  const mcpPlan = decl.mcp !== void 0 ? [`will set mcp ${decl.mcp}`] : [];
3341
+ const migrationPlan = decl.migrations !== void 0 ? [`will apply migrations ${decl.migrations.join(", ")}`] : [];
3342
+ const bucketPlan = decl.buckets !== void 0 ? [`will ensure buckets ${decl.buckets.map((one) => one.name).join(", ")}`] : [];
1736
3343
  const current = await findApp(client, decl.name, opts.skipped);
1737
3344
  if (!current) {
1738
3345
  const willSet = names.length > 0 ? [`will set ${names.join(", ")}`] : [];
@@ -1740,11 +3347,11 @@ async function upsertApp(client, decl, opts, appIds) {
1740
3347
  return {
1741
3348
  name: decl.name,
1742
3349
  action: "created",
1743
- ...note([...willSet, ...mcpPlan, ...decl.deploy_dir ? [`will deploy ${decl.deploy_dir}`] : []])
3350
+ ...note([...willSet, ...mcpPlan, ...decl.deploy_dir ? [`will deploy ${decl.deploy_dir}`] : [], ...migrationPlan, ...bucketPlan])
1744
3351
  };
1745
3352
  }
1746
- const { values } = await materialize(declared, EMPTY, false);
1747
- return createApp(client, decl, values, builtTree(decl, opts), opts, appIds);
3353
+ const { values: values2 } = await materialize(declared, EMPTY, false);
3354
+ return createApp(client, decl, values2, builtTree(decl, opts), declaredMigrations(decl, opts), opts, appIds);
1748
3355
  }
1749
3356
  const owned = current.project === opts.project;
1750
3357
  if (!owned && !opts.adopt) {
@@ -1773,7 +3380,8 @@ async function upsertApp(client, decl, opts, appIds) {
1773
3380
  const managed = new Set(names);
1774
3381
  const unmanaged = live.map((secret) => secret.name).filter((name) => !managed.has(name));
1775
3382
  const held = new Map(live.map((secret) => [secret.name, secret.value_hash]));
1776
- const { values: env, unmade } = await materialize(declared, new Set(held.keys()), opts.dryRun);
3383
+ const { values, unmade } = await materialize(declared, new Set(held.keys()), opts.dryRun);
3384
+ const env = withAppId(values, decl, opts, current.id);
1777
3385
  const staleEnv = Object.fromEntries(
1778
3386
  Object.entries(env).filter(([name, value]) => held.get(name) !== secretHash(name, value))
1779
3387
  );
@@ -1786,15 +3394,28 @@ async function upsertApp(client, decl, opts, appIds) {
1786
3394
  const tree = builtTree(decl, opts);
1787
3395
  const newest = tree ? (await client.apps.deployments(current.id, { limit: 1 })).data[0] : void 0;
1788
3396
  const stale = tree !== null && newest?.content_hash !== tree.hash;
1789
- const action = !owned || drifted || stale || staleNames.length > 0 ? "updated" : "unchanged";
3397
+ let action = !owned || drifted || stale || staleNames.length > 0 ? "updated" : "unchanged";
1790
3398
  const plan = opts.dryRun ? [
1791
3399
  ...staleNames.length > 0 ? [`will set ${staleNames.join(", ")}`] : [],
1792
3400
  ..."mcp" in patch ? patch.mcp === null ? ["will clear mcp"] : mcpPlan : [],
1793
- ...decl.deploy_dir ? [stale ? `will deploy ${decl.deploy_dir}` : `${decl.deploy_dir} unchanged`] : []
3401
+ ...decl.deploy_dir ? [stale ? `will deploy ${decl.deploy_dir}` : `${decl.deploy_dir} unchanged`] : [],
3402
+ ...migrationPlan,
3403
+ ...bucketPlan
1794
3404
  ] : [];
1795
3405
  if (!opts.dryRun) {
1796
3406
  if (drifted) await client.apps.update(current.id, { ...patch, project: opts.project });
1797
3407
  await writeApp(client, current.id, staleEnv, stale ? tree : null);
3408
+ const migrations = declaredMigrations(decl, opts);
3409
+ if (migrations.length > 0) {
3410
+ const { fresh, note: note2 } = await applyMigrations(client, current.id, migrations);
3411
+ if (fresh > 0) action = "updated";
3412
+ plan.push(note2);
3413
+ }
3414
+ if (decl.buckets !== void 0) {
3415
+ const { fresh, note: note2 } = await applyBuckets(client, current.id, decl.buckets);
3416
+ if (fresh > 0) action = "updated";
3417
+ plan.push(note2);
3418
+ }
1798
3419
  }
1799
3420
  return {
1800
3421
  name: decl.name,
@@ -1806,6 +3427,106 @@ async function upsertApp(client, decl, opts, appIds) {
1806
3427
  };
1807
3428
  }
1808
3429
 
3430
+ // src/reconcile-cards.ts
3431
+ var LIVE = /* @__PURE__ */ new Set(["pending_payment", "issuing", "active"]);
3432
+ var nameOf = (identity, decl) => `${identity} / card ${decl.label}`;
3433
+ async function upsertCard(client, identityId, decl, live, dryRun, name) {
3434
+ const current = live.find((card) => card.label === decl.label && LIVE.has(card.status));
3435
+ if (current) return { name, action: "unchanged", id: current.id };
3436
+ const spent = live.find((card) => card.label === decl.label);
3437
+ if (spent) {
3438
+ return { name, action: "refused", id: spent.id, reason: `a card labelled "${decl.label}" is ${spent.status}; issue a replacement by hand` };
3439
+ }
3440
+ if (dryRun) {
3441
+ const quote = await client.identities.cards.quote(identityId, { load_micro_usd: decl.load_micro_usd });
3442
+ const price = decimalStringFromMicro(microUsd(quote.price_micro_usd));
3443
+ return { name, action: "created", reason: `would debit ${price} USD of credits (${quote.price_micro_usd} micro-USD)` };
3444
+ }
3445
+ const issued = await client.identities.cards.issue(identityId, decl);
3446
+ return { name, action: "created", id: issued.id };
3447
+ }
3448
+ async function reconcileCards(client, identity, identityId, decls, dryRun, into, skipped) {
3449
+ if (decls.length === 0) return;
3450
+ if (identityId === void 0) {
3451
+ for (const decl of decls) {
3452
+ into.push(
3453
+ dryRun ? { name: nameOf(identity, decl), action: "created", reason: "quoted once the identity exists; issuing debits credits" } : { name: nameOf(identity, decl), action: "refused", reason: `identity "${identity}" was not provisioned in this run` }
3454
+ );
3455
+ }
3456
+ return;
3457
+ }
3458
+ const live = await listAll(client.config, `/v1/identities/${identityId}/cards`, CardSchema, skipped);
3459
+ for (const decl of decls) {
3460
+ const name = nameOf(identity, decl);
3461
+ await attempt(into, name, () => upsertCard(client, identityId, decl, live, dryRun, name));
3462
+ }
3463
+ }
3464
+
3465
+ // src/reconcile-legal.ts
3466
+ var FILE = "vetta identity legal form";
3467
+ var VERIFY = "vetta identity legal verify";
3468
+ function latest(cases) {
3469
+ return [...cases].sort((a, b) => b.created_at.localeCompare(a.created_at))[0];
3470
+ }
3471
+ function match(decl, companies) {
3472
+ const wanted = new Set(
3473
+ decl.name_options.map((raw) => canonicalName(raw).name)
3474
+ );
3475
+ return companies.find(
3476
+ (c) => c.state === decl.state && c.name_options.some((o) => wanted.has(o.name))
3477
+ );
3478
+ }
3479
+ function report(decl, company, verification) {
3480
+ const name = decl.label;
3481
+ if (company !== void 0) {
3482
+ const reason = company.status === "failed" ? `failed: ${company.failure_reason ?? "the filing was rejected"}` : company.status;
3483
+ return {
3484
+ name,
3485
+ action: company.status === "failed" ? "refused" : "unchanged",
3486
+ id: company.id,
3487
+ reason
3488
+ };
3489
+ }
3490
+ if (verification === void 0)
3491
+ return {
3492
+ name,
3493
+ action: "refused",
3494
+ reason: `no verification case \u2014 a person must start one with \`${VERIFY}\``
3495
+ };
3496
+ if (verification.status === "verified") {
3497
+ return {
3498
+ name,
3499
+ action: "refused",
3500
+ id: verification.id,
3501
+ reason: `verified; the fee is debited from credits, so a person files it with \`${FILE}\``
3502
+ };
3503
+ }
3504
+ return {
3505
+ name,
3506
+ action: "refused",
3507
+ id: verification.id,
3508
+ reason: `verification ${verification.status}; nothing to file yet`
3509
+ };
3510
+ }
3511
+ async function reportLegal(client, decls, identityId, skipped) {
3512
+ if (decls.length === 0) return [];
3513
+ if (identityId === void 0)
3514
+ return decls.map((decl) => report(decl, void 0, void 0));
3515
+ const base = `/v1/identities/${identityId}/legal`;
3516
+ const [companies, cases] = await Promise.all([
3517
+ listAll(client.config, `${base}/companies`, LegalCompanySchema, skipped),
3518
+ listAll(
3519
+ client.config,
3520
+ `${base}/verifications`,
3521
+ LegalVerificationSchema,
3522
+ skipped
3523
+ )
3524
+ ]);
3525
+ return decls.map(
3526
+ (decl) => report(decl, match(decl, companies), latest(cases))
3527
+ );
3528
+ }
3529
+
1809
3530
  // src/vaults.ts
1810
3531
  async function upsertVault(client, decl, existing, identityIds, dryRun) {
1811
3532
  const identityId = decl.identity === void 0 ? void 0 : identityIds.get(decl.identity);
@@ -1851,23 +3572,94 @@ async function upsertCredential(client, vault, decl, existing, dryRun, from) {
1851
3572
  return { name, action: "created", id: created.id };
1852
3573
  }
1853
3574
 
3575
+ // src/reconcile-wallet.ts
3576
+ var declaredPolicy = (decl, live) => Object.fromEntries(
3577
+ Object.entries(decl.policy ?? {}).filter(([key, value]) => value !== void 0 && changed(value, live[key]))
3578
+ );
3579
+ var walletName = (identity, decl) => decl.network === void 0 || decl.network === DEFAULT_WALLET_NETWORK ? `${identity} / wallet` : `${identity} / wallet ${decl.network}`;
3580
+ async function reconcileWallet(client, identity, decl, dryRun) {
3581
+ const name = walletName(identity.name, decl);
3582
+ const network = decl.network ?? DEFAULT_WALLET_NETWORK;
3583
+ if (identity.id === void 0) {
3584
+ if (dryRun) return { name, action: "created" };
3585
+ return { name, action: "refused", reason: `identity "${identity.name}" was not provisioned in this run` };
3586
+ }
3587
+ const current = await absent(client.identities.wallet.get(identity.id, { network }));
3588
+ if (!current) {
3589
+ if (dryRun) return { name, action: "created" };
3590
+ const created = await client.identities.wallet.create(identity.id, decl);
3591
+ return { name, action: "created", id: created.id };
3592
+ }
3593
+ const patch = declaredPolicy(decl, current.policy);
3594
+ if (Object.keys(patch).length === 0) return { name, action: "unchanged", id: current.id };
3595
+ if (current.status !== "active") {
3596
+ return { name, action: "refused", id: current.id, reason: `wallet is ${current.status}; unfreeze it on the platform before changing its policy` };
3597
+ }
3598
+ if (!dryRun) await client.identities.wallet.updatePolicy(identity.id, patch, { network });
3599
+ return { name, action: "updated", id: current.id };
3600
+ }
3601
+
1854
3602
  // src/up.ts
3603
+ var SERVING_TRIES = 90;
3604
+ var SERVING_MS = 2e3;
3605
+ async function awaitServing(addresses, probe) {
3606
+ await Promise.all(
3607
+ addresses.map(async (address) => {
3608
+ for (let poll = 0; poll < SERVING_TRIES; poll++) {
3609
+ try {
3610
+ const { status } = await probe(address, { method: "GET" });
3611
+ if (status < 500 && status !== 404) return;
3612
+ } catch {
3613
+ }
3614
+ await new Promise((resolve5) => setTimeout(resolve5, SERVING_MS));
3615
+ }
3616
+ })
3617
+ );
3618
+ }
3619
+ function selectCrew(config, select) {
3620
+ if (select === void 0) return { apps: config.apps, agents: config.agents, deselected: [] };
3621
+ const named = (kind, names) => names.map((name) => `${kind} "${name}"`);
3622
+ const unknown = [
3623
+ ...named("app", select.apps.filter((name) => !config.apps.some((decl) => decl.name === name))),
3624
+ ...named("agent", select.agents.filter((name) => !config.agents.some((decl) => decl.name === name)))
3625
+ ];
3626
+ if (unknown.length > 0) {
3627
+ throw new BlueprintRefusal(`selection names ${unknown.join(", ")}, which this declaration does not declare`);
3628
+ }
3629
+ const apps = config.apps.filter((decl) => select.apps.includes(decl.name));
3630
+ const agents = config.agents.filter((decl) => select.agents.includes(decl.name));
3631
+ const missing = [
3632
+ ...named("app", config.apps.filter((decl) => decl.required === true && !apps.includes(decl)).map((decl) => decl.name)),
3633
+ ...named("agent", config.agents.filter((decl) => decl.required === true && !agents.includes(decl)).map((decl) => decl.name))
3634
+ ];
3635
+ if (missing.length > 0) {
3636
+ throw new BlueprintRefusal(
3637
+ `selection leaves out ${missing.join(", ")}, which this template requires \u2014 ${missing.length === 1 ? "it" : "they"} cannot be deselected`
3638
+ );
3639
+ }
3640
+ const deselected = [
3641
+ ...config.apps.filter((decl) => !apps.includes(decl)).map((decl) => decl.name),
3642
+ ...config.agents.filter((decl) => !agents.includes(decl)).map((decl) => decl.name)
3643
+ ];
3644
+ return { apps, agents, deselected };
3645
+ }
1855
3646
  function identityEnv(config, ids) {
1856
3647
  if (config.identities.length !== 1) return {};
1857
3648
  const id = ids.get(config.identities[0].name);
1858
3649
  return id === void 0 ? {} : { [PLATFORM_ENV.identity_id]: id };
1859
3650
  }
1860
3651
  async function upsertSkill(client, decl, opts) {
1861
- if (opts.hosted) {
3652
+ const inline = opts.skills[decl.slug];
3653
+ if (opts.hosted && inline === void 0) {
1862
3654
  throw new Error(
1863
3655
  `skill "${decl.slug}" is declared as the file "${decl.file}", and a hosted apply has no checkout to read it from. Push it with \`naive up\` from a clone, or publish it as part of the blueprint.`
1864
3656
  );
1865
3657
  }
1866
- const content = readFileSync3(resolve3(opts.dir, decl.file), "utf8");
3658
+ const content = inline ?? readFileSync4(resolve4(opts.dir, decl.file), "utf8");
1867
3659
  const sha256 = createHash3("sha256").update(content).digest("hex");
1868
3660
  const current = await absent(client.skills.get(decl.slug));
1869
- const latest = current ? (await client.skills.listVersions(decl.slug, { limit: 1 })).data[0] : void 0;
1870
- const action = !current ? "created" : latest?.sha256 === sha256 ? "unchanged" : "updated";
3661
+ const latest2 = current ? (await client.skills.listVersions(decl.slug, { limit: 1 })).data[0] : void 0;
3662
+ const action = !current ? "created" : latest2?.sha256 === sha256 ? "unchanged" : "updated";
1871
3663
  if (action === "unchanged" || opts.dryRun) {
1872
3664
  return { name: decl.slug, action, ...current ? { id: current.id } : {} };
1873
3665
  }
@@ -1890,14 +3682,14 @@ async function upsertIdentity(client, decl, dryRun, identityIds) {
1890
3682
  if (drifted && !dryRun) await client.identities.update(current.id, { description: decl.description ?? null });
1891
3683
  return { name: decl.name, action: drifted ? "updated" : "unchanged", id: current.id };
1892
3684
  }
1893
- async function applyRemoved(client, removed, live, owner, report) {
1894
- const dryRun = report.dry_run;
3685
+ async function applyRemoved(client, removed, live, owner, report2) {
3686
+ const dryRun = report2.dry_run;
1895
3687
  for (const name of removed.agents) {
1896
3688
  const current = live.agents.find((agent) => agent.name === name);
1897
- await attempt(report.agents, name, () => tombstone(name, current, (id) => client.agents.delete(id), dryRun));
3689
+ await attempt(report2.agents, name, () => tombstone(name, current, (id) => client.agents.delete(id), dryRun));
1898
3690
  }
1899
3691
  for (const name of removed.apps) {
1900
- await attempt(report.apps, name, async () => {
3692
+ await attempt(report2.apps, name, async () => {
1901
3693
  const current = await findApp(client, name, owner.skipped);
1902
3694
  if (current !== null && current.project !== owner.project && !owner.adopt) {
1903
3695
  return { name, action: "refused", id: current.id, reason: ownershipRefusal(name, owner.project, current.project) };
@@ -1907,18 +3699,18 @@ async function applyRemoved(client, removed, live, owner, report) {
1907
3699
  }
1908
3700
  for (const slug of removed.skills) {
1909
3701
  await attempt(
1910
- report.skills,
3702
+ report2.skills,
1911
3703
  slug,
1912
3704
  async () => tombstone(slug, await absent(client.skills.get(slug)), (id) => client.skills.delete(id), dryRun)
1913
3705
  );
1914
3706
  }
1915
3707
  for (const name of removed.vaults) {
1916
3708
  const current = live.vaults.find((vault) => vault.display_name === name);
1917
- await attempt(report.vaults, name, () => tombstone(name, current, (id) => client.vaults.delete(id), dryRun));
3709
+ await attempt(report2.vaults, name, () => tombstone(name, current, (id) => client.vaults.delete(id), dryRun));
1918
3710
  }
1919
3711
  for (const name of removed.identities) {
1920
3712
  await attempt(
1921
- report.identities,
3713
+ report2.identities,
1922
3714
  name,
1923
3715
  async () => tombstone(name, await absent(client.identities.get(name)), (id) => client.identities.delete(id), dryRun)
1924
3716
  );
@@ -1935,81 +3727,136 @@ function reportKept(config, live, into) {
1935
3727
  if (current !== void 0) into.push({ name, action: "unchanged", id: current.id, reason: `not declared by ${by}; kept, never deleted` });
1936
3728
  }
1937
3729
  }
3730
+ function reaches(decl, app) {
3731
+ return Object.entries(decl.tools?.configs ?? {}).some(
3732
+ ([name, one]) => name.startsWith(`${app}.`) && one.enabled && one.permission !== "deny"
3733
+ );
3734
+ }
1938
3735
  async function up(config, client, opts = {}) {
1939
3736
  const dryRun = opts.dry_run ?? false;
1940
3737
  const dir = opts.dir ?? process.cwd();
1941
3738
  const ids = { apps: /* @__PURE__ */ new Map(), identities: /* @__PURE__ */ new Map() };
1942
- const report = { dry_run: dryRun, ...applied(config), skills: [], identities: [], vaults: [], apps: [], agents: [], schedules: [], intake: [], skipped: [] };
1943
- const owner = { project: config.name, adopt: opts.adopt ?? false, skipped: report.skipped };
3739
+ const taken = selectCrew(config, opts.select);
3740
+ const report2 = { dry_run: dryRun, ...applied(config), skills: [], identities: [], vaults: [], apps: [], agents: [], schedules: [], intake: [], skipped: [], deselected: taken.deselected };
3741
+ const owner = { project: config.name, adopt: opts.adopt ?? false, skipped: report2.skipped };
1944
3742
  const hosted = opts.trees !== void 0;
1945
3743
  for (const decl of config.skills) {
1946
- await attempt(report.skills, decl.slug, () => upsertSkill(client, decl, { dryRun, dir, hosted }));
3744
+ await attempt(report2.skills, decl.slug, () => upsertSkill(client, decl, { dryRun, dir, hosted, skills: opts.skills ?? {} }));
1947
3745
  }
1948
3746
  for (const decl of config.identities) {
1949
- await attempt(report.identities, decl.name, () => upsertIdentity(client, decl, dryRun, ids.identities));
3747
+ await attempt(report2.identities, decl.name, () => upsertIdentity(client, decl, dryRun, ids.identities));
3748
+ const identityId = ids.identities.get(decl.name);
3749
+ report2.identities.push(...await reportLegal(client, decl.legal ?? [], identityId, report2.skipped));
3750
+ for (const wallet of decl.wallet === void 0 ? [] : Array.isArray(decl.wallet) ? decl.wallet : [decl.wallet]) {
3751
+ await attempt(
3752
+ report2.identities,
3753
+ walletName(decl.name, wallet),
3754
+ () => reconcileWallet(client, { name: decl.name, id: identityId }, wallet, dryRun)
3755
+ );
3756
+ }
3757
+ await reconcileCards(client, decl.name, identityId, decl.cards ?? [], dryRun, report2.identities, report2.skipped);
1950
3758
  }
1951
- const vaults = config.vaults.length > 0 || config.removed.vaults.length > 0 ? await listAll(client.config, "/v1/vaults", VaultSchema, report.skipped) : [];
3759
+ const vaults = config.vaults.length > 0 || config.removed.vaults.length > 0 ? await listAll(client.config, "/v1/vaults", VaultSchema, report2.skipped) : [];
1952
3760
  for (const decl of config.vaults) {
1953
- const line = await attempt(report.vaults, decl.name, () => upsertVault(client, decl, vaults, ids.identities, dryRun));
1954
- const vaultId = line.action === "unchanged" ? line.id : void 0;
1955
- const rows = vaultId === void 0 ? [] : await listAll(client.config, `/v1/vaults/${vaultId}/credentials`, VaultCredentialSchema, report.skipped);
3761
+ const line2 = await attempt(report2.vaults, decl.name, () => upsertVault(client, decl, vaults, ids.identities, dryRun));
3762
+ const vaultId = line2.action === "unchanged" ? line2.id : void 0;
3763
+ const rows = vaultId === void 0 ? [] : await listAll(client.config, `/v1/vaults/${vaultId}/credentials`, VaultCredentialSchema, report2.skipped);
1956
3764
  for (const cred of decl.credentials) {
1957
3765
  await attempt(
1958
- report.vaults,
3766
+ report2.vaults,
1959
3767
  `${decl.name} / ${cred.key}`,
1960
- () => upsertCredential(client, line, cred, rows, dryRun, opts.env ?? process.env)
3768
+ () => upsertCredential(client, line2, cred, rows, dryRun, opts.env ?? process.env)
1961
3769
  );
1962
3770
  }
1963
3771
  }
1964
3772
  const platform = opts.platform && {
1965
3773
  ...opts.platform,
1966
- known: { ...opts.platform.known, ...identityEnv(config, ids.identities) }
3774
+ known: {
3775
+ ...opts.platform.known,
3776
+ [PLATFORM_ENV.project]: opts.project ?? config.name,
3777
+ ...identityEnv(config, ids.identities)
3778
+ }
1967
3779
  };
1968
- for (const decl of config.apps) {
1969
- await attempt(
1970
- report.apps,
1971
- decl.name,
1972
- () => upsertApp(
1973
- client,
1974
- decl,
1975
- { ...owner, dryRun, dir, trees: opts.trees, tree_errors: opts.tree_errors, env: opts.env ?? process.env, platform },
1976
- ids.apps
3780
+ report2.apps.push(
3781
+ ...await eachInFlight(
3782
+ taken.apps,
3783
+ (decl) => line(
3784
+ decl.name,
3785
+ () => upsertApp(
3786
+ client,
3787
+ decl,
3788
+ {
3789
+ ...owner,
3790
+ dryRun,
3791
+ dir,
3792
+ trees: opts.trees,
3793
+ tree_errors: opts.tree_errors,
3794
+ migrations: opts.migrations,
3795
+ env: opts.env ?? process.env,
3796
+ platform
3797
+ },
3798
+ ids.apps
3799
+ )
1977
3800
  )
1978
- );
1979
- }
1980
- const existing = config.agents.length > 0 || config.removed.agents.length > 0 || config.kept.agents.length > 0 ? await listAll(client.config, "/v1/agents", AgentSchema, report.skipped) : [];
1981
- const deployments = config.agents.some((decl) => decl.schedules !== void 0) ? await listAll(client.config, "/v1/deployments", DeploymentSchema, report.skipped) : [];
3801
+ )
3802
+ );
3803
+ const existing = taken.agents.length > 0 || config.removed.agents.length > 0 || config.kept.agents.length > 0 ? await listAll(client.config, "/v1/agents", AgentSchema, report2.skipped) : [];
3804
+ const deployments = taken.agents.some((decl) => decl.schedules !== void 0) ? await listAll(client.config, "/v1/deployments", DeploymentSchema, report2.skipped) : [];
1982
3805
  const scoped = config.apps.length > 0 ? [...ids.apps.values()] : void 0;
1983
- const briefed = [];
1984
- for (const decl of config.agents) {
1985
- const line = await attempt(
1986
- report.agents,
1987
- decl.name,
1988
- () => upsertAgent(client, decl, existing, ids, dryRun, scoped)
1989
- );
3806
+ const crew = await eachInFlight(taken.agents, async (decl) => {
3807
+ const agent = await line(decl.name, () => upsertAgent(client, decl, existing, ids, dryRun, scoped, { project: opts.project ?? config.name, ...applied(config) }));
3808
+ const schedules = [];
1990
3809
  if (decl.schedules !== void 0) {
1991
- await reconcileSchedules(client, line, decl.schedules, deployments, ids.identities, dryRun, report.schedules);
3810
+ await reconcileSchedules(client, agent, decl.schedules, deployments, ids.identities, dryRun, schedules);
1992
3811
  }
1993
- if (decl.intake !== void 0 && line.action === "created") briefed.push([line, decl.intake]);
3812
+ return { decl, agent, schedules };
3813
+ });
3814
+ const briefed = [];
3815
+ const unbuilt = dryRun ? [] : config.apps.map((decl) => decl.name).filter((name) => !ids.apps.has(name));
3816
+ for (const { decl, agent, schedules } of crew) {
3817
+ report2.agents.push(agent);
3818
+ report2.schedules.push(...schedules);
3819
+ if (decl.intake === void 0 || agent.action !== "created") continue;
3820
+ const needs = unbuilt.find((app) => reaches(decl, app));
3821
+ if (needs === void 0) briefed.push([agent, decl.intake]);
3822
+ else report2.intake.push({ name: decl.name, action: "refused", reason: `needs app "${needs}", which was not provisioned in this run` });
1994
3823
  }
1995
- reportKept(config, existing, report.agents);
1996
- await applyRemoved(client, config.removed, { agents: existing, vaults }, owner, report);
1997
- for (const [line, decl] of briefed) {
1998
- await attempt(report.intake, line.name, () => openIntake(client, line, decl, dryRun));
3824
+ const agentIds = new Map(existing.map((agent) => [agent.name, agent.id]));
3825
+ for (const line2 of report2.agents) if (line2.id !== void 0) agentIds.set(line2.name, line2.id);
3826
+ for (const decl of config.agents) {
3827
+ if (hasRoster(decl.multiagent)) await patchRoster(client, decl, decl.multiagent, existing, agentIds, dryRun, report2.agents);
3828
+ }
3829
+ reportKept(config, existing, report2.agents);
3830
+ await applyRemoved(client, config.removed, { agents: existing, vaults }, owner, report2);
3831
+ if (briefed.length > 0 && !dryRun) {
3832
+ const serving = taken.apps.flatMap((decl) => {
3833
+ const url = report2.apps.find((one) => one.name === decl.name)?.url;
3834
+ return decl.mcp !== void 0 && typeof url === "string" ? [url + decl.mcp] : [];
3835
+ });
3836
+ await awaitServing(serving, opts.probe ?? globalThis.fetch);
1999
3837
  }
2000
- return report;
3838
+ report2.intake.push(
3839
+ ...await eachInFlight(briefed, ([agent, decl]) => line(agent.name, () => openIntake(client, agent, decl, dryRun)))
3840
+ );
3841
+ return report2;
2001
3842
  }
2002
3843
  export {
2003
3844
  BLUEPRINTS,
2004
3845
  BlueprintRefusal,
2005
3846
  CONFIG_CANDIDATES,
2006
3847
  PLATFORM_ENV,
3848
+ PROJECT_CONTEXT_PREAMBLE,
2007
3849
  chooseTemplate,
2008
3850
  collectFiles,
2009
3851
  contentHash,
2010
3852
  defineProject,
3853
+ generatedPassword,
3854
+ generatedSecret,
3855
+ hasRoster,
2011
3856
  loadConfig,
3857
+ migrationFiles,
2012
3858
  parseProject,
3859
+ selectCrew,
2013
3860
  up
2014
3861
  };
2015
3862
  //# sourceMappingURL=index.js.map