@usenaive-sdk/blueprints 0.1.0 → 0.3.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,10 @@ 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
+ /** A ticket was minted that lets one browser past an app's own operator gate (§29.7). */
97
+ "app.entered",
98
+ "blueprint.installed"
96
99
  ];
97
100
  var AUDIT_ACTIONS = [
98
101
  ...AUDIT_ACTIONS_SHIPPED,
@@ -239,7 +242,16 @@ var StopReasonSchema = z4.enum([
239
242
  * Distinct from `awaiting_input`: nobody is being asked for anything, so no human can unblock it —
240
243
  * the child's completion does. Reporting `idle`/`end_turn` here would claim the task was finished.
241
244
  */
242
- "awaiting_delegation"
245
+ "awaiting_delegation",
246
+ /**
247
+ * The turn yielded because the agent asked the operator a question and cannot proceed without
248
+ * the answer (§7.1). Distinct from all three neighbours, and none can stand in: `awaiting_input`
249
+ * says only that there is nothing left to do, so a client reading it renders a bare composer
250
+ * where a specific answer is owed; `awaiting_approval` says the block is a tool call to allow or
251
+ * deny, and its route carries a verb and no payload; `awaiting_delegation` says no human can
252
+ * unblock it at all. What is being asked is the parked `pending_actions` row.
253
+ */
254
+ "awaiting_answer"
243
255
  ]);
244
256
  var ThreadRoleSchema = z4.enum(["coordinator", "member"]);
245
257
  var MessageSchema = z4.object({
@@ -252,10 +264,57 @@ var ToolResultSchema = z4.object({
252
264
  is_error: z4.boolean().optional(),
253
265
  content: z4.string().optional()
254
266
  });
267
+ 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()
271
+ };
272
+ var QuestionFieldSchema = z4.discriminatedUnion("type", [
273
+ z4.object({
274
+ ...QuestionFieldBase,
275
+ type: z4.literal("text"),
276
+ placeholder: z4.string().max(200).optional()
277
+ }),
278
+ z4.object({
279
+ ...QuestionFieldBase,
280
+ type: z4.literal("choice"),
281
+ /**
282
+ * Plain strings: the option is both the value and the label, because the answer is read back by
283
+ * a model, which wants the words and not an id. Fewer than two is not a choice, and more than
284
+ * twelve is a list — which belongs in the prompt.
285
+ */
286
+ options: z4.array(z4.string().min(1).max(200)).min(2).max(12),
287
+ multiple: z4.boolean().default(false),
288
+ /**
289
+ * The free-text escape, and it defaults to **true** on purpose: the agent wrote the options and
290
+ * the agent is the one that does not know the answer, so an operator must always be able to say
291
+ * something it did not think of. A question that truly admits only its list opts out explicitly.
292
+ */
293
+ other: z4.boolean().default(true)
294
+ })
295
+ ]);
296
+ var QuestionSchema = z4.object({
297
+ prompt: z4.string().min(1).max(1e3),
298
+ fields: z4.array(QuestionFieldSchema).min(1).max(3)
299
+ });
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)])
303
+ ).refine((answers) => Object.keys(answers).length <= 3, {
304
+ message: "a question has at most 3 fields, so an answer has at most 3 keys"
305
+ });
255
306
  var PendingActionSchema = z4.object({
307
+ kind: z4.enum(["tool", "question"]).default("tool"),
256
308
  tool_call_id: z4.string(),
257
309
  name: z4.string(),
258
- args: z4.record(z4.string(), z4.unknown())
310
+ args: z4.record(z4.string(), z4.unknown()),
311
+ /**
312
+ * Present iff `kind` is `"question"`, and it is the parse of `args` rather than a second source
313
+ * of truth: `args` is what the model called and therefore what replays byte-identically, while
314
+ * `question` is what a renderer reads without having to know that `ask_operator`'s arguments
315
+ * happen to be the question. Same content, two jobs.
316
+ */
317
+ question: QuestionSchema.optional()
259
318
  });
260
319
  var SessionSchema = z4.object({
261
320
  id: idOf("ses"),
@@ -314,6 +373,10 @@ var ToolConfirmationSchema = z4.object({
314
373
  decision: z4.enum(["allow", "deny"]),
315
374
  reason: z4.string().optional()
316
375
  });
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())]))
379
+ });
317
380
 
318
381
  // ../core/src/schema/event.ts
319
382
  import { z as z5 } from "zod";
@@ -756,6 +819,11 @@ var AppSecretSchema = z11.object({
756
819
  value_hash: z11.string().nullable(),
757
820
  created_at: Timestamp
758
821
  });
822
+ var AppEntrySchema = z11.object({
823
+ object: z11.literal("app_entry"),
824
+ url: z11.string(),
825
+ ticket: z11.string()
826
+ });
759
827
  var AppDomainStatusSchema = z11.enum(["pending", "connected", "failed"]);
760
828
  var AppDomainSchema = z11.object({
761
829
  object: z11.literal("app_domain"),
@@ -768,36 +836,168 @@ var AppDomainSchema = z11.object({
768
836
  var ENV_NAME = /^[A-Z_][A-Z0-9_]*$/;
769
837
  var APP_NAME = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
770
838
 
771
- // ../core/src/schema/vault.ts
839
+ // ../core/src/schema/install.ts
772
840
  import { z as z12 } from "zod";
773
- var VaultCredentialKindSchema = z12.enum(["env_var", "static_bearer", "mcp_oauth"]);
774
- var VaultSchema = z12.object({
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),
845
+ action: ResourceActionSchema,
846
+ /** The `agt_`/`app_`/`ses_`… the line resolved to. Absent on a refusal that never got an id. */
847
+ id: z12.string().optional(),
848
+ /** 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({
863
+ id: idOf("bpi"),
864
+ object: z12.literal("blueprint_install"),
865
+ /** The customer's own `defineProject({ name })` — the same word `app.project` stamps (§29.2). */
866
+ project: z12.string().min(1),
867
+ /** The pair the declaration resolved to; `null` for a declaration that names no blueprint. */
868
+ blueprint: z12.string().nullable(),
869
+ template: z12.string().nullable(),
870
+ /**
871
+ * Applies, not versions of anything: 1 on the first apply, +1 on each one after, whichever writer
872
+ * made it. It is not an agent version and it never rolls back.
873
+ */
874
+ revision: z12.number().int().positive(),
875
+ /**
876
+ * The blueprint artifact version this came from, or `null` when it was applied from a working
877
+ * tree — an operator editing a clone has no version to name.
878
+ */
879
+ artifact_version: z12.string().nullable(),
880
+ /**
881
+ * The setup answers this project was configured with (§7.1 fields, keyed by their `key`), so a
882
+ * re-apply does not re-ask and both writers configure from the same answers. Configuration only:
883
+ * a secret is a vault credential (§23) or an app secret (§29.2), and neither is ever an answer.
884
+ */
885
+ answers: z12.record(z12.string(), z12.unknown()),
886
+ /**
887
+ * NO `declaration` ON THE WIRE, and that is the reconciliation of §29.6 with §31.1.
888
+ *
889
+ * The applied bytes are stored — a `revision_conflict` names the paths two writers disagree at,
890
+ * and it can only do that by holding what was applied. They are not published. An apply that
891
+ * names a published `blueprint` and `template` sends no declaration: the server reads the
892
+ * catalog's, which §31.1 says never leaves it. Echoing it back on the 201 (and again on every
893
+ * later list) would hand any organization that can sign up the whole private config of a
894
+ * blueprint it has installed nothing of — every agent's system prompt, its `mcp_servers`, its
895
+ * vault credential declarations and its app `env` keys. A caller that sent a declaration already
896
+ * has it, so nothing is lost by not saying it twice.
897
+ */
898
+ source: BlueprintInstallSourceSchema,
899
+ /**
900
+ * WHAT THE APPLY DID, or `null` when it did nothing but record.
901
+ *
902
+ * An apply that sends its own `declaration` has already reconciled it — that is what `naive up`
903
+ * is — so the row it writes is a record and this is `null`. An apply that names a **published**
904
+ * pair has reconciled nothing: it was never given the bytes (§31.1), so the server reads them
905
+ * from the catalog and runs the same engine over them, and this is what that run reported.
906
+ *
907
+ * It is stored rather than only returned because a refusal inside a successful apply — an app
908
+ * whose `env` reads a variable no hosted run has, a session refused for credit — is the one fact
909
+ * a person needs after the screen that showed it is gone.
910
+ */
911
+ report: BlueprintReportSchema.nullable(),
912
+ applied_at: Timestamp,
913
+ created_at: Timestamp
914
+ });
915
+
916
+ // ../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(),
922
+ /** The message each fire opens its session with — the sentence worth printing. */
923
+ input: z13.string().min(1),
924
+ budget_micro_usd: MicroUsdField.nonnegative()
925
+ });
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),
934
+ 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({
939
+ /** `apps[].name` in the declaration — the same name §31.5 prints and the install ensures by. */
940
+ app: z13.string().min(1),
941
+ content_hash: ContentHashSchema
942
+ });
943
+ var BlueprintArtifactSchema = z13.object({
944
+ id: idOf("bpa"),
945
+ object: z13.literal("blueprint_artifact"),
946
+ /** The machine — its repository, screens and `/api/*`. */
947
+ blueprint: z13.string().min(1),
948
+ /** Which of that blueprint's templates provides the crew. */
949
+ template: z13.string().min(1),
950
+ /** The blueprint repository's own released version. Stored, never parsed (ADR-0401). */
951
+ version: z13.string().min(1),
952
+ /**
953
+ * The built trees, one per deployable app, ordered as the declaration orders its apps. Never
954
+ * empty: an artifact that built nothing is not an artifact, and the column behind it says so
955
+ * (migration 0040) exactly as the single `content_hash` it replaces was `not null`.
956
+ */
957
+ trees: z13.array(BlueprintTreeSchema).min(1),
958
+ /**
959
+ * The setup questions, asked by the studio **before** provisioning. `QuestionField` (§7.1) is
960
+ * reused rather than restated: a setup question and a question a parked agent asks are the same
961
+ * object at different times, so one component renders both.
962
+ */
963
+ questions: z13.array(QuestionFieldSchema),
964
+ schedules: z13.array(BlueprintScheduleSchema),
965
+ /** 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),
968
+ published_at: Timestamp
969
+ });
970
+
971
+ // ../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({
775
975
  id: idOf("vlt"),
776
- object: z12.literal("vault"),
976
+ object: z14.literal("vault"),
777
977
  identity_id: idOf("idn").nullable(),
778
- display_name: z12.string().min(1).max(128),
779
- credential_count: z12.number().int().nonnegative(),
978
+ display_name: z14.string().min(1).max(128),
979
+ credential_count: z14.number().int().nonnegative(),
780
980
  created_at: Timestamp
781
981
  });
782
- var VaultCredentialSchema = z12.object({
982
+ var VaultCredentialSchema = z14.object({
783
983
  id: idOf("vcr"),
784
- object: z12.literal("vault_credential"),
984
+ object: z14.literal("vault_credential"),
785
985
  vault_id: idOf("vlt"),
786
986
  kind: VaultCredentialKindSchema,
787
987
  /** For `env_var`, the environment variable name. For the MCP kinds, a display label. */
788
- key: z12.string().min(1).max(128),
988
+ key: z14.string().min(1).max(128),
789
989
  /** The exact destination host an `env_var` credential is bound to. Never a wildcard. */
790
- host: z12.string().nullable(),
990
+ host: z14.string().nullable(),
791
991
  /** The exact MCP server URL an `mcp_*` token is matched to. */
792
- mcp_server_url: z12.url().nullable(),
793
- placeholder_ref: z12.string(),
992
+ mcp_server_url: z14.url().nullable(),
993
+ placeholder_ref: z14.string(),
794
994
  expires_at: Timestamp.nullable(),
795
995
  last_injected_at: Timestamp.nullable(),
796
996
  created_at: Timestamp
797
997
  });
798
998
 
799
999
  // ../core/src/schema/social.ts
800
- import { z as z13 } from "zod";
1000
+ import { z as z15 } from "zod";
801
1001
  var SOCIAL_PLATFORMS = [
802
1002
  "bluesky",
803
1003
  "facebook",
@@ -819,19 +1019,19 @@ var SOCIAL_TEXT_PLATFORMS = [
819
1019
  "threads",
820
1020
  "x"
821
1021
  ];
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"),
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"),
827
1027
  identity_id: idOf("idn"),
828
1028
  platform: SocialPlatformSchema,
829
- username: z13.string().nullable(),
830
- display_name: z13.string().nullable(),
831
- avatar_url: z13.url().nullable(),
1029
+ username: z15.string().nullable(),
1030
+ display_name: z15.string().nullable(),
1031
+ avatar_url: z15.url().nullable(),
832
1032
  connected_at: Timestamp.nullable()
833
1033
  });
834
- var SocialPostStatusSchema = z13.enum([
1034
+ var SocialPostStatusSchema = z15.enum([
835
1035
  "draft",
836
1036
  "scheduled",
837
1037
  "processing",
@@ -839,44 +1039,44 @@ var SocialPostStatusSchema = z13.enum([
839
1039
  "failed",
840
1040
  "deleted"
841
1041
  ]);
842
- var SocialPostSchema = z13.object({
843
- id: z13.string().min(1),
844
- object: z13.literal("social_post"),
1042
+ var SocialPostSchema = z15.object({
1043
+ id: z15.string().min(1),
1044
+ object: z15.literal("social_post"),
845
1045
  identity_id: idOf("idn"),
846
- title: z13.string(),
847
- platforms: z13.array(SocialTextPlatformSchema),
1046
+ title: z15.string(),
1047
+ platforms: z15.array(SocialTextPlatformSchema),
848
1048
  status: SocialPostStatusSchema,
849
1049
  scheduled_at: Timestamp.nullable(),
850
1050
  posted_at: Timestamp.nullable(),
851
- error: z13.string().nullable()
1051
+ error: z15.string().nullable()
852
1052
  });
853
- var SocialUploadSchema = z13.object({
854
- id: z13.string().min(1),
855
- object: z13.literal("social_upload"),
1053
+ var SocialUploadSchema = z15.object({
1054
+ id: z15.string().min(1),
1055
+ object: z15.literal("social_upload"),
856
1056
  identity_id: idOf("idn"),
857
- type: z13.enum(["image", "video", "document"]),
858
- url: z13.url().nullable()
1057
+ type: z15.enum(["image", "video", "document"]),
1058
+ url: z15.url().nullable()
859
1059
  });
860
- var SocialAnalyticsSchema = z13.object({
861
- object: z13.literal("social_analytics"),
1060
+ var SocialAnalyticsSchema = z15.object({
1061
+ object: z15.literal("social_analytics"),
862
1062
  identity_id: idOf("idn"),
863
- resource_id: z13.string().min(1),
1063
+ resource_id: z15.string().min(1),
864
1064
  platform: SocialPlatformSchema.nullable(),
865
- data: z13.unknown()
1065
+ data: z15.unknown()
866
1066
  });
867
- var SocialCommentSchema = z13.object({
868
- id: z13.string().min(1),
869
- object: z13.literal("social_comment"),
1067
+ var SocialCommentSchema = z15.object({
1068
+ id: z15.string().min(1),
1069
+ object: z15.literal("social_comment"),
870
1070
  identity_id: idOf("idn"),
871
- post_id: z13.string().min(1),
872
- platforms: z13.array(SocialPlatformSchema),
1071
+ post_id: z15.string().min(1),
1072
+ platforms: z15.array(SocialPlatformSchema),
873
1073
  status: SocialPostStatusSchema,
874
1074
  posted_at: Timestamp.nullable(),
875
- data: z13.unknown()
1075
+ data: z15.unknown()
876
1076
  });
877
1077
 
878
1078
  // src/define.ts
879
- import { z as z14 } from "zod";
1079
+ import { z as z16 } from "zod";
880
1080
 
881
1081
  // src/template.ts
882
1082
  var BLUEPRINTS = {
@@ -915,84 +1115,113 @@ function chooseTemplate(blueprint, template) {
915
1115
  var BlueprintRefusal = class extends Error {
916
1116
  name = "BlueprintRefusal";
917
1117
  };
918
- var EnvValueSchema = z14.union([z14.string(), z14.object({ from_env: z14.string().min(1) })]);
919
- var AppDeclSchema = z14.object({
1118
+ var EnvValueSchema = z16.union([
1119
+ z16.string(),
1120
+ z16.object({ from_env: z16.string().min(1) }),
1121
+ z16.object({ generate: z16.literal(true) })
1122
+ ]);
1123
+ var AppDeclSchema = z16.object({
920
1124
  /** 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"),
1125
+ name: z16.string().regex(APP_NAME, "a lowercase DNS label, 63 characters or fewer"),
922
1126
  type: AppTypeSchema,
923
- description: z14.string().optional(),
1127
+ description: z16.string().optional(),
924
1128
  /** A directory whose files are collected and shipped as the app's file-map deployment. */
925
- deploy_dir: z14.string().min(1).optional(),
1129
+ deploy_dir: z16.string().min(1).optional(),
926
1130
  /** The path the app itself serves MCP on (§29.5); `fullstack` only. Absent means none. */
927
1131
  mcp: AppMcpPathSchema.optional(),
928
1132
  /** 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()
1133
+ env: z16.record(z16.string().regex(ENV_NAME), EnvValueSchema, { error: "keyed by environment variable name (A-Z, 0-9, _)" }).optional()
930
1134
  });
931
- var ScheduleDeclSchema = z14.object({
932
- cron: z14.string().min(1),
933
- timezone: z14.string().min(1).optional(),
1135
+ var ScheduleDeclSchema = z16.object({
1136
+ cron: z16.string().min(1),
1137
+ timezone: z16.string().min(1).optional(),
934
1138
  /** The message every fire opens its session with (`input_template` on the wire). */
935
- input: z14.string().min(1),
1139
+ input: z16.string().min(1),
936
1140
  budget_micro_usd: MicroUsdField.nonnegative(),
937
- identity: z14.string().min(1).optional(),
938
- enabled: z14.boolean().optional()
1141
+ identity: z16.string().min(1).optional(),
1142
+ enabled: z16.boolean().optional()
939
1143
  });
940
- var AgentDeclSchema = z14.object({
941
- name: z14.string().min(1),
942
- model: z14.string().min(1),
1144
+ var AgentDeclSchema = z16.object({
1145
+ name: z16.string().min(1),
1146
+ model: z16.string().min(1),
943
1147
  budget: BudgetSchema,
944
- description: z14.string().optional(),
945
- system: z14.string().optional(),
1148
+ description: z16.string().optional(),
1149
+ system: z16.string().optional(),
946
1150
  tools: ToolsetSchema.optional(),
947
- skills: z14.array(SkillRefSchema).optional(),
948
- mcp_servers: z14.array(McpServerSchema).optional(),
1151
+ skills: z16.array(SkillRefSchema).optional(),
1152
+ mcp_servers: z16.array(McpServerSchema).optional(),
949
1153
  /** Declared app *names*; `up` resolves them to `app_` ids in `tools.configs.apps` (§29.3). */
950
- allowed_apps: z14.array(z14.string()).optional(),
1154
+ allowed_apps: z16.array(z16.string()).optional(),
951
1155
  /** A declared identity *name*; `up` attaches it after the agent upsert (§22). */
952
- identity: z14.string().min(1).optional(),
1156
+ identity: z16.string().min(1).optional(),
953
1157
  /** The agent's deployments as a complete set: declared → owned, and the rest are pruned (ADR-0355). */
954
- schedules: z14.array(ScheduleDeclSchema).optional()
1158
+ schedules: z16.array(ScheduleDeclSchema).optional(),
1159
+ /**
1160
+ * The first thing this agent reads, once, on the apply that **creates** it (`canonical-spec
1161
+ * §31.4`). It is what turns a provisioned crew into a crew that has started working.
1162
+ *
1163
+ * Neither of its neighbours can carry it. A `schedule` fires again and again; an intake happens
1164
+ * exactly once, at creation, and never on a later apply of the same agent. `system` is standing
1165
+ * instruction re-read every turn; an intake is a message the agent is sent.
1166
+ *
1167
+ * `budget_micro_usd` is optional here and required on a schedule, because a cron spends
1168
+ * repeatedly and unattended while an intake spends once, at an apply a person is watching. Absent
1169
+ * means the agent's own `budget` governs.
1170
+ */
1171
+ intake: z16.object({ message: z16.string().min(1), budget_micro_usd: MicroUsdField.nonnegative().optional() }).optional()
955
1172
  });
956
- var CredentialDeclSchema = z14.object({
1173
+ var CredentialDeclSchema = z16.object({
957
1174
  kind: VaultCredentialKindSchema,
958
- key: z14.string().min(1).max(128),
1175
+ key: z16.string().min(1).max(128),
959
1176
  /** The one exact destination host an `env_var` is bound to. */
960
- host: z14.string().min(1).optional(),
1177
+ host: z16.string().min(1).optional(),
961
1178
  /** 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) })
1179
+ mcp_server_url: z16.url().optional(),
1180
+ value: z16.object({ from_env: z16.string().min(1) })
964
1181
  });
965
- var VaultDeclSchema = z14.object({
1182
+ var VaultDeclSchema = z16.object({
966
1183
  name: VaultSchema.shape.display_name,
967
- identity: z14.string().min(1).optional(),
968
- credentials: z14.array(CredentialDeclSchema)
1184
+ identity: z16.string().min(1).optional(),
1185
+ credentials: z16.array(CredentialDeclSchema)
969
1186
  });
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)
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)
973
1190
  });
974
1191
  var IdentityDeclSchema = IdentitySchema.pick({ name: true, description: true }).partial({
975
1192
  description: true
976
1193
  });
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([])
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([])
983
1200
  });
984
- var KeptSchema = z14.object({ agents: z14.array(z14.string().min(1)).default([]) });
985
- var ProjectSchema = z14.object({
986
- name: z14.string().min(1),
1201
+ var KeptSchema = z16.object({ agents: z16.array(z16.string().min(1)).default([]) });
1202
+ var ProjectSchema = z16.object({
1203
+ name: z16.string().min(1),
987
1204
  /** The machine this project runs: its repo, its screens, its `/api/*` (`template.ts`). */
988
- blueprint: z14.string().min(1).optional(),
1205
+ blueprint: z16.string().min(1).optional(),
989
1206
  /** 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([]),
1207
+ template: z16.string().min(1).optional(),
1208
+ /**
1209
+ * What a person is asked BEFORE the crew is provisioned (`canonical-spec §31.2`).
1210
+ *
1211
+ * `QuestionField` is reused rather than restated: a setup question and a question a parked agent
1212
+ * asks mid-run are the same object asked at different times, so a blueprint author writes one
1213
+ * shape and one component renders both. These are published on the artifact — they are the whole
1214
+ * reason the catalog has a `questions` column — and the answers a person gives go on the install
1215
+ * (§29.6), never into the tree, because the tree is identical for everyone.
1216
+ *
1217
+ * **Configuration only.** A secret is a vault credential or an app secret; neither is an answer.
1218
+ */
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([]),
996
1225
  removed: RemovedSchema.default({ apps: [], agents: [], skills: [], identities: [], vaults: [] }),
997
1226
  kept: KeptSchema.default({ agents: [] })
998
1227
  });
@@ -1168,6 +1397,48 @@ async function loadConfig(dir) {
1168
1397
  return validate(path, raw);
1169
1398
  }
1170
1399
 
1400
+ // src/files.ts
1401
+ import { createHash } from "node:crypto";
1402
+ import { readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
1403
+ import { join as join2, relative, sep } from "node:path";
1404
+ var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
1405
+ var MAX_FILE_BYTES = 8 * 1024 * 1024;
1406
+ function collectFiles(source) {
1407
+ if (typeof source !== "string") return fromMap(source);
1408
+ const root = source;
1409
+ const files = {};
1410
+ const walk = (dir) => {
1411
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1412
+ if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".env")) continue;
1413
+ const full = join2(dir, entry.name);
1414
+ if (entry.isDirectory()) walk(full);
1415
+ else if (entry.isFile() && statSync(full).size <= MAX_FILE_BYTES) {
1416
+ files[relative(root, full).split(sep).join("/")] = readFileSync2(full).toString("base64");
1417
+ }
1418
+ }
1419
+ };
1420
+ walk(root);
1421
+ return files;
1422
+ }
1423
+ function fromMap(map) {
1424
+ const files = {};
1425
+ for (const [path, base64] of Object.entries(map)) {
1426
+ const segments = path.split("/");
1427
+ if (segments.some((segment) => SKIP_DIRS.has(segment) || segment.startsWith(".env"))) continue;
1428
+ if (Buffer.from(base64, "base64").length > MAX_FILE_BYTES) continue;
1429
+ files[path] = base64;
1430
+ }
1431
+ return files;
1432
+ }
1433
+ function contentHash(files) {
1434
+ const hash = createHash("sha256");
1435
+ for (const [path, base64] of Object.entries(files).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
1436
+ const bytes = Buffer.from(base64, "base64");
1437
+ hash.update(`${path}\0${bytes.length}\0`).update(bytes);
1438
+ }
1439
+ return hash.digest("hex");
1440
+ }
1441
+
1171
1442
  // src/up.ts
1172
1443
  import { createHash as createHash3 } from "node:crypto";
1173
1444
  import { readFileSync as readFileSync3 } from "node:fs";
@@ -1175,7 +1446,7 @@ import { resolve as resolve3 } from "node:path";
1175
1446
 
1176
1447
  // src/report.ts
1177
1448
  import { ApiError, send } from "@usenaive-sdk/vetta";
1178
- import { z as z15 } from "zod";
1449
+ import { z as z17 } from "zod";
1179
1450
  function message2(error) {
1180
1451
  return error instanceof Error ? error.message : String(error);
1181
1452
  }
@@ -1205,10 +1476,10 @@ async function absent(work) {
1205
1476
  throw error;
1206
1477
  }
1207
1478
  }
1208
- var PageShape = z15.object({
1209
- data: z15.array(z15.unknown()),
1210
- has_more: z15.boolean(),
1211
- next_cursor: z15.string().nullable()
1479
+ var PageShape = z17.object({
1480
+ data: z17.array(z17.unknown()),
1481
+ has_more: z17.boolean(),
1482
+ next_cursor: z17.string().nullable()
1212
1483
  });
1213
1484
  function rowLabel(row) {
1214
1485
  const { object, id } = row ?? {};
@@ -1241,7 +1512,7 @@ async function tombstone(name, current, remove, dryRun) {
1241
1512
 
1242
1513
  // src/agents.ts
1243
1514
  function desiredTools(decl, current, ids) {
1244
- if (decl.allowed_apps === void 0) return decl.tools;
1515
+ if (ids === void 0) return decl.tools;
1245
1516
  const base = decl.tools ?? current?.tools ?? { default_config: { permission: "allow" }, configs: {} };
1246
1517
  const appsTool = base.configs["apps"] ?? { enabled: true, permission: base.default_config.permission };
1247
1518
  return {
@@ -1266,14 +1537,14 @@ async function createAgent(client, decl, tools, identityId) {
1266
1537
  if (identityId !== void 0) await client.identities.attach(created.id, identityId);
1267
1538
  return { name: decl.name, action: "created", id: created.id };
1268
1539
  }
1269
- async function upsertAgent(client, decl, existing, ids, dryRun) {
1540
+ async function upsertAgent(client, decl, existing, ids, dryRun, scoped) {
1270
1541
  const unresolved = (decl.allowed_apps ?? []).filter((name) => !ids.apps.has(name));
1271
1542
  const identityId = decl.identity === void 0 ? void 0 : ids.identities.get(decl.identity);
1272
1543
  const missing = unresolved.length > 0 ? `allowed_apps needs app "${unresolved[0]}"` : decl.identity !== void 0 && identityId === void 0 ? `identity needs "${decl.identity}"` : null;
1273
1544
  if (missing !== null && !dryRun) {
1274
1545
  return { name: decl.name, action: "refused", reason: `${missing}, which was not provisioned in this run` };
1275
1546
  }
1276
- const appIds = (decl.allowed_apps ?? []).flatMap((name) => ids.apps.get(name) ?? []);
1547
+ const appIds = decl.allowed_apps !== void 0 ? decl.allowed_apps.flatMap((name) => ids.apps.get(name) ?? []) : scoped;
1277
1548
  const current = existing.find((agent) => agent.name === decl.name) ?? null;
1278
1549
  const tools = desiredTools(decl, current, appIds);
1279
1550
  if (!current) return dryRun ? { name: decl.name, action: "created" } : createAgent(client, decl, tools, identityId);
@@ -1295,6 +1566,15 @@ async function upsertAgent(client, decl, existing, ids, dryRun) {
1295
1566
  }
1296
1567
  return { name: decl.name, action: "updated", id: current.id };
1297
1568
  }
1569
+ async function openIntake(client, agent, decl, dryRun) {
1570
+ if (dryRun || agent.id === void 0) return { name: agent.name, action: "created" };
1571
+ const started = await client.sessions.create({
1572
+ agent_id: agent.id,
1573
+ message: decl.message,
1574
+ ...decl.budget_micro_usd !== void 0 ? { budget_micro_usd: decl.budget_micro_usd } : {}
1575
+ });
1576
+ return { name: agent.name, action: "created", id: started.id };
1577
+ }
1298
1578
  async function upsertSchedule(client, agent, decl, current, identityIds, dryRun) {
1299
1579
  const name = `${agent.name} @ ${decl.cron}`;
1300
1580
  if (agent.action === "refused") throw new Error(`agent "${agent.name}" was not provisioned in this run`);
@@ -1337,40 +1617,8 @@ async function reconcileSchedules(client, agent, declared, live, identityIds, dr
1337
1617
  }
1338
1618
 
1339
1619
  // src/apps.ts
1340
- import { createHash as createHash2 } from "node:crypto";
1620
+ import { createHash as createHash2, randomBytes } from "node:crypto";
1341
1621
  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
1622
  async function findApp(client, name, skipped) {
1375
1623
  const rows = await listAll(client.config, "/v1/apps", AppSchema, skipped, { name });
1376
1624
  return rows.find((app) => app.name === name) ?? null;
@@ -1381,25 +1629,74 @@ function ownershipRefusal(name, project, owner) {
1381
1629
  return `app "${name}" is owned by ${held}; this project is "${project}". Rename the app in your config, or re-run \`naive up --adopt\` to ${verb} it.`;
1382
1630
  }
1383
1631
  var ensureApp = (client, body) => client.apps.create(body);
1384
- function resolveEnv(decl) {
1385
- const env = {};
1386
- for (const [name, value] of Object.entries(decl.env ?? {})) {
1632
+ var PLATFORM_ENV = {
1633
+ /** The base URL of the API this apply ran through. */
1634
+ api_url: "NAIVE_API_URL",
1635
+ /** The identity the install runs as, when the declaration declares exactly one. */
1636
+ identity_id: "NAIVE_IDENTITY_ID",
1637
+ /** A scoped organization API key minted for the app that asks for it. */
1638
+ api_key: "NAIVE_API_KEY"
1639
+ };
1640
+ function planEnv(decl, opts) {
1641
+ if (decl.env === void 0) return [];
1642
+ const plan = /* @__PURE__ */ new Map();
1643
+ for (const [name, value] of Object.entries(decl.env)) {
1387
1644
  if (typeof value === "string") {
1388
- env[name] = value;
1645
+ plan.set(name, { name, value });
1646
+ continue;
1647
+ }
1648
+ if ("generate" in value) {
1649
+ plan.set(name, { name, invent: async () => generated() });
1389
1650
  continue;
1390
1651
  }
1391
- const local = process.env[value.from_env];
1392
- if (local === void 0) throw new Error(`env ${name} reads local variable ${value.from_env}, which is not set`);
1393
- env[name] = local;
1652
+ const known = opts.platform?.known[value.from_env];
1653
+ const mint = opts.platform?.minted?.[value.from_env];
1654
+ if (known !== void 0) plan.set(name, { name, value: known });
1655
+ else if (mint !== void 0) plan.set(name, { name, invent: () => mint(decl.name) });
1656
+ else {
1657
+ const local = opts.env[value.from_env];
1658
+ if (local === void 0) throw new Error(`env ${name} reads local variable ${value.from_env}, which is not set`);
1659
+ plan.set(name, { name, value: local });
1660
+ }
1661
+ }
1662
+ for (const [name, value] of Object.entries(opts.platform?.known ?? {})) plan.set(name, { name, value });
1663
+ return [...plan.values()];
1664
+ }
1665
+ async function materialize(plan, held, dryRun) {
1666
+ const values = {};
1667
+ const unmade = [];
1668
+ for (const one of plan) {
1669
+ if (one.value !== void 0) values[one.name] = one.value;
1670
+ else if (held.has(one.name)) continue;
1671
+ else if (dryRun) unmade.push(one.name);
1672
+ else values[one.name] = await invent(one);
1394
1673
  }
1395
- return env;
1674
+ return { values, unmade };
1396
1675
  }
1676
+ async function invent(one) {
1677
+ try {
1678
+ return await one.invent();
1679
+ } catch (cause) {
1680
+ throw new Error(
1681
+ `env ${one.name} is provisioned by the platform, and could not be: ${cause instanceof Error ? cause.message : String(cause)}`
1682
+ );
1683
+ }
1684
+ }
1685
+ var generated = () => randomBytes(32).toString("base64url");
1686
+ var EMPTY = /* @__PURE__ */ new Set();
1397
1687
  function secretHash(name, value) {
1398
1688
  return createHash2("sha256").update(`${name}\0`).update(value).digest("hex");
1399
1689
  }
1400
- function localTree(decl, dir) {
1690
+ function builtTree(decl, opts) {
1401
1691
  if (!decl.deploy_dir) return null;
1402
- const files = collectFiles(resolve2(dir, decl.deploy_dir));
1692
+ const hosted = opts.trees;
1693
+ if (hosted !== void 0 && hosted[decl.name] === void 0) {
1694
+ const failed = opts.tree_errors?.[decl.name];
1695
+ throw new Error(
1696
+ `app "${decl.name}" declares deploy_dir "${decl.deploy_dir}", but this apply was handed no built tree for it` + (failed === void 0 ? "" : `: ${failed}`)
1697
+ );
1698
+ }
1699
+ const files = collectFiles(hosted?.[decl.name] ?? resolve2(opts.dir, decl.deploy_dir));
1403
1700
  return { files, hash: contentHash(files) };
1404
1701
  }
1405
1702
  async function writeApp(client, id, env, tree) {
@@ -1419,7 +1716,7 @@ async function settleDeploy(client, id) {
1419
1716
  await new Promise((resolve4) => setTimeout(resolve4, SETTLE_MS));
1420
1717
  }
1421
1718
  }
1422
- async function createApp(client, decl, env, opts, appIds) {
1719
+ async function createApp(client, decl, env, tree, opts, appIds) {
1423
1720
  const created = await ensureApp(client, {
1424
1721
  name: decl.name,
1425
1722
  type: decl.type,
@@ -1428,12 +1725,12 @@ async function createApp(client, decl, env, opts, appIds) {
1428
1725
  ...decl.mcp !== void 0 ? { mcp: decl.mcp } : {}
1429
1726
  });
1430
1727
  appIds.set(decl.name, created.id);
1431
- await writeApp(client, created.id, env, localTree(decl, opts.dir));
1728
+ await writeApp(client, created.id, env, tree);
1432
1729
  return { name: decl.name, action: "created", id: created.id, url: (await client.apps.get(created.id)).url };
1433
1730
  }
1434
1731
  async function upsertApp(client, decl, opts, appIds) {
1435
- const env = resolveEnv(decl);
1436
- const names = Object.keys(env);
1732
+ const declared = planEnv(decl, opts);
1733
+ const names = declared.map((one) => one.name);
1437
1734
  const note = (notes) => notes.length > 0 ? { reason: notes.join("; ") } : {};
1438
1735
  const mcpPlan = decl.mcp !== void 0 ? [`will set mcp ${decl.mcp}`] : [];
1439
1736
  const current = await findApp(client, decl.name, opts.skipped);
@@ -1446,7 +1743,8 @@ async function upsertApp(client, decl, opts, appIds) {
1446
1743
  ...note([...willSet, ...mcpPlan, ...decl.deploy_dir ? [`will deploy ${decl.deploy_dir}`] : []])
1447
1744
  };
1448
1745
  }
1449
- return createApp(client, decl, env, opts, appIds);
1746
+ const { values } = await materialize(declared, EMPTY, false);
1747
+ return createApp(client, decl, values, builtTree(decl, opts), opts, appIds);
1450
1748
  }
1451
1749
  const owned = current.project === opts.project;
1452
1750
  if (!owned && !opts.adopt) {
@@ -1471,19 +1769,21 @@ async function upsertApp(client, decl, opts, appIds) {
1471
1769
  if (!owned && !opts.dryRun) {
1472
1770
  await ensureApp(client, { name: decl.name, type: decl.type, project: opts.project, adopt: true });
1473
1771
  }
1474
- const live = decl.env === void 0 ? [] : (await client.apps.secrets(current.id)).data;
1475
- const unmanaged = live.map((secret) => secret.name).filter((name) => !(name in env));
1772
+ const live = declared.length === 0 ? [] : (await client.apps.secrets(current.id)).data;
1773
+ const managed = new Set(names);
1774
+ const unmanaged = live.map((secret) => secret.name).filter((name) => !managed.has(name));
1476
1775
  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);
1477
1777
  const staleEnv = Object.fromEntries(
1478
1778
  Object.entries(env).filter(([name, value]) => held.get(name) !== secretHash(name, value))
1479
1779
  );
1480
- const staleNames = Object.keys(staleEnv);
1780
+ const staleNames = [...Object.keys(staleEnv), ...unmade];
1481
1781
  const patch = {
1482
1782
  ...decl.description !== void 0 && decl.description !== current.description ? { description: decl.description } : {},
1483
1783
  ...(decl.mcp ?? null) !== current.mcp ? { mcp: decl.mcp ?? null } : {}
1484
1784
  };
1485
1785
  const drifted = Object.keys(patch).length > 0;
1486
- const tree = localTree(decl, opts.dir);
1786
+ const tree = builtTree(decl, opts);
1487
1787
  const newest = tree ? (await client.apps.deployments(current.id, { limit: 1 })).data[0] : void 0;
1488
1788
  const stale = tree !== null && newest?.content_hash !== tree.hash;
1489
1789
  const action = !owned || drifted || stale || staleNames.length > 0 ? "updated" : "unchanged";
@@ -1531,14 +1831,14 @@ async function upsertVault(client, decl, existing, identityIds, dryRun) {
1531
1831
  }
1532
1832
  return { name: decl.name, action: "unchanged", id: current.id };
1533
1833
  }
1534
- async function upsertCredential(client, vault, decl, existing, dryRun) {
1834
+ async function upsertCredential(client, vault, decl, existing, dryRun, from) {
1535
1835
  const name = `${vault.name} / ${decl.key}`;
1536
1836
  if (vault.action === "refused") throw new Error(`vault "${vault.name}" was not provisioned in this run`);
1537
1837
  const current = existing.find(
1538
1838
  (row) => row.kind === decl.kind && row.key === decl.key && row.host === (decl.host ?? null) && row.mcp_server_url === (decl.mcp_server_url ?? null)
1539
1839
  );
1540
1840
  if (current) return { name, action: "unchanged", id: current.id };
1541
- const value = process.env[decl.value.from_env];
1841
+ const value = from[decl.value.from_env];
1542
1842
  if (value === void 0) throw new Error(`credential ${decl.key} reads local variable ${decl.value.from_env}, which is not set`);
1543
1843
  if (dryRun || vault.id === void 0) return { name, action: "created" };
1544
1844
  const created = await client.vaults.credentials.create(vault.id, {
@@ -1552,7 +1852,17 @@ async function upsertCredential(client, vault, decl, existing, dryRun) {
1552
1852
  }
1553
1853
 
1554
1854
  // src/up.ts
1855
+ function identityEnv(config, ids) {
1856
+ if (config.identities.length !== 1) return {};
1857
+ const id = ids.get(config.identities[0].name);
1858
+ return id === void 0 ? {} : { [PLATFORM_ENV.identity_id]: id };
1859
+ }
1555
1860
  async function upsertSkill(client, decl, opts) {
1861
+ if (opts.hosted) {
1862
+ throw new Error(
1863
+ `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
+ );
1865
+ }
1556
1866
  const content = readFileSync3(resolve3(opts.dir, decl.file), "utf8");
1557
1867
  const sha256 = createHash3("sha256").update(content).digest("hex");
1558
1868
  const current = await absent(client.skills.get(decl.slug));
@@ -1629,10 +1939,11 @@ async function up(config, client, opts = {}) {
1629
1939
  const dryRun = opts.dry_run ?? false;
1630
1940
  const dir = opts.dir ?? process.cwd();
1631
1941
  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: [] };
1942
+ const report = { dry_run: dryRun, ...applied(config), skills: [], identities: [], vaults: [], apps: [], agents: [], schedules: [], intake: [], skipped: [] };
1633
1943
  const owner = { project: config.name, adopt: opts.adopt ?? false, skipped: report.skipped };
1944
+ const hosted = opts.trees !== void 0;
1634
1945
  for (const decl of config.skills) {
1635
- await attempt(report.skills, decl.slug, () => upsertSkill(client, decl, { dryRun, dir }));
1946
+ await attempt(report.skills, decl.slug, () => upsertSkill(client, decl, { dryRun, dir, hosted }));
1636
1947
  }
1637
1948
  for (const decl of config.identities) {
1638
1949
  await attempt(report.identities, decl.name, () => upsertIdentity(client, decl, dryRun, ids.identities));
@@ -1643,29 +1954,59 @@ async function up(config, client, opts = {}) {
1643
1954
  const vaultId = line.action === "unchanged" ? line.id : void 0;
1644
1955
  const rows = vaultId === void 0 ? [] : await listAll(client.config, `/v1/vaults/${vaultId}/credentials`, VaultCredentialSchema, report.skipped);
1645
1956
  for (const cred of decl.credentials) {
1646
- await attempt(report.vaults, `${decl.name} / ${cred.key}`, () => upsertCredential(client, line, cred, rows, dryRun));
1957
+ await attempt(
1958
+ report.vaults,
1959
+ `${decl.name} / ${cred.key}`,
1960
+ () => upsertCredential(client, line, cred, rows, dryRun, opts.env ?? process.env)
1961
+ );
1647
1962
  }
1648
1963
  }
1964
+ const platform = opts.platform && {
1965
+ ...opts.platform,
1966
+ known: { ...opts.platform.known, ...identityEnv(config, ids.identities) }
1967
+ };
1649
1968
  for (const decl of config.apps) {
1650
- await attempt(report.apps, decl.name, () => upsertApp(client, decl, { ...owner, dryRun, dir }, ids.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
1977
+ )
1978
+ );
1651
1979
  }
1652
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) : [];
1653
1981
  const deployments = config.agents.some((decl) => decl.schedules !== void 0) ? await listAll(client.config, "/v1/deployments", DeploymentSchema, report.skipped) : [];
1982
+ const scoped = config.apps.length > 0 ? [...ids.apps.values()] : void 0;
1983
+ const briefed = [];
1654
1984
  for (const decl of config.agents) {
1655
- const line = await attempt(report.agents, decl.name, () => upsertAgent(client, decl, existing, ids, dryRun));
1985
+ const line = await attempt(
1986
+ report.agents,
1987
+ decl.name,
1988
+ () => upsertAgent(client, decl, existing, ids, dryRun, scoped)
1989
+ );
1656
1990
  if (decl.schedules !== void 0) {
1657
1991
  await reconcileSchedules(client, line, decl.schedules, deployments, ids.identities, dryRun, report.schedules);
1658
1992
  }
1993
+ if (decl.intake !== void 0 && line.action === "created") briefed.push([line, decl.intake]);
1659
1994
  }
1660
1995
  reportKept(config, existing, report.agents);
1661
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));
1999
+ }
1662
2000
  return report;
1663
2001
  }
1664
2002
  export {
1665
2003
  BLUEPRINTS,
1666
2004
  BlueprintRefusal,
1667
2005
  CONFIG_CANDIDATES,
2006
+ PLATFORM_ENV,
1668
2007
  chooseTemplate,
2008
+ collectFiles,
2009
+ contentHash,
1669
2010
  defineProject,
1670
2011
  loadConfig,
1671
2012
  parseProject,