@usenaive-sdk/blueprints 0.1.0 → 0.2.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
@@ -92,7 +92,8 @@ var AUDIT_ACTIONS_P3 = [
92
92
  "app.secret_deleted",
93
93
  "app.domain_connected",
94
94
  "app.domain_disconnected",
95
- "app.db_queried"
95
+ "app.db_queried",
96
+ "blueprint.installed"
96
97
  ];
97
98
  var AUDIT_ACTIONS = [
98
99
  ...AUDIT_ACTIONS_SHIPPED,
@@ -239,7 +240,16 @@ var StopReasonSchema = z4.enum([
239
240
  * Distinct from `awaiting_input`: nobody is being asked for anything, so no human can unblock it —
240
241
  * the child's completion does. Reporting `idle`/`end_turn` here would claim the task was finished.
241
242
  */
242
- "awaiting_delegation"
243
+ "awaiting_delegation",
244
+ /**
245
+ * The turn yielded because the agent asked the operator a question and cannot proceed without
246
+ * the answer (§7.1). Distinct from all three neighbours, and none can stand in: `awaiting_input`
247
+ * says only that there is nothing left to do, so a client reading it renders a bare composer
248
+ * where a specific answer is owed; `awaiting_approval` says the block is a tool call to allow or
249
+ * deny, and its route carries a verb and no payload; `awaiting_delegation` says no human can
250
+ * unblock it at all. What is being asked is the parked `pending_actions` row.
251
+ */
252
+ "awaiting_answer"
243
253
  ]);
244
254
  var ThreadRoleSchema = z4.enum(["coordinator", "member"]);
245
255
  var MessageSchema = z4.object({
@@ -252,10 +262,57 @@ var ToolResultSchema = z4.object({
252
262
  is_error: z4.boolean().optional(),
253
263
  content: z4.string().optional()
254
264
  });
265
+ var QuestionFieldBase = {
266
+ key: z4.string().min(1).max(64),
267
+ label: z4.string().min(1).max(200),
268
+ help: z4.string().max(500).optional()
269
+ };
270
+ var QuestionFieldSchema = z4.discriminatedUnion("type", [
271
+ z4.object({
272
+ ...QuestionFieldBase,
273
+ type: z4.literal("text"),
274
+ placeholder: z4.string().max(200).optional()
275
+ }),
276
+ z4.object({
277
+ ...QuestionFieldBase,
278
+ type: z4.literal("choice"),
279
+ /**
280
+ * Plain strings: the option is both the value and the label, because the answer is read back by
281
+ * a model, which wants the words and not an id. Fewer than two is not a choice, and more than
282
+ * twelve is a list — which belongs in the prompt.
283
+ */
284
+ options: z4.array(z4.string().min(1).max(200)).min(2).max(12),
285
+ multiple: z4.boolean().default(false),
286
+ /**
287
+ * The free-text escape, and it defaults to **true** on purpose: the agent wrote the options and
288
+ * the agent is the one that does not know the answer, so an operator must always be able to say
289
+ * something it did not think of. A question that truly admits only its list opts out explicitly.
290
+ */
291
+ other: z4.boolean().default(true)
292
+ })
293
+ ]);
294
+ var QuestionSchema = z4.object({
295
+ prompt: z4.string().min(1).max(1e3),
296
+ fields: z4.array(QuestionFieldSchema).min(1).max(3)
297
+ });
298
+ var AnswersSchema = z4.record(
299
+ z4.string().min(1).max(64),
300
+ z4.union([z4.string().max(2e3), z4.array(z4.string().max(2e3)).min(1).max(12)])
301
+ ).refine((answers) => Object.keys(answers).length <= 3, {
302
+ message: "a question has at most 3 fields, so an answer has at most 3 keys"
303
+ });
255
304
  var PendingActionSchema = z4.object({
305
+ kind: z4.enum(["tool", "question"]).default("tool"),
256
306
  tool_call_id: z4.string(),
257
307
  name: z4.string(),
258
- args: z4.record(z4.string(), z4.unknown())
308
+ args: z4.record(z4.string(), z4.unknown()),
309
+ /**
310
+ * Present iff `kind` is `"question"`, and it is the parse of `args` rather than a second source
311
+ * of truth: `args` is what the model called and therefore what replays byte-identically, while
312
+ * `question` is what a renderer reads without having to know that `ask_operator`'s arguments
313
+ * happen to be the question. Same content, two jobs.
314
+ */
315
+ question: QuestionSchema.optional()
259
316
  });
260
317
  var SessionSchema = z4.object({
261
318
  id: idOf("ses"),
@@ -314,6 +371,10 @@ var ToolConfirmationSchema = z4.object({
314
371
  decision: z4.enum(["allow", "deny"]),
315
372
  reason: z4.string().optional()
316
373
  });
374
+ var SessionAnswerSchema = z4.object({
375
+ tool_call_id: z4.string(),
376
+ answers: z4.record(z4.string(), z4.union([z4.string(), z4.array(z4.string())]))
377
+ });
317
378
 
318
379
  // ../core/src/schema/event.ts
319
380
  import { z as z5 } from "zod";
@@ -768,36 +829,125 @@ var AppDomainSchema = z11.object({
768
829
  var ENV_NAME = /^[A-Z_][A-Z0-9_]*$/;
769
830
  var APP_NAME = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
770
831
 
771
- // ../core/src/schema/vault.ts
832
+ // ../core/src/schema/install.ts
772
833
  import { z as z12 } from "zod";
773
- var VaultCredentialKindSchema = z12.enum(["env_var", "static_bearer", "mcp_oauth"]);
774
- var VaultSchema = z12.object({
834
+ var BlueprintInstallSourceSchema = z12.enum(["studio", "cli"]);
835
+ var BlueprintInstallSchema = z12.object({
836
+ id: idOf("bpi"),
837
+ object: z12.literal("blueprint_install"),
838
+ /** The customer's own `defineProject({ name })` — the same word `app.project` stamps (§29.2). */
839
+ project: z12.string().min(1),
840
+ /** The pair the declaration resolved to; `null` for a declaration that names no blueprint. */
841
+ blueprint: z12.string().nullable(),
842
+ template: z12.string().nullable(),
843
+ /**
844
+ * Applies, not versions of anything: 1 on the first apply, +1 on each one after, whichever writer
845
+ * made it. It is not an agent version and it never rolls back.
846
+ */
847
+ revision: z12.number().int().positive(),
848
+ /**
849
+ * The blueprint artifact version this came from, or `null` when it was applied from a working
850
+ * tree — an operator editing a clone has no version to name.
851
+ */
852
+ artifact_version: z12.string().nullable(),
853
+ /**
854
+ * The setup answers this project was configured with (§7.1 fields, keyed by their `key`), so a
855
+ * re-apply does not re-ask and both writers configure from the same answers. Configuration only:
856
+ * a secret is a vault credential (§23) or an app secret (§29.2), and neither is ever an answer.
857
+ */
858
+ answers: z12.record(z12.string(), z12.unknown()),
859
+ /**
860
+ * NO `declaration` ON THE WIRE, and that is the reconciliation of §29.6 with §31.1.
861
+ *
862
+ * The applied bytes are stored — a `revision_conflict` names the paths two writers disagree at,
863
+ * and it can only do that by holding what was applied. They are not published. An apply that
864
+ * names a published `blueprint` and `template` sends no declaration: the server reads the
865
+ * catalog's, which §31.1 says never leaves it. Echoing it back on the 201 (and again on every
866
+ * later list) would hand any organization that can sign up the whole private config of a
867
+ * blueprint it has installed nothing of — every agent's system prompt, its `mcp_servers`, its
868
+ * vault credential declarations and its app `env` keys. A caller that sent a declaration already
869
+ * has it, so nothing is lost by not saying it twice.
870
+ */
871
+ source: BlueprintInstallSourceSchema,
872
+ applied_at: Timestamp,
873
+ created_at: Timestamp
874
+ });
875
+
876
+ // ../core/src/schema/blueprint.ts
877
+ import { z as z13 } from "zod";
878
+ var BlueprintScheduleSchema = z13.object({
879
+ agent: z13.string().min(1),
880
+ cron: z13.string().min(1),
881
+ timezone: z13.string().nullable(),
882
+ /** The message each fire opens its session with — the sentence worth printing. */
883
+ input: z13.string().min(1),
884
+ budget_micro_usd: MicroUsdField.nonnegative()
885
+ });
886
+ var BlueprintCrewSchema = z13.object({
887
+ name: z13.string().min(1),
888
+ description: z13.string().nullable(),
889
+ model: z13.string().min(1),
890
+ briefed: z13.boolean()
891
+ });
892
+ var BlueprintAppSchema = z13.object({
893
+ name: z13.string().min(1),
894
+ type: AppTypeSchema,
895
+ description: z13.string().nullable()
896
+ });
897
+ var ContentHashSchema = z13.string().regex(/^[0-9a-f]{64}$/, "a lowercase sha-256 hex digest");
898
+ var BlueprintArtifactSchema = z13.object({
899
+ id: idOf("bpa"),
900
+ object: z13.literal("blueprint_artifact"),
901
+ /** The machine — its repository, screens and `/api/*`. */
902
+ blueprint: z13.string().min(1),
903
+ /** Which of that blueprint's templates provides the crew. */
904
+ template: z13.string().min(1),
905
+ /** The blueprint repository's own released version. Stored, never parsed (ADR-0401). */
906
+ version: z13.string().min(1),
907
+ content_hash: ContentHashSchema,
908
+ /**
909
+ * The setup questions, asked by the studio **before** provisioning. `QuestionField` (§7.1) is
910
+ * reused rather than restated: a setup question and a question a parked agent asks are the same
911
+ * object at different times, so one component renders both.
912
+ */
913
+ questions: z13.array(QuestionFieldSchema),
914
+ schedules: z13.array(BlueprintScheduleSchema),
915
+ /** Who the crew is, and what it deploys — §31.5. Derived from `declaration`, never stored twice. */
916
+ agents: z13.array(BlueprintCrewSchema),
917
+ apps: z13.array(BlueprintAppSchema),
918
+ published_at: Timestamp
919
+ });
920
+
921
+ // ../core/src/schema/vault.ts
922
+ import { z as z14 } from "zod";
923
+ var VaultCredentialKindSchema = z14.enum(["env_var", "static_bearer", "mcp_oauth"]);
924
+ var VaultSchema = z14.object({
775
925
  id: idOf("vlt"),
776
- object: z12.literal("vault"),
926
+ object: z14.literal("vault"),
777
927
  identity_id: idOf("idn").nullable(),
778
- display_name: z12.string().min(1).max(128),
779
- credential_count: z12.number().int().nonnegative(),
928
+ display_name: z14.string().min(1).max(128),
929
+ credential_count: z14.number().int().nonnegative(),
780
930
  created_at: Timestamp
781
931
  });
782
- var VaultCredentialSchema = z12.object({
932
+ var VaultCredentialSchema = z14.object({
783
933
  id: idOf("vcr"),
784
- object: z12.literal("vault_credential"),
934
+ object: z14.literal("vault_credential"),
785
935
  vault_id: idOf("vlt"),
786
936
  kind: VaultCredentialKindSchema,
787
937
  /** For `env_var`, the environment variable name. For the MCP kinds, a display label. */
788
- key: z12.string().min(1).max(128),
938
+ key: z14.string().min(1).max(128),
789
939
  /** The exact destination host an `env_var` credential is bound to. Never a wildcard. */
790
- host: z12.string().nullable(),
940
+ host: z14.string().nullable(),
791
941
  /** The exact MCP server URL an `mcp_*` token is matched to. */
792
- mcp_server_url: z12.url().nullable(),
793
- placeholder_ref: z12.string(),
942
+ mcp_server_url: z14.url().nullable(),
943
+ placeholder_ref: z14.string(),
794
944
  expires_at: Timestamp.nullable(),
795
945
  last_injected_at: Timestamp.nullable(),
796
946
  created_at: Timestamp
797
947
  });
798
948
 
799
949
  // ../core/src/schema/social.ts
800
- import { z as z13 } from "zod";
950
+ import { z as z15 } from "zod";
801
951
  var SOCIAL_PLATFORMS = [
802
952
  "bluesky",
803
953
  "facebook",
@@ -819,19 +969,19 @@ var SOCIAL_TEXT_PLATFORMS = [
819
969
  "threads",
820
970
  "x"
821
971
  ];
822
- var SocialPlatformSchema = z13.enum(SOCIAL_PLATFORMS);
823
- var SocialTextPlatformSchema = z13.enum(SOCIAL_TEXT_PLATFORMS);
824
- var SocialAccountSchema = z13.object({
825
- id: z13.string().min(1),
826
- object: z13.literal("social_account"),
972
+ var SocialPlatformSchema = z15.enum(SOCIAL_PLATFORMS);
973
+ var SocialTextPlatformSchema = z15.enum(SOCIAL_TEXT_PLATFORMS);
974
+ var SocialAccountSchema = z15.object({
975
+ id: z15.string().min(1),
976
+ object: z15.literal("social_account"),
827
977
  identity_id: idOf("idn"),
828
978
  platform: SocialPlatformSchema,
829
- username: z13.string().nullable(),
830
- display_name: z13.string().nullable(),
831
- avatar_url: z13.url().nullable(),
979
+ username: z15.string().nullable(),
980
+ display_name: z15.string().nullable(),
981
+ avatar_url: z15.url().nullable(),
832
982
  connected_at: Timestamp.nullable()
833
983
  });
834
- var SocialPostStatusSchema = z13.enum([
984
+ var SocialPostStatusSchema = z15.enum([
835
985
  "draft",
836
986
  "scheduled",
837
987
  "processing",
@@ -839,44 +989,44 @@ var SocialPostStatusSchema = z13.enum([
839
989
  "failed",
840
990
  "deleted"
841
991
  ]);
842
- var SocialPostSchema = z13.object({
843
- id: z13.string().min(1),
844
- object: z13.literal("social_post"),
992
+ var SocialPostSchema = z15.object({
993
+ id: z15.string().min(1),
994
+ object: z15.literal("social_post"),
845
995
  identity_id: idOf("idn"),
846
- title: z13.string(),
847
- platforms: z13.array(SocialTextPlatformSchema),
996
+ title: z15.string(),
997
+ platforms: z15.array(SocialTextPlatformSchema),
848
998
  status: SocialPostStatusSchema,
849
999
  scheduled_at: Timestamp.nullable(),
850
1000
  posted_at: Timestamp.nullable(),
851
- error: z13.string().nullable()
1001
+ error: z15.string().nullable()
852
1002
  });
853
- var SocialUploadSchema = z13.object({
854
- id: z13.string().min(1),
855
- object: z13.literal("social_upload"),
1003
+ var SocialUploadSchema = z15.object({
1004
+ id: z15.string().min(1),
1005
+ object: z15.literal("social_upload"),
856
1006
  identity_id: idOf("idn"),
857
- type: z13.enum(["image", "video", "document"]),
858
- url: z13.url().nullable()
1007
+ type: z15.enum(["image", "video", "document"]),
1008
+ url: z15.url().nullable()
859
1009
  });
860
- var SocialAnalyticsSchema = z13.object({
861
- object: z13.literal("social_analytics"),
1010
+ var SocialAnalyticsSchema = z15.object({
1011
+ object: z15.literal("social_analytics"),
862
1012
  identity_id: idOf("idn"),
863
- resource_id: z13.string().min(1),
1013
+ resource_id: z15.string().min(1),
864
1014
  platform: SocialPlatformSchema.nullable(),
865
- data: z13.unknown()
1015
+ data: z15.unknown()
866
1016
  });
867
- var SocialCommentSchema = z13.object({
868
- id: z13.string().min(1),
869
- object: z13.literal("social_comment"),
1017
+ var SocialCommentSchema = z15.object({
1018
+ id: z15.string().min(1),
1019
+ object: z15.literal("social_comment"),
870
1020
  identity_id: idOf("idn"),
871
- post_id: z13.string().min(1),
872
- platforms: z13.array(SocialPlatformSchema),
1021
+ post_id: z15.string().min(1),
1022
+ platforms: z15.array(SocialPlatformSchema),
873
1023
  status: SocialPostStatusSchema,
874
1024
  posted_at: Timestamp.nullable(),
875
- data: z13.unknown()
1025
+ data: z15.unknown()
876
1026
  });
877
1027
 
878
1028
  // src/define.ts
879
- import { z as z14 } from "zod";
1029
+ import { z as z16 } from "zod";
880
1030
 
881
1031
  // src/template.ts
882
1032
  var BLUEPRINTS = {
@@ -915,84 +1065,109 @@ function chooseTemplate(blueprint, template) {
915
1065
  var BlueprintRefusal = class extends Error {
916
1066
  name = "BlueprintRefusal";
917
1067
  };
918
- var EnvValueSchema = z14.union([z14.string(), z14.object({ from_env: z14.string().min(1) })]);
919
- var AppDeclSchema = z14.object({
1068
+ var EnvValueSchema = z16.union([z16.string(), z16.object({ from_env: z16.string().min(1) })]);
1069
+ var AppDeclSchema = z16.object({
920
1070
  /** Becomes a hostname, so the grammar is the platform's `APP_NAME` (a DNS label). */
921
- name: z14.string().regex(APP_NAME, "a lowercase DNS label, 63 characters or fewer"),
1071
+ name: z16.string().regex(APP_NAME, "a lowercase DNS label, 63 characters or fewer"),
922
1072
  type: AppTypeSchema,
923
- description: z14.string().optional(),
1073
+ description: z16.string().optional(),
924
1074
  /** A directory whose files are collected and shipped as the app's file-map deployment. */
925
- deploy_dir: z14.string().min(1).optional(),
1075
+ deploy_dir: z16.string().min(1).optional(),
926
1076
  /** The path the app itself serves MCP on (§29.5); `fullstack` only. Absent means none. */
927
1077
  mcp: AppMcpPathSchema.optional(),
928
1078
  /** Written as the app's write-only secrets (§29.2) on every apply; names follow the platform's `ENV_NAME`. */
929
- env: z14.record(z14.string().regex(ENV_NAME), EnvValueSchema, { error: "keyed by environment variable name (A-Z, 0-9, _)" }).optional()
1079
+ env: z16.record(z16.string().regex(ENV_NAME), EnvValueSchema, { error: "keyed by environment variable name (A-Z, 0-9, _)" }).optional()
930
1080
  });
931
- var ScheduleDeclSchema = z14.object({
932
- cron: z14.string().min(1),
933
- timezone: z14.string().min(1).optional(),
1081
+ var ScheduleDeclSchema = z16.object({
1082
+ cron: z16.string().min(1),
1083
+ timezone: z16.string().min(1).optional(),
934
1084
  /** The message every fire opens its session with (`input_template` on the wire). */
935
- input: z14.string().min(1),
1085
+ input: z16.string().min(1),
936
1086
  budget_micro_usd: MicroUsdField.nonnegative(),
937
- identity: z14.string().min(1).optional(),
938
- enabled: z14.boolean().optional()
1087
+ identity: z16.string().min(1).optional(),
1088
+ enabled: z16.boolean().optional()
939
1089
  });
940
- var AgentDeclSchema = z14.object({
941
- name: z14.string().min(1),
942
- model: z14.string().min(1),
1090
+ var AgentDeclSchema = z16.object({
1091
+ name: z16.string().min(1),
1092
+ model: z16.string().min(1),
943
1093
  budget: BudgetSchema,
944
- description: z14.string().optional(),
945
- system: z14.string().optional(),
1094
+ description: z16.string().optional(),
1095
+ system: z16.string().optional(),
946
1096
  tools: ToolsetSchema.optional(),
947
- skills: z14.array(SkillRefSchema).optional(),
948
- mcp_servers: z14.array(McpServerSchema).optional(),
1097
+ skills: z16.array(SkillRefSchema).optional(),
1098
+ mcp_servers: z16.array(McpServerSchema).optional(),
949
1099
  /** Declared app *names*; `up` resolves them to `app_` ids in `tools.configs.apps` (§29.3). */
950
- allowed_apps: z14.array(z14.string()).optional(),
1100
+ allowed_apps: z16.array(z16.string()).optional(),
951
1101
  /** A declared identity *name*; `up` attaches it after the agent upsert (§22). */
952
- identity: z14.string().min(1).optional(),
1102
+ identity: z16.string().min(1).optional(),
953
1103
  /** The agent's deployments as a complete set: declared → owned, and the rest are pruned (ADR-0355). */
954
- schedules: z14.array(ScheduleDeclSchema).optional()
1104
+ schedules: z16.array(ScheduleDeclSchema).optional(),
1105
+ /**
1106
+ * The first thing this agent reads, once, on the apply that **creates** it (`canonical-spec
1107
+ * §31.4`). It is what turns a provisioned crew into a crew that has started working.
1108
+ *
1109
+ * Neither of its neighbours can carry it. A `schedule` fires again and again; an intake happens
1110
+ * exactly once, at creation, and never on a later apply of the same agent. `system` is standing
1111
+ * instruction re-read every turn; an intake is a message the agent is sent.
1112
+ *
1113
+ * `budget_micro_usd` is optional here and required on a schedule, because a cron spends
1114
+ * repeatedly and unattended while an intake spends once, at an apply a person is watching. Absent
1115
+ * means the agent's own `budget` governs.
1116
+ */
1117
+ intake: z16.object({ message: z16.string().min(1), budget_micro_usd: MicroUsdField.nonnegative().optional() }).optional()
955
1118
  });
956
- var CredentialDeclSchema = z14.object({
1119
+ var CredentialDeclSchema = z16.object({
957
1120
  kind: VaultCredentialKindSchema,
958
- key: z14.string().min(1).max(128),
1121
+ key: z16.string().min(1).max(128),
959
1122
  /** The one exact destination host an `env_var` is bound to. */
960
- host: z14.string().min(1).optional(),
1123
+ host: z16.string().min(1).optional(),
961
1124
  /** The MCP server URL a `static_bearer` / `mcp_oauth` token is matched to. */
962
- mcp_server_url: z14.url().optional(),
963
- value: z14.object({ from_env: z14.string().min(1) })
1125
+ mcp_server_url: z16.url().optional(),
1126
+ value: z16.object({ from_env: z16.string().min(1) })
964
1127
  });
965
- var VaultDeclSchema = z14.object({
1128
+ var VaultDeclSchema = z16.object({
966
1129
  name: VaultSchema.shape.display_name,
967
- identity: z14.string().min(1).optional(),
968
- credentials: z14.array(CredentialDeclSchema)
1130
+ identity: z16.string().min(1).optional(),
1131
+ credentials: z16.array(CredentialDeclSchema)
969
1132
  });
970
- var SkillDeclSchema = z14.object({
971
- slug: z14.string().regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters, digits and dashes"),
972
- file: z14.string().min(1)
1133
+ var SkillDeclSchema = z16.object({
1134
+ slug: z16.string().regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters, digits and dashes"),
1135
+ file: z16.string().min(1)
973
1136
  });
974
1137
  var IdentityDeclSchema = IdentitySchema.pick({ name: true, description: true }).partial({
975
1138
  description: true
976
1139
  });
977
- var RemovedSchema = z14.object({
978
- apps: z14.array(z14.string().min(1)).default([]),
979
- agents: z14.array(z14.string().min(1)).default([]),
980
- skills: z14.array(z14.string().min(1)).default([]),
981
- identities: z14.array(z14.string().min(1)).default([]),
982
- vaults: z14.array(z14.string().min(1)).default([])
1140
+ var RemovedSchema = z16.object({
1141
+ apps: z16.array(z16.string().min(1)).default([]),
1142
+ agents: z16.array(z16.string().min(1)).default([]),
1143
+ skills: z16.array(z16.string().min(1)).default([]),
1144
+ identities: z16.array(z16.string().min(1)).default([]),
1145
+ vaults: z16.array(z16.string().min(1)).default([])
983
1146
  });
984
- var KeptSchema = z14.object({ agents: z14.array(z14.string().min(1)).default([]) });
985
- var ProjectSchema = z14.object({
986
- name: z14.string().min(1),
1147
+ var KeptSchema = z16.object({ agents: z16.array(z16.string().min(1)).default([]) });
1148
+ var ProjectSchema = z16.object({
1149
+ name: z16.string().min(1),
987
1150
  /** The machine this project runs: its repo, its screens, its `/api/*` (`template.ts`). */
988
- blueprint: z14.string().min(1).optional(),
1151
+ blueprint: z16.string().min(1).optional(),
989
1152
  /** Which of that blueprint's templates provides the crew; a blueprint with a default may omit it. */
990
- template: z14.string().min(1).optional(),
991
- skills: z14.array(SkillDeclSchema).default([]),
992
- identities: z14.array(IdentityDeclSchema).default([]),
993
- vaults: z14.array(VaultDeclSchema).default([]),
994
- apps: z14.array(AppDeclSchema).default([]),
995
- agents: z14.array(AgentDeclSchema).default([]),
1153
+ template: z16.string().min(1).optional(),
1154
+ /**
1155
+ * What a person is asked BEFORE the crew is provisioned (`canonical-spec §31.2`).
1156
+ *
1157
+ * `QuestionField` is reused rather than restated: a setup question and a question a parked agent
1158
+ * asks mid-run are the same object asked at different times, so a blueprint author writes one
1159
+ * shape and one component renders both. These are published on the artifact — they are the whole
1160
+ * reason the catalog has a `questions` column — and the answers a person gives go on the install
1161
+ * (§29.6), never into the tree, because the tree is identical for everyone.
1162
+ *
1163
+ * **Configuration only.** A secret is a vault credential or an app secret; neither is an answer.
1164
+ */
1165
+ questions: z16.array(QuestionFieldSchema).default([]),
1166
+ skills: z16.array(SkillDeclSchema).default([]),
1167
+ identities: z16.array(IdentityDeclSchema).default([]),
1168
+ vaults: z16.array(VaultDeclSchema).default([]),
1169
+ apps: z16.array(AppDeclSchema).default([]),
1170
+ agents: z16.array(AgentDeclSchema).default([]),
996
1171
  removed: RemovedSchema.default({ apps: [], agents: [], skills: [], identities: [], vaults: [] }),
997
1172
  kept: KeptSchema.default({ agents: [] })
998
1173
  });
@@ -1168,6 +1343,48 @@ async function loadConfig(dir) {
1168
1343
  return validate(path, raw);
1169
1344
  }
1170
1345
 
1346
+ // src/files.ts
1347
+ import { createHash } from "node:crypto";
1348
+ import { readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
1349
+ import { join as join2, relative, sep } from "node:path";
1350
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
1351
+ var MAX_FILE_BYTES = 8 * 1024 * 1024;
1352
+ function collectFiles(source) {
1353
+ if (typeof source !== "string") return fromMap(source);
1354
+ const root = source;
1355
+ const files = {};
1356
+ const walk = (dir) => {
1357
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1358
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".env")) continue;
1359
+ const full = join2(dir, entry.name);
1360
+ if (entry.isDirectory()) walk(full);
1361
+ else if (entry.isFile() && statSync(full).size <= MAX_FILE_BYTES) {
1362
+ files[relative(root, full).split(sep).join("/")] = readFileSync2(full).toString("base64");
1363
+ }
1364
+ }
1365
+ };
1366
+ walk(root);
1367
+ return files;
1368
+ }
1369
+ function fromMap(map) {
1370
+ const files = {};
1371
+ for (const [path, base64] of Object.entries(map)) {
1372
+ const segments = path.split("/");
1373
+ if (segments.some((segment) => SKIP_DIRS.has(segment) || segment.startsWith(".env"))) continue;
1374
+ if (Buffer.from(base64, "base64").length > MAX_FILE_BYTES) continue;
1375
+ files[path] = base64;
1376
+ }
1377
+ return files;
1378
+ }
1379
+ function contentHash(files) {
1380
+ const hash = createHash("sha256");
1381
+ for (const [path, base64] of Object.entries(files).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
1382
+ const bytes = Buffer.from(base64, "base64");
1383
+ hash.update(`${path}\0${bytes.length}\0`).update(bytes);
1384
+ }
1385
+ return hash.digest("hex");
1386
+ }
1387
+
1171
1388
  // src/up.ts
1172
1389
  import { createHash as createHash3 } from "node:crypto";
1173
1390
  import { readFileSync as readFileSync3 } from "node:fs";
@@ -1175,7 +1392,7 @@ import { resolve as resolve3 } from "node:path";
1175
1392
 
1176
1393
  // src/report.ts
1177
1394
  import { ApiError, send } from "@usenaive-sdk/vetta";
1178
- import { z as z15 } from "zod";
1395
+ import { z as z17 } from "zod";
1179
1396
  function message2(error) {
1180
1397
  return error instanceof Error ? error.message : String(error);
1181
1398
  }
@@ -1205,10 +1422,10 @@ async function absent(work) {
1205
1422
  throw error;
1206
1423
  }
1207
1424
  }
1208
- var PageShape = z15.object({
1209
- data: z15.array(z15.unknown()),
1210
- has_more: z15.boolean(),
1211
- next_cursor: z15.string().nullable()
1425
+ var PageShape = z17.object({
1426
+ data: z17.array(z17.unknown()),
1427
+ has_more: z17.boolean(),
1428
+ next_cursor: z17.string().nullable()
1212
1429
  });
1213
1430
  function rowLabel(row) {
1214
1431
  const { object, id } = row ?? {};
@@ -1295,6 +1512,15 @@ async function upsertAgent(client, decl, existing, ids, dryRun) {
1295
1512
  }
1296
1513
  return { name: decl.name, action: "updated", id: current.id };
1297
1514
  }
1515
+ async function openIntake(client, agent, decl, dryRun) {
1516
+ if (dryRun || agent.id === void 0) return { name: agent.name, action: "created" };
1517
+ const started = await client.sessions.create({
1518
+ agent_id: agent.id,
1519
+ message: decl.message,
1520
+ ...decl.budget_micro_usd !== void 0 ? { budget_micro_usd: decl.budget_micro_usd } : {}
1521
+ });
1522
+ return { name: agent.name, action: "created", id: started.id };
1523
+ }
1298
1524
  async function upsertSchedule(client, agent, decl, current, identityIds, dryRun) {
1299
1525
  const name = `${agent.name} @ ${decl.cron}`;
1300
1526
  if (agent.action === "refused") throw new Error(`agent "${agent.name}" was not provisioned in this run`);
@@ -1339,38 +1565,6 @@ async function reconcileSchedules(client, agent, declared, live, identityIds, dr
1339
1565
  // src/apps.ts
1340
1566
  import { createHash as createHash2 } from "node:crypto";
1341
1567
  import { resolve as resolve2 } from "node:path";
1342
-
1343
- // src/files.ts
1344
- import { createHash } from "node:crypto";
1345
- import { readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
1346
- import { join as join2, relative, sep } from "node:path";
1347
- var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
1348
- var MAX_FILE_BYTES = 8 * 1024 * 1024;
1349
- function collectFiles(root) {
1350
- const files = {};
1351
- const walk = (dir) => {
1352
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
1353
- if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".env")) continue;
1354
- const full = join2(dir, entry.name);
1355
- if (entry.isDirectory()) walk(full);
1356
- else if (entry.isFile() && statSync(full).size <= MAX_FILE_BYTES) {
1357
- files[relative(root, full).split(sep).join("/")] = readFileSync2(full).toString("base64");
1358
- }
1359
- }
1360
- };
1361
- walk(root);
1362
- return files;
1363
- }
1364
- function contentHash(files) {
1365
- const hash = createHash("sha256");
1366
- for (const [path, base64] of Object.entries(files).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
1367
- const bytes = Buffer.from(base64, "base64");
1368
- hash.update(`${path}\0${bytes.length}\0`).update(bytes);
1369
- }
1370
- return hash.digest("hex");
1371
- }
1372
-
1373
- // src/apps.ts
1374
1568
  async function findApp(client, name, skipped) {
1375
1569
  const rows = await listAll(client.config, "/v1/apps", AppSchema, skipped, { name });
1376
1570
  return rows.find((app) => app.name === name) ?? null;
@@ -1629,7 +1823,7 @@ async function up(config, client, opts = {}) {
1629
1823
  const dryRun = opts.dry_run ?? false;
1630
1824
  const dir = opts.dir ?? process.cwd();
1631
1825
  const ids = { apps: /* @__PURE__ */ new Map(), identities: /* @__PURE__ */ new Map() };
1632
- const report = { dry_run: dryRun, ...applied(config), skills: [], identities: [], vaults: [], apps: [], agents: [], schedules: [], skipped: [] };
1826
+ const report = { dry_run: dryRun, ...applied(config), skills: [], identities: [], vaults: [], apps: [], agents: [], schedules: [], intake: [], skipped: [] };
1633
1827
  const owner = { project: config.name, adopt: opts.adopt ?? false, skipped: report.skipped };
1634
1828
  for (const decl of config.skills) {
1635
1829
  await attempt(report.skills, decl.slug, () => upsertSkill(client, decl, { dryRun, dir }));
@@ -1651,14 +1845,19 @@ async function up(config, client, opts = {}) {
1651
1845
  }
1652
1846
  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) : [];
1653
1847
  const deployments = config.agents.some((decl) => decl.schedules !== void 0) ? await listAll(client.config, "/v1/deployments", DeploymentSchema, report.skipped) : [];
1848
+ const briefed = [];
1654
1849
  for (const decl of config.agents) {
1655
1850
  const line = await attempt(report.agents, decl.name, () => upsertAgent(client, decl, existing, ids, dryRun));
1656
1851
  if (decl.schedules !== void 0) {
1657
1852
  await reconcileSchedules(client, line, decl.schedules, deployments, ids.identities, dryRun, report.schedules);
1658
1853
  }
1854
+ if (decl.intake !== void 0 && line.action === "created") briefed.push([line, decl.intake]);
1659
1855
  }
1660
1856
  reportKept(config, existing, report.agents);
1661
1857
  await applyRemoved(client, config.removed, { agents: existing, vaults }, owner, report);
1858
+ for (const [line, decl] of briefed) {
1859
+ await attempt(report.intake, line.name, () => openIntake(client, line, decl, dryRun));
1860
+ }
1662
1861
  return report;
1663
1862
  }
1664
1863
  export {
@@ -1666,6 +1865,8 @@ export {
1666
1865
  BlueprintRefusal,
1667
1866
  CONFIG_CANDIDATES,
1668
1867
  chooseTemplate,
1868
+ collectFiles,
1869
+ contentHash,
1669
1870
  defineProject,
1670
1871
  loadConfig,
1671
1872
  parseProject,