@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.d.ts +60 -1
- package/dist/index.js +335 -134
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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/
|
|
832
|
+
// ../core/src/schema/install.ts
|
|
772
833
|
import { z as z12 } from "zod";
|
|
773
|
-
var
|
|
774
|
-
var
|
|
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:
|
|
926
|
+
object: z14.literal("vault"),
|
|
777
927
|
identity_id: idOf("idn").nullable(),
|
|
778
|
-
display_name:
|
|
779
|
-
credential_count:
|
|
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 =
|
|
932
|
+
var VaultCredentialSchema = z14.object({
|
|
783
933
|
id: idOf("vcr"),
|
|
784
|
-
object:
|
|
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:
|
|
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:
|
|
940
|
+
host: z14.string().nullable(),
|
|
791
941
|
/** The exact MCP server URL an `mcp_*` token is matched to. */
|
|
792
|
-
mcp_server_url:
|
|
793
|
-
placeholder_ref:
|
|
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
|
|
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 =
|
|
823
|
-
var SocialTextPlatformSchema =
|
|
824
|
-
var SocialAccountSchema =
|
|
825
|
-
id:
|
|
826
|
-
object:
|
|
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:
|
|
830
|
-
display_name:
|
|
831
|
-
avatar_url:
|
|
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 =
|
|
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 =
|
|
843
|
-
id:
|
|
844
|
-
object:
|
|
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:
|
|
847
|
-
platforms:
|
|
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:
|
|
1001
|
+
error: z15.string().nullable()
|
|
852
1002
|
});
|
|
853
|
-
var SocialUploadSchema =
|
|
854
|
-
id:
|
|
855
|
-
object:
|
|
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:
|
|
858
|
-
url:
|
|
1007
|
+
type: z15.enum(["image", "video", "document"]),
|
|
1008
|
+
url: z15.url().nullable()
|
|
859
1009
|
});
|
|
860
|
-
var SocialAnalyticsSchema =
|
|
861
|
-
object:
|
|
1010
|
+
var SocialAnalyticsSchema = z15.object({
|
|
1011
|
+
object: z15.literal("social_analytics"),
|
|
862
1012
|
identity_id: idOf("idn"),
|
|
863
|
-
resource_id:
|
|
1013
|
+
resource_id: z15.string().min(1),
|
|
864
1014
|
platform: SocialPlatformSchema.nullable(),
|
|
865
|
-
data:
|
|
1015
|
+
data: z15.unknown()
|
|
866
1016
|
});
|
|
867
|
-
var SocialCommentSchema =
|
|
868
|
-
id:
|
|
869
|
-
object:
|
|
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:
|
|
872
|
-
platforms:
|
|
1021
|
+
post_id: z15.string().min(1),
|
|
1022
|
+
platforms: z15.array(SocialPlatformSchema),
|
|
873
1023
|
status: SocialPostStatusSchema,
|
|
874
1024
|
posted_at: Timestamp.nullable(),
|
|
875
|
-
data:
|
|
1025
|
+
data: z15.unknown()
|
|
876
1026
|
});
|
|
877
1027
|
|
|
878
1028
|
// src/define.ts
|
|
879
|
-
import { z as
|
|
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 =
|
|
919
|
-
var AppDeclSchema =
|
|
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:
|
|
1071
|
+
name: z16.string().regex(APP_NAME, "a lowercase DNS label, 63 characters or fewer"),
|
|
922
1072
|
type: AppTypeSchema,
|
|
923
|
-
description:
|
|
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:
|
|
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:
|
|
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 =
|
|
932
|
-
cron:
|
|
933
|
-
timezone:
|
|
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:
|
|
1085
|
+
input: z16.string().min(1),
|
|
936
1086
|
budget_micro_usd: MicroUsdField.nonnegative(),
|
|
937
|
-
identity:
|
|
938
|
-
enabled:
|
|
1087
|
+
identity: z16.string().min(1).optional(),
|
|
1088
|
+
enabled: z16.boolean().optional()
|
|
939
1089
|
});
|
|
940
|
-
var AgentDeclSchema =
|
|
941
|
-
name:
|
|
942
|
-
model:
|
|
1090
|
+
var AgentDeclSchema = z16.object({
|
|
1091
|
+
name: z16.string().min(1),
|
|
1092
|
+
model: z16.string().min(1),
|
|
943
1093
|
budget: BudgetSchema,
|
|
944
|
-
description:
|
|
945
|
-
system:
|
|
1094
|
+
description: z16.string().optional(),
|
|
1095
|
+
system: z16.string().optional(),
|
|
946
1096
|
tools: ToolsetSchema.optional(),
|
|
947
|
-
skills:
|
|
948
|
-
mcp_servers:
|
|
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:
|
|
1100
|
+
allowed_apps: z16.array(z16.string()).optional(),
|
|
951
1101
|
/** A declared identity *name*; `up` attaches it after the agent upsert (§22). */
|
|
952
|
-
identity:
|
|
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:
|
|
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 =
|
|
1119
|
+
var CredentialDeclSchema = z16.object({
|
|
957
1120
|
kind: VaultCredentialKindSchema,
|
|
958
|
-
key:
|
|
1121
|
+
key: z16.string().min(1).max(128),
|
|
959
1122
|
/** The one exact destination host an `env_var` is bound to. */
|
|
960
|
-
host:
|
|
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:
|
|
963
|
-
value:
|
|
1125
|
+
mcp_server_url: z16.url().optional(),
|
|
1126
|
+
value: z16.object({ from_env: z16.string().min(1) })
|
|
964
1127
|
});
|
|
965
|
-
var VaultDeclSchema =
|
|
1128
|
+
var VaultDeclSchema = z16.object({
|
|
966
1129
|
name: VaultSchema.shape.display_name,
|
|
967
|
-
identity:
|
|
968
|
-
credentials:
|
|
1130
|
+
identity: z16.string().min(1).optional(),
|
|
1131
|
+
credentials: z16.array(CredentialDeclSchema)
|
|
969
1132
|
});
|
|
970
|
-
var SkillDeclSchema =
|
|
971
|
-
slug:
|
|
972
|
-
file:
|
|
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 =
|
|
978
|
-
apps:
|
|
979
|
-
agents:
|
|
980
|
-
skills:
|
|
981
|
-
identities:
|
|
982
|
-
vaults:
|
|
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 =
|
|
985
|
-
var ProjectSchema =
|
|
986
|
-
name:
|
|
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:
|
|
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:
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
|
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 =
|
|
1209
|
-
data:
|
|
1210
|
-
has_more:
|
|
1211
|
-
next_cursor:
|
|
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,
|