@bluewombat/runtime 0.5.0 → 0.7.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/README.md +142 -57
- package/SPECS.md +35 -18
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/agent-shorthand.d.ts +45 -0
- package/dist/config/agent-shorthand.d.ts.map +1 -0
- package/dist/config/agent-shorthand.js +256 -0
- package/dist/config/agent-shorthand.js.map +1 -0
- package/dist/config/load-config.d.ts.map +1 -1
- package/dist/config/load-config.js +121 -24
- package/dist/config/load-config.js.map +1 -1
- package/dist/config/parse-args.d.ts.map +1 -1
- package/dist/config/parse-args.js +84 -24
- package/dist/config/parse-args.js.map +1 -1
- package/dist/config/types.d.ts +32 -12
- package/dist/config/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/loop/deliveries.d.ts +16 -0
- package/dist/loop/deliveries.d.ts.map +1 -1
- package/dist/loop/deliveries.js +25 -0
- package/dist/loop/deliveries.js.map +1 -1
- package/dist/loop/drive.d.ts.map +1 -1
- package/dist/loop/drive.js +15 -1
- package/dist/loop/drive.js.map +1 -1
- package/dist/loop/open-host.d.ts +1 -1
- package/dist/loop/open-host.d.ts.map +1 -1
- package/dist/loop/open-host.js +13 -1
- package/dist/loop/open-host.js.map +1 -1
- package/dist/loop/report.d.ts.map +1 -1
- package/dist/loop/report.js +20 -0
- package/dist/loop/report.js.map +1 -1
- package/dist/loop/tick.d.ts +3 -1
- package/dist/loop/tick.d.ts.map +1 -1
- package/dist/loop/tick.js.map +1 -1
- package/dist/loop/transformers.d.ts +9 -2
- package/dist/loop/transformers.d.ts.map +1 -1
- package/dist/loop/transformers.js +31 -29
- package/dist/loop/transformers.js.map +1 -1
- package/dist/loop/warm.d.ts +21 -0
- package/dist/loop/warm.d.ts.map +1 -0
- package/dist/loop/warm.js +59 -0
- package/dist/loop/warm.js.map +1 -0
- package/dist/operator/agent-plugins.d.ts +14 -0
- package/dist/operator/agent-plugins.d.ts.map +1 -0
- package/dist/operator/agent-plugins.js +149 -0
- package/dist/operator/agent-plugins.js.map +1 -0
- package/dist/operator/doctor.d.ts.map +1 -1
- package/dist/operator/doctor.js +27 -20
- package/dist/operator/doctor.js.map +1 -1
- package/dist/operator/film.js +5 -0
- package/dist/operator/film.js.map +1 -1
- package/dist/operator/init.js +14 -5
- package/dist/operator/init.js.map +1 -1
- package/dist/operator/live.js +9 -7
- package/dist/operator/live.js.map +1 -1
- package/dist/operator/release.d.ts +18 -0
- package/dist/operator/release.d.ts.map +1 -0
- package/dist/operator/release.js +65 -0
- package/dist/operator/release.js.map +1 -0
- package/package.json +15 -15
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* The short way to write an agent pass: which agent, which prompt, which
|
|
4
|
+
* plugins — and Host writes the argv a pass has always been.
|
|
5
|
+
*
|
|
6
|
+
* agents:
|
|
7
|
+
* cursor: { adapter: cursor, home: ./.mason/agent-home, apiKeyEnv: CURSOR_API_KEY,
|
|
8
|
+
* transcripts: ./.mason/transcripts, pluginsOut: ./.mason/plugins }
|
|
9
|
+
* builder:
|
|
10
|
+
* producer: { agent: cursor, prompt: ./prompts/builder.md,
|
|
11
|
+
* plugins: [./ai-plugins/core/builder], profile: builder, timeoutMs: … }
|
|
12
|
+
*
|
|
13
|
+
* expands to
|
|
14
|
+
*
|
|
15
|
+
* node <slots>/builders/producer.mjs --prompt-file <prompt> --
|
|
16
|
+
* node <slots>/agents/cursor.mjs --transcript-dir … --agent-home … --api-key-env …
|
|
17
|
+
* --plugin-dir <core> --profile builder --plugins-out …
|
|
18
|
+
*
|
|
19
|
+
* The expansion is the whole feature: nothing downstream knows a pass was
|
|
20
|
+
* written short, so `cmd` stays the one shape the rest of Host reads, and a
|
|
21
|
+
* pass that needs something the short form cannot say still writes `cmd`.
|
|
22
|
+
*/
|
|
23
|
+
/** Where the shipped slots live, next to the config: what `npm install` in a Mason home makes. */
|
|
24
|
+
const SLOTS_PACKAGE = "node_modules/@bluewombat/slots";
|
|
25
|
+
/** The shipped role slot behind each pass that drives an agent. */
|
|
26
|
+
export const ROLE_SCRIPTS = {
|
|
27
|
+
planner: "planners/producer.mjs",
|
|
28
|
+
"builder.producer": "builders/producer.mjs",
|
|
29
|
+
"builder.repair": "builders/repair.mjs",
|
|
30
|
+
"assembly.fix": "assembly/fix.mjs",
|
|
31
|
+
"assembly.validate": "assembly/validate.mjs",
|
|
32
|
+
"authority.describe": "messages/git-producer.mjs",
|
|
33
|
+
};
|
|
34
|
+
const ADAPTERS = new Set(["cursor", "claude"]);
|
|
35
|
+
const AGENT_KEYS = new Set([
|
|
36
|
+
"adapter",
|
|
37
|
+
"bin",
|
|
38
|
+
"model",
|
|
39
|
+
"home",
|
|
40
|
+
"apiKeyEnv",
|
|
41
|
+
"transcripts",
|
|
42
|
+
"pluginsOut",
|
|
43
|
+
"args",
|
|
44
|
+
]);
|
|
45
|
+
/** The keys a pass written short may carry, besides its own (`timeoutMs`, `gates`). */
|
|
46
|
+
export const SHORT_PASS_KEYS = new Set(["agent", "prompt", "rules", "plugins", "profile", "args"]);
|
|
47
|
+
export function readAgents(value, configDir) {
|
|
48
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
49
|
+
return { ok: false, reason: 'Config "agents" must be a YAML mapping of agent names.' };
|
|
50
|
+
}
|
|
51
|
+
const agents = new Map();
|
|
52
|
+
for (const [name, raw] of Object.entries(value)) {
|
|
53
|
+
const key = `agents.${name}`;
|
|
54
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
55
|
+
return { ok: false, reason: `Config "${key}" must be a YAML mapping.` };
|
|
56
|
+
}
|
|
57
|
+
const object = raw;
|
|
58
|
+
for (const field of Object.keys(object)) {
|
|
59
|
+
if (!AGENT_KEYS.has(field)) {
|
|
60
|
+
return { ok: false, reason: `Config "${key}" has unknown key "${field}".` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const fields = {};
|
|
64
|
+
for (const field of [
|
|
65
|
+
"adapter",
|
|
66
|
+
"bin",
|
|
67
|
+
"model",
|
|
68
|
+
"home",
|
|
69
|
+
"apiKeyEnv",
|
|
70
|
+
"transcripts",
|
|
71
|
+
"pluginsOut",
|
|
72
|
+
]) {
|
|
73
|
+
const text = object[field];
|
|
74
|
+
if (text !== undefined && (typeof text !== "string" || text.trim().length === 0)) {
|
|
75
|
+
return { ok: false, reason: `Config "${key}.${field}" must be a non-empty string.` };
|
|
76
|
+
}
|
|
77
|
+
fields[field] = text;
|
|
78
|
+
}
|
|
79
|
+
const adapter = fields.adapter;
|
|
80
|
+
if (adapter === undefined) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
reason: `Config "${key}" needs an "adapter": cursor, claude, or the path of an agent wrapper.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if ((fields.home === undefined) !== (fields.apiKeyEnv === undefined)) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
reason: `Config "${key}" isolates an agent with "home" and "apiKeyEnv" together, or neither.`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const args = stringList(object.args, `${key}.args`);
|
|
93
|
+
if (args.ok === false) {
|
|
94
|
+
return args;
|
|
95
|
+
}
|
|
96
|
+
const at = (path) => path === undefined ? undefined : resolve(configDir, path);
|
|
97
|
+
const decl = {
|
|
98
|
+
adapter: ADAPTERS.has(adapter)
|
|
99
|
+
? resolve(configDir, SLOTS_PACKAGE, "agents", `${adapter}.mjs`)
|
|
100
|
+
: resolve(configDir, adapter),
|
|
101
|
+
args: args.value,
|
|
102
|
+
};
|
|
103
|
+
// A binary named bare is looked up on PATH; one written as a path is next to the config.
|
|
104
|
+
const bin = fields.bin;
|
|
105
|
+
if (bin !== undefined) {
|
|
106
|
+
decl.bin = bin.includes("/") ? resolve(configDir, bin) : bin;
|
|
107
|
+
}
|
|
108
|
+
if (fields.model !== undefined) {
|
|
109
|
+
decl.model = fields.model;
|
|
110
|
+
}
|
|
111
|
+
const home = at(fields.home);
|
|
112
|
+
if (home !== undefined) {
|
|
113
|
+
decl.home = home;
|
|
114
|
+
}
|
|
115
|
+
if (fields.apiKeyEnv !== undefined) {
|
|
116
|
+
decl.apiKeyEnv = fields.apiKeyEnv;
|
|
117
|
+
}
|
|
118
|
+
const transcripts = at(fields.transcripts);
|
|
119
|
+
if (transcripts !== undefined) {
|
|
120
|
+
decl.transcripts = transcripts;
|
|
121
|
+
}
|
|
122
|
+
const pluginsOut = at(fields.pluginsOut);
|
|
123
|
+
if (pluginsOut !== undefined) {
|
|
124
|
+
decl.pluginsOut = pluginsOut;
|
|
125
|
+
}
|
|
126
|
+
agents.set(name, decl);
|
|
127
|
+
}
|
|
128
|
+
return { ok: true, value: agents };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The argv a pass written short stands for. `object` is the pass's own
|
|
132
|
+
* mapping; its keys were checked by the caller.
|
|
133
|
+
*/
|
|
134
|
+
export function expandAgentPass(object, role, agents, configDir, key) {
|
|
135
|
+
if (object.cmd !== undefined) {
|
|
136
|
+
return { ok: false, reason: `Config "${key}" names "cmd" and "agent": write one of the two.` };
|
|
137
|
+
}
|
|
138
|
+
const name = object.agent;
|
|
139
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
140
|
+
return { ok: false, reason: `Config "${key}.agent" must name an agent of "agents".` };
|
|
141
|
+
}
|
|
142
|
+
const agent = agents.get(name);
|
|
143
|
+
if (agent === undefined) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
reason: `Config "${key}.agent" is "${name}", and "agents" declares no such agent.`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const prompt = pathField(object, "prompt", key, configDir);
|
|
150
|
+
if (prompt.ok === false) {
|
|
151
|
+
return prompt;
|
|
152
|
+
}
|
|
153
|
+
const rules = pathField(object, "rules", key, configDir);
|
|
154
|
+
if (rules.ok === false) {
|
|
155
|
+
return rules;
|
|
156
|
+
}
|
|
157
|
+
const read = pathField(object, "read", key, configDir);
|
|
158
|
+
if (read.ok === false) {
|
|
159
|
+
return read;
|
|
160
|
+
}
|
|
161
|
+
const plugins = stringList(object.plugins, `${key}.plugins`);
|
|
162
|
+
if (plugins.ok === false) {
|
|
163
|
+
return plugins;
|
|
164
|
+
}
|
|
165
|
+
const profile = object.profile;
|
|
166
|
+
if (profile !== undefined && (typeof profile !== "string" || profile.trim().length === 0)) {
|
|
167
|
+
return { ok: false, reason: `Config "${key}.profile" must be a deck profile name.` };
|
|
168
|
+
}
|
|
169
|
+
if (profile !== undefined && agent.pluginsOut === undefined) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
reason: `Config "${key}" names a deck profile, so "agents.${name}.pluginsOut" must say where it is built.`,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const args = stringList(object.args, `${key}.args`);
|
|
176
|
+
if (args.ok === false) {
|
|
177
|
+
return args;
|
|
178
|
+
}
|
|
179
|
+
const argv = ["node", resolve(configDir, SLOTS_PACKAGE, ROLE_SCRIPTS[role])];
|
|
180
|
+
if (prompt.value !== undefined) {
|
|
181
|
+
argv.push("--prompt-file", prompt.value);
|
|
182
|
+
}
|
|
183
|
+
if (rules.value !== undefined) {
|
|
184
|
+
argv.push("--rules-file", rules.value);
|
|
185
|
+
}
|
|
186
|
+
if (read.value !== undefined) {
|
|
187
|
+
argv.push("--read", read.value);
|
|
188
|
+
}
|
|
189
|
+
argv.push("--", "node", agent.adapter);
|
|
190
|
+
if (agent.bin !== undefined) {
|
|
191
|
+
argv.push("--bin", agent.bin);
|
|
192
|
+
}
|
|
193
|
+
if (agent.model !== undefined) {
|
|
194
|
+
argv.push("--model", agent.model);
|
|
195
|
+
}
|
|
196
|
+
if (agent.transcripts !== undefined) {
|
|
197
|
+
argv.push("--transcript-dir", agent.transcripts);
|
|
198
|
+
}
|
|
199
|
+
if (agent.home !== undefined && agent.apiKeyEnv !== undefined) {
|
|
200
|
+
argv.push("--agent-home", agent.home, "--api-key-env", agent.apiKeyEnv);
|
|
201
|
+
}
|
|
202
|
+
for (const dir of plugins.value) {
|
|
203
|
+
argv.push("--plugin-dir", resolve(configDir, dir));
|
|
204
|
+
}
|
|
205
|
+
if (profile !== undefined && agent.pluginsOut !== undefined) {
|
|
206
|
+
argv.push("--profile", profile, "--plugins-out", agent.pluginsOut);
|
|
207
|
+
}
|
|
208
|
+
for (const arg of [...agent.args, ...args.value]) {
|
|
209
|
+
argv.push("--agent-arg", arg);
|
|
210
|
+
}
|
|
211
|
+
return { ok: true, value: argv };
|
|
212
|
+
}
|
|
213
|
+
/** The agent and profile a pass's argv names, written short or not; for `doctor`. */
|
|
214
|
+
export function agentPartOf(argv) {
|
|
215
|
+
const split = argv.indexOf("--");
|
|
216
|
+
const agent = split === -1 ? [] : argv.slice(split + 1);
|
|
217
|
+
const found = {
|
|
218
|
+
pluginDirs: [],
|
|
219
|
+
};
|
|
220
|
+
agent.forEach((token, at) => {
|
|
221
|
+
const value = agent[at + 1];
|
|
222
|
+
if (value === undefined) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (token === "--profile") {
|
|
226
|
+
found.profile = value;
|
|
227
|
+
}
|
|
228
|
+
else if (token === "--plugins-out") {
|
|
229
|
+
found.pluginsOut = value;
|
|
230
|
+
}
|
|
231
|
+
else if (token === "--plugin-dir") {
|
|
232
|
+
found.pluginDirs.push(value);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
return found;
|
|
236
|
+
}
|
|
237
|
+
function pathField(object, field, key, configDir) {
|
|
238
|
+
const value = object[field];
|
|
239
|
+
if (value === undefined) {
|
|
240
|
+
return { ok: true, value: undefined };
|
|
241
|
+
}
|
|
242
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
243
|
+
return { ok: false, reason: `Config "${key}.${field}" must be a non-empty path.` };
|
|
244
|
+
}
|
|
245
|
+
return { ok: true, value: resolve(configDir, value) };
|
|
246
|
+
}
|
|
247
|
+
function stringList(value, key) {
|
|
248
|
+
if (value === undefined) {
|
|
249
|
+
return { ok: true, value: [] };
|
|
250
|
+
}
|
|
251
|
+
if (!Array.isArray(value) || value.some((token) => typeof token !== "string")) {
|
|
252
|
+
return { ok: false, reason: `Config "${key}" must be an array of strings.` };
|
|
253
|
+
}
|
|
254
|
+
return { ok: true, value: value };
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=agent-shorthand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-shorthand.js","sourceRoot":"","sources":["../../src/config/agent-shorthand.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../../src/config/load-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../../src/config/load-config.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAIV,cAAc,EAIf,MAAM,YAAY,CAAC;AAgCpB,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;CAAE,GACjD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA+KrD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { expandAgentPass, readAgents, SHORT_PASS_KEYS, } from "./agent-shorthand.js";
|
|
4
5
|
const KNOWN_KEYS = new Set([
|
|
5
6
|
"manager",
|
|
6
7
|
"managerOptions",
|
|
@@ -9,6 +10,7 @@ const KNOWN_KEYS = new Set([
|
|
|
9
10
|
"workspaceRoot",
|
|
10
11
|
"ledger",
|
|
11
12
|
"persist",
|
|
13
|
+
"agents",
|
|
12
14
|
"planner",
|
|
13
15
|
"builder",
|
|
14
16
|
"assembly",
|
|
@@ -18,9 +20,10 @@ const KNOWN_KEYS = new Set([
|
|
|
18
20
|
"maxRefusals",
|
|
19
21
|
"pollIntervalMs",
|
|
20
22
|
]);
|
|
21
|
-
const WORK_LINE_KEYS = new Set(["stable", "branch", "isolation", "isolationOptions"]);
|
|
23
|
+
const WORK_LINE_KEYS = new Set(["stable", "branch", "isolation", "isolationOptions", "warm"]);
|
|
22
24
|
const PASS_KEYS = new Set(["cmd", "timeoutMs"]);
|
|
23
|
-
const
|
|
25
|
+
const GATED_PASS_KEYS = new Set(["cmd", "timeoutMs", "gates"]);
|
|
26
|
+
const STAGE_KEYS = new Set(["producer", "repair", "maxAttempts"]);
|
|
24
27
|
const ASSEMBLY_KEYS = new Set(["fix", "validate", "maxAttempts", "gates"]);
|
|
25
28
|
const GATES_KEYS = new Set(["defaultTimeoutMs", "gates"]);
|
|
26
29
|
const AUTHORITY_KEYS = new Set(["enabled", "publish", "refresh", "describe"]);
|
|
@@ -97,6 +100,9 @@ export function loadConfig(path) {
|
|
|
97
100
|
if (workLine.isolationOptions !== undefined) {
|
|
98
101
|
invocation.workLineIsolationOptions = workLine.isolationOptions;
|
|
99
102
|
}
|
|
103
|
+
if (workLine.warm !== undefined) {
|
|
104
|
+
invocation.workLineWarm = workLine.warm;
|
|
105
|
+
}
|
|
100
106
|
}
|
|
101
107
|
const workspaceRoot = stringField(object, "workspaceRoot");
|
|
102
108
|
if (workspaceRoot.ok === false) {
|
|
@@ -119,29 +125,37 @@ export function loadConfig(path) {
|
|
|
119
125
|
if (persist.value !== undefined) {
|
|
120
126
|
invocation.persist = persist.value;
|
|
121
127
|
}
|
|
128
|
+
let agents = new Map();
|
|
129
|
+
if (object.agents !== undefined) {
|
|
130
|
+
const read = readAgents(object.agents, configDir);
|
|
131
|
+
if (read.ok === false) {
|
|
132
|
+
return read;
|
|
133
|
+
}
|
|
134
|
+
agents = read.value;
|
|
135
|
+
}
|
|
122
136
|
if (object.planner !== undefined) {
|
|
123
|
-
const planner =
|
|
137
|
+
const planner = readGatedPass(object.planner, configDir, "planner", agents);
|
|
124
138
|
if (planner.ok === false) {
|
|
125
139
|
return planner;
|
|
126
140
|
}
|
|
127
141
|
invocation.planner = planner.value;
|
|
128
142
|
}
|
|
129
143
|
if (object.builder !== undefined) {
|
|
130
|
-
const builder = readBuilder(object.builder, configDir);
|
|
144
|
+
const builder = readBuilder(object.builder, configDir, agents);
|
|
131
145
|
if (builder.ok === false) {
|
|
132
146
|
return builder;
|
|
133
147
|
}
|
|
134
148
|
invocation.builder = builder.value;
|
|
135
149
|
}
|
|
136
150
|
if (object.assembly !== undefined) {
|
|
137
|
-
const assembly = readAssembly(object.assembly, configDir);
|
|
151
|
+
const assembly = readAssembly(object.assembly, configDir, agents);
|
|
138
152
|
if (assembly.ok === false) {
|
|
139
153
|
return assembly;
|
|
140
154
|
}
|
|
141
155
|
invocation.assembly = assembly.value;
|
|
142
156
|
}
|
|
143
157
|
if (object.authority !== undefined) {
|
|
144
|
-
const authority = readAuthority(object.authority, configDir);
|
|
158
|
+
const authority = readAuthority(object.authority, configDir, agents);
|
|
145
159
|
if (authority.ok === false) {
|
|
146
160
|
return authority;
|
|
147
161
|
}
|
|
@@ -243,6 +257,14 @@ function readWorkLine(value, configDir) {
|
|
|
243
257
|
Array.isArray(isolationOptions))) {
|
|
244
258
|
return { ok: false, reason: 'Config "workLine.isolationOptions" must be a YAML mapping.' };
|
|
245
259
|
}
|
|
260
|
+
let warm;
|
|
261
|
+
if (object.warm !== undefined) {
|
|
262
|
+
const read = readPass(object.warm, configDir, "workLine.warm");
|
|
263
|
+
if (read.ok === false) {
|
|
264
|
+
return read;
|
|
265
|
+
}
|
|
266
|
+
warm = read.value;
|
|
267
|
+
}
|
|
246
268
|
return {
|
|
247
269
|
ok: true,
|
|
248
270
|
...(stable.value === undefined ? {} : { stable: resolve(configDir, stable.value) }),
|
|
@@ -251,6 +273,7 @@ function readWorkLine(value, configDir) {
|
|
|
251
273
|
...(isolationOptions === undefined
|
|
252
274
|
? {}
|
|
253
275
|
: { isolationOptions: isolationOptions }),
|
|
276
|
+
...(warm === undefined ? {} : { warm }),
|
|
254
277
|
};
|
|
255
278
|
}
|
|
256
279
|
/**
|
|
@@ -269,7 +292,7 @@ function readWorkLine(value, configDir) {
|
|
|
269
292
|
* shared work line and one that keeps every fold local are different products,
|
|
270
293
|
* and neither is the obvious one to assume.
|
|
271
294
|
*/
|
|
272
|
-
function readAuthority(value, configDir) {
|
|
295
|
+
function readAuthority(value, configDir, agents) {
|
|
273
296
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
274
297
|
return { ok: false, reason: 'Config "authority" must be a YAML mapping.' };
|
|
275
298
|
}
|
|
@@ -315,13 +338,26 @@ function readAuthority(value, configDir) {
|
|
|
315
338
|
};
|
|
316
339
|
// Optional: the Project's own words for what it submits.
|
|
317
340
|
const describe = object.describe;
|
|
318
|
-
if (describe !==
|
|
341
|
+
if (describe !== null && typeof describe === "object" && !Array.isArray(describe)) {
|
|
342
|
+
const short = describe;
|
|
343
|
+
for (const name of Object.keys(short)) {
|
|
344
|
+
if (!SHORT_PASS_KEYS.has(name)) {
|
|
345
|
+
return { ok: false, reason: `Config "authority.describe" has unknown key "${name}".` };
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const argv = expandAgentPass(short, "authority.describe", agents, configDir, "authority.describe");
|
|
349
|
+
if (argv.ok === false) {
|
|
350
|
+
return argv;
|
|
351
|
+
}
|
|
352
|
+
spec.describeArgv = argv.value;
|
|
353
|
+
}
|
|
354
|
+
else if (describe !== undefined) {
|
|
319
355
|
if (!Array.isArray(describe) ||
|
|
320
356
|
describe.length === 0 ||
|
|
321
357
|
describe.some((token) => typeof token !== "string")) {
|
|
322
358
|
return {
|
|
323
359
|
ok: false,
|
|
324
|
-
reason: 'Config "authority.describe" must be a non-empty array of strings.',
|
|
360
|
+
reason: 'Config "authority.describe" must be a non-empty array of strings, or an agent pass ({ agent: … }).',
|
|
325
361
|
};
|
|
326
362
|
}
|
|
327
363
|
spec.describeArgv = resolveArgv(describe, configDir);
|
|
@@ -483,10 +519,67 @@ function readPass(value, configDir, key) {
|
|
|
483
519
|
value: { cmd: resolveArgv(cmd.value, configDir), timeoutMs: timeoutMs.value },
|
|
484
520
|
};
|
|
485
521
|
}
|
|
522
|
+
/**
|
|
523
|
+
* A command, and its own Gate sequence — `planner`, `builder.producer`,
|
|
524
|
+
* `builder.repair`, `assembly.fix`, `assembly.validate`. Deliberately its own
|
|
525
|
+
* ~10 lines rather than a parameterized `readPass`: each reader's error
|
|
526
|
+
* messages and key set stay independently legible.
|
|
527
|
+
*/
|
|
528
|
+
function readGatedPass(value, configDir, key, agents) {
|
|
529
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
530
|
+
return {
|
|
531
|
+
ok: false,
|
|
532
|
+
reason: `Config "${key}" must be a YAML mapping with "cmd" (or "agent") and "timeoutMs".`,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
const object = value;
|
|
536
|
+
const short = object.agent !== undefined;
|
|
537
|
+
for (const name of Object.keys(object)) {
|
|
538
|
+
const known = GATED_PASS_KEYS.has(name) ||
|
|
539
|
+
(short && (SHORT_PASS_KEYS.has(name) || (name === "read" && key === "planner")));
|
|
540
|
+
if (!known) {
|
|
541
|
+
return { ok: false, reason: `Config "${key}" has unknown key "${name}".` };
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
let cmd;
|
|
545
|
+
if (short) {
|
|
546
|
+
const expanded = expandAgentPass(object, key, agents, configDir, key);
|
|
547
|
+
if (expanded.ok === false) {
|
|
548
|
+
return expanded;
|
|
549
|
+
}
|
|
550
|
+
cmd = expanded.value;
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
const written = stringArrayField(object, "cmd");
|
|
554
|
+
if (written.ok === false || written.value === undefined) {
|
|
555
|
+
return {
|
|
556
|
+
ok: false,
|
|
557
|
+
reason: `Config "${key}.cmd" must be a non-empty array of strings, or "${key}.agent" an agent of "agents".`,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
cmd = resolveArgv(written.value, configDir);
|
|
561
|
+
}
|
|
562
|
+
const timeoutMs = positiveIntField(object, "timeoutMs");
|
|
563
|
+
if (timeoutMs.ok === false || timeoutMs.value === undefined) {
|
|
564
|
+
return {
|
|
565
|
+
ok: false,
|
|
566
|
+
reason: `Config "${key}" needs a positive "timeoutMs": nothing else bounds a command.`,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
let gates = [];
|
|
570
|
+
if (object.gates !== undefined) {
|
|
571
|
+
const read = readGates(object.gates, configDir, `${key}.gates`);
|
|
572
|
+
if (read.ok === false) {
|
|
573
|
+
return read;
|
|
574
|
+
}
|
|
575
|
+
gates = read.value;
|
|
576
|
+
}
|
|
577
|
+
return { ok: true, value: { cmd, timeoutMs: timeoutMs.value, gates } };
|
|
578
|
+
}
|
|
486
579
|
/**
|
|
487
580
|
* Making a Subtask: both passes named, neither borrowing the other's command.
|
|
488
581
|
*/
|
|
489
|
-
function readBuilder(value, configDir) {
|
|
582
|
+
function readBuilder(value, configDir, agents) {
|
|
490
583
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
491
584
|
return { ok: false, reason: 'Config "builder" must be a YAML mapping.' };
|
|
492
585
|
}
|
|
@@ -502,18 +595,17 @@ function readBuilder(value, configDir) {
|
|
|
502
595
|
if (object.repair === undefined) {
|
|
503
596
|
return { ok: false, reason: 'Config "builder" needs a "repair".' };
|
|
504
597
|
}
|
|
505
|
-
const readProducer =
|
|
598
|
+
const readProducer = readGatedPass(object.producer, configDir, "builder.producer", agents);
|
|
506
599
|
if (readProducer.ok === false) {
|
|
507
600
|
return readProducer;
|
|
508
601
|
}
|
|
509
|
-
const readRepair =
|
|
602
|
+
const readRepair = readGatedPass(object.repair, configDir, "builder.repair", agents);
|
|
510
603
|
if (readRepair.ok === false) {
|
|
511
604
|
return readRepair;
|
|
512
605
|
}
|
|
513
606
|
const spec = {
|
|
514
607
|
producer: readProducer.value,
|
|
515
608
|
repair: readRepair.value,
|
|
516
|
-
gates: [],
|
|
517
609
|
};
|
|
518
610
|
const maxAttempts = positiveIntField(object, "maxAttempts");
|
|
519
611
|
if (maxAttempts.ok === false) {
|
|
@@ -522,20 +614,13 @@ function readBuilder(value, configDir) {
|
|
|
522
614
|
if (maxAttempts.value !== undefined) {
|
|
523
615
|
spec.maxAttempts = maxAttempts.value;
|
|
524
616
|
}
|
|
525
|
-
if (object.gates !== undefined) {
|
|
526
|
-
const gates = readGates(object.gates, configDir, "builder.gates");
|
|
527
|
-
if (gates.ok === false) {
|
|
528
|
-
return gates;
|
|
529
|
-
}
|
|
530
|
-
spec.gates = gates.value;
|
|
531
|
-
}
|
|
532
617
|
return { ok: true, value: spec };
|
|
533
618
|
}
|
|
534
619
|
/**
|
|
535
620
|
* Judging the assembled feature. A command here is a fix of what a judgement
|
|
536
621
|
* refused, not a second first-pass of the request.
|
|
537
622
|
*/
|
|
538
|
-
function readAssembly(value, configDir) {
|
|
623
|
+
function readAssembly(value, configDir, agents) {
|
|
539
624
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
540
625
|
return { ok: false, reason: 'Config "assembly" must be a YAML mapping.' };
|
|
541
626
|
}
|
|
@@ -554,14 +639,14 @@ function readAssembly(value, configDir) {
|
|
|
554
639
|
}
|
|
555
640
|
const spec = { gates: [] };
|
|
556
641
|
if (object.fix !== undefined) {
|
|
557
|
-
const read =
|
|
642
|
+
const read = readGatedPass(object.fix, configDir, "assembly.fix", agents);
|
|
558
643
|
if (read.ok === false) {
|
|
559
644
|
return read;
|
|
560
645
|
}
|
|
561
646
|
spec.fix = read.value;
|
|
562
647
|
}
|
|
563
648
|
if (object.validate !== undefined) {
|
|
564
|
-
const read =
|
|
649
|
+
const read = readGatedPass(object.validate, configDir, "assembly.validate", agents);
|
|
565
650
|
if (read.ok === false) {
|
|
566
651
|
return read;
|
|
567
652
|
}
|
|
@@ -592,8 +677,20 @@ function readAssembly(value, configDir) {
|
|
|
592
677
|
* resolved against the slot's working directory, which is the Task workspace.
|
|
593
678
|
* A workspace is a git worktree that is published and then destroyed: output
|
|
594
679
|
* left there is committed into the work first, and lost after.
|
|
680
|
+
*
|
|
681
|
+
* `--transcript-dir` and `--agent-home` are Host-owned write paths (films, and
|
|
682
|
+
* the vendor's isolated HOME). Both must resolve against the config directory
|
|
683
|
+
* even before the directory exists. So must `--plugins-out`, where a deck
|
|
684
|
+
* profile is built. `--plugin-dir` is read, not written, but a relative one
|
|
685
|
+
* left as a word would be looked up in the Task workspace — never where the
|
|
686
|
+
* operator's plugin is.
|
|
595
687
|
*/
|
|
596
|
-
const WRITTEN_PATH_FLAGS = new Set([
|
|
688
|
+
const WRITTEN_PATH_FLAGS = new Set([
|
|
689
|
+
"--transcript-dir",
|
|
690
|
+
"--agent-home",
|
|
691
|
+
"--plugins-out",
|
|
692
|
+
"--plugin-dir",
|
|
693
|
+
]);
|
|
597
694
|
function resolveArgv(argv, configDir) {
|
|
598
695
|
return argv.map((token, at) => WRITTEN_PATH_FLAGS.has(argv[at - 1] ?? "")
|
|
599
696
|
? resolve(configDir, token)
|