@a5c-ai/babysitter-sdk 0.0.169 → 0.0.170-staging.00aac85c
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/cli/commands/configure.d.ts +124 -0
- package/dist/cli/commands/configure.d.ts.map +1 -0
- package/dist/cli/commands/configure.js +514 -0
- package/dist/cli/commands/health.d.ts +89 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +579 -0
- package/dist/cli/commands/hookLog.d.ts +15 -0
- package/dist/cli/commands/hookLog.d.ts.map +1 -0
- package/dist/cli/commands/hookLog.js +286 -0
- package/dist/cli/commands/hookRun.d.ts +20 -0
- package/dist/cli/commands/hookRun.d.ts.map +1 -0
- package/dist/cli/commands/hookRun.js +544 -0
- package/dist/cli/commands/runExecuteTasks.d.ts +42 -0
- package/dist/cli/commands/runExecuteTasks.d.ts.map +1 -0
- package/dist/cli/commands/runExecuteTasks.js +377 -0
- package/dist/cli/commands/runIterate.d.ts +5 -1
- package/dist/cli/commands/runIterate.d.ts.map +1 -1
- package/dist/cli/commands/runIterate.js +75 -6
- package/dist/cli/commands/session.d.ts +97 -0
- package/dist/cli/commands/session.d.ts.map +1 -0
- package/dist/cli/commands/session.js +922 -0
- package/dist/cli/commands/skill.d.ts +87 -0
- package/dist/cli/commands/skill.d.ts.map +1 -0
- package/dist/cli/commands/skill.js +869 -0
- package/dist/cli/completionProof.d.ts +4 -0
- package/dist/cli/completionProof.d.ts.map +1 -0
- package/dist/cli/{completionSecret.js → completionProof.js} +7 -7
- package/dist/cli/main.d.ts +14 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +649 -16
- package/dist/config/defaults.d.ts +165 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +281 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +35 -0
- package/dist/hooks/dispatcher.d.ts.map +1 -1
- package/dist/hooks/dispatcher.js +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/runtime/constants.d.ts +1 -1
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/createRun.d.ts.map +1 -1
- package/dist/runtime/createRun.js +7 -3
- package/dist/runtime/exceptions.d.ts +186 -3
- package/dist/runtime/exceptions.d.ts.map +1 -1
- package/dist/runtime/exceptions.js +416 -15
- package/dist/runtime/types.d.ts +1 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/session/index.d.ts +9 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +30 -0
- package/dist/session/parse.d.ts +45 -0
- package/dist/session/parse.d.ts.map +1 -0
- package/dist/session/parse.js +159 -0
- package/dist/session/types.d.ts +194 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +45 -0
- package/dist/session/write.d.ts +50 -0
- package/dist/session/write.d.ts.map +1 -0
- package/dist/session/write.js +196 -0
- package/dist/storage/createRunDir.d.ts.map +1 -1
- package/dist/storage/createRunDir.js +1 -0
- package/dist/storage/paths.d.ts +5 -1
- package/dist/storage/paths.d.ts.map +1 -1
- package/dist/storage/paths.js +6 -1
- package/dist/storage/types.d.ts +3 -1
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/tasks/kinds/index.d.ts.map +1 -1
- package/dist/tasks/kinds/index.js +6 -1
- package/dist/testing/runHarness.d.ts.map +1 -1
- package/dist/testing/runHarness.js +5 -1
- package/package.json +1 -2
- package/dist/cli/completionSecret.d.ts +0 -4
- package/dist/cli/completionSecret.d.ts.map +0 -1
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* run:execute-tasks command - Execute pending node tasks in a run
|
|
4
|
+
*
|
|
5
|
+
* This command replaces the shell-based task execution logic in native-orchestrator.sh.
|
|
6
|
+
* It:
|
|
7
|
+
* 1. Loads the journal and builds the effect index
|
|
8
|
+
* 2. Filters pending effects by kind (default "node")
|
|
9
|
+
* 3. Slices to maxTasks (default 3)
|
|
10
|
+
* 4. For each task: reads task.json, spawns `node`, captures stdout/stderr, commits result
|
|
11
|
+
* 5. Returns a summary of executed tasks
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.runExecuteTasks = runExecuteTasks;
|
|
48
|
+
const node_child_process_1 = require("node:child_process");
|
|
49
|
+
const node_fs_1 = require("node:fs");
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
const effectIndex_1 = require("../../runtime/replay/effectIndex");
|
|
52
|
+
const commitEffectResult_1 = require("../../runtime/commitEffectResult");
|
|
53
|
+
const tasks_1 = require("../../storage/tasks");
|
|
54
|
+
const defaults_1 = require("../../config/defaults");
|
|
55
|
+
function log(verbose, ...args) {
|
|
56
|
+
if (verbose) {
|
|
57
|
+
console.error(`[run:execute-tasks]`, ...args);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a path relative to runDir unless it is already absolute.
|
|
62
|
+
*/
|
|
63
|
+
function resolveRelative(runDir, ref) {
|
|
64
|
+
if (path.isAbsolute(ref) || /^[A-Za-z]:[\\/]/.test(ref)) {
|
|
65
|
+
return ref;
|
|
66
|
+
}
|
|
67
|
+
return path.join(runDir, ref);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Make a path relative to runDir, using forward slashes.
|
|
71
|
+
*/
|
|
72
|
+
function toRunRelative(runDir, absolute) {
|
|
73
|
+
return path.relative(runDir, absolute).replace(/\\/g, "/");
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Spawn a node process for the given task and capture its output.
|
|
77
|
+
*/
|
|
78
|
+
function spawnNodeTask(options) {
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
const child = (0, node_child_process_1.spawn)("node", [options.entry, ...options.args], {
|
|
81
|
+
cwd: options.cwd,
|
|
82
|
+
env: { ...process.env, ...options.env },
|
|
83
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
84
|
+
timeout: options.timeout,
|
|
85
|
+
});
|
|
86
|
+
const stdoutChunks = [];
|
|
87
|
+
const stderrChunks = [];
|
|
88
|
+
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
89
|
+
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
90
|
+
child.on("close", (code) => {
|
|
91
|
+
resolve({
|
|
92
|
+
exitCode: code ?? 1,
|
|
93
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
94
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
child.on("error", (err) => {
|
|
98
|
+
resolve({
|
|
99
|
+
exitCode: 1,
|
|
100
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
101
|
+
stderr: `${Buffer.concat(stderrChunks).toString("utf8")}\n${err.message}`,
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Execute a single task: read its definition, spawn node, commit the result.
|
|
108
|
+
*/
|
|
109
|
+
async function executeOneTask(runDir, record, timeout, verbose) {
|
|
110
|
+
const effectId = record.effectId;
|
|
111
|
+
const label = record.label ?? null;
|
|
112
|
+
log(verbose, `Executing: ${effectId}${label ? ` (${label})` : ""}`);
|
|
113
|
+
// Read task.json
|
|
114
|
+
const taskDef = await (0, tasks_1.readTaskDefinition)(runDir, effectId);
|
|
115
|
+
if (!taskDef) {
|
|
116
|
+
log(verbose, `Missing task definition for ${effectId}`);
|
|
117
|
+
const errorPayload = {
|
|
118
|
+
name: "Error",
|
|
119
|
+
message: `Missing task definition for effect ${effectId}`,
|
|
120
|
+
};
|
|
121
|
+
await (0, commitEffectResult_1.commitEffectResult)({
|
|
122
|
+
runDir,
|
|
123
|
+
effectId,
|
|
124
|
+
invocationKey: record.invocationKey,
|
|
125
|
+
result: {
|
|
126
|
+
status: "error",
|
|
127
|
+
error: errorPayload,
|
|
128
|
+
startedAt: new Date().toISOString(),
|
|
129
|
+
finishedAt: new Date().toISOString(),
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
effectId,
|
|
134
|
+
label,
|
|
135
|
+
status: "error",
|
|
136
|
+
exitCode: 1,
|
|
137
|
+
durationMs: 0,
|
|
138
|
+
stdoutRef: null,
|
|
139
|
+
stderrRef: null,
|
|
140
|
+
resultRef: null,
|
|
141
|
+
error: errorPayload,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Extract node config
|
|
145
|
+
const nodeConfig = (taskDef.node ?? {});
|
|
146
|
+
const entry = nodeConfig.entry;
|
|
147
|
+
if (!entry) {
|
|
148
|
+
log(verbose, `Missing node.entry in task.json for ${effectId}`);
|
|
149
|
+
const errorPayload = {
|
|
150
|
+
name: "Error",
|
|
151
|
+
message: `Missing node.entry in task definition for effect ${effectId}`,
|
|
152
|
+
};
|
|
153
|
+
await (0, commitEffectResult_1.commitEffectResult)({
|
|
154
|
+
runDir,
|
|
155
|
+
effectId,
|
|
156
|
+
invocationKey: record.invocationKey,
|
|
157
|
+
result: {
|
|
158
|
+
status: "error",
|
|
159
|
+
error: errorPayload,
|
|
160
|
+
startedAt: new Date().toISOString(),
|
|
161
|
+
finishedAt: new Date().toISOString(),
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
effectId,
|
|
166
|
+
label,
|
|
167
|
+
status: "error",
|
|
168
|
+
exitCode: 1,
|
|
169
|
+
durationMs: 0,
|
|
170
|
+
stdoutRef: null,
|
|
171
|
+
stderrRef: null,
|
|
172
|
+
resultRef: null,
|
|
173
|
+
error: errorPayload,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const nodeArgs = nodeConfig.args ?? [];
|
|
177
|
+
const taskTimeout = nodeConfig.timeoutMs ?? timeout;
|
|
178
|
+
// Extract IO config
|
|
179
|
+
const ioConfig = (taskDef.io ?? {});
|
|
180
|
+
const inputRef = ioConfig.inputJsonPath ?? `tasks/${effectId}/inputs.json`;
|
|
181
|
+
const outputRef = ioConfig.outputJsonPath ?? `tasks/${effectId}/result.json`;
|
|
182
|
+
const stdoutRef = ioConfig.stdoutPath ?? `tasks/${effectId}/stdout.log`;
|
|
183
|
+
const stderrRef = ioConfig.stderrPath ?? `tasks/${effectId}/stderr.log`;
|
|
184
|
+
// Resolve paths relative to runDir
|
|
185
|
+
const inputAbs = resolveRelative(runDir, inputRef);
|
|
186
|
+
const outputAbs = resolveRelative(runDir, outputRef);
|
|
187
|
+
const stdoutAbs = resolveRelative(runDir, stdoutRef);
|
|
188
|
+
const stderrAbs = resolveRelative(runDir, stderrRef);
|
|
189
|
+
const entryAbs = resolveRelative(runDir, entry);
|
|
190
|
+
const cwdAbs = nodeConfig.cwd ? resolveRelative(runDir, nodeConfig.cwd) : runDir;
|
|
191
|
+
// Ensure directories exist
|
|
192
|
+
await Promise.all([
|
|
193
|
+
node_fs_1.promises.mkdir(path.dirname(inputAbs), { recursive: true }),
|
|
194
|
+
node_fs_1.promises.mkdir(path.dirname(outputAbs), { recursive: true }),
|
|
195
|
+
node_fs_1.promises.mkdir(path.dirname(stdoutAbs), { recursive: true }),
|
|
196
|
+
node_fs_1.promises.mkdir(path.dirname(stderrAbs), { recursive: true }),
|
|
197
|
+
]);
|
|
198
|
+
// Stage inputs.json
|
|
199
|
+
const inputsRefField = taskDef.inputsRef;
|
|
200
|
+
if (inputsRefField) {
|
|
201
|
+
const sourceAbs = resolveRelative(runDir, inputsRefField);
|
|
202
|
+
try {
|
|
203
|
+
await node_fs_1.promises.copyFile(sourceAbs, inputAbs);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// If the source doesn't exist, write an empty object
|
|
207
|
+
await node_fs_1.promises.writeFile(inputAbs, "{}\n", "utf8");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// Extract inline inputs or default to {}
|
|
212
|
+
const inputs = taskDef.inputs ?? {};
|
|
213
|
+
await node_fs_1.promises.writeFile(inputAbs, JSON.stringify(inputs) + "\n", "utf8");
|
|
214
|
+
}
|
|
215
|
+
// Build environment for the child process
|
|
216
|
+
const taskEnv = {
|
|
217
|
+
...(nodeConfig.env ?? {}),
|
|
218
|
+
BABYSITTER_INPUT_JSON: inputAbs,
|
|
219
|
+
BABYSITTER_OUTPUT_JSON: outputAbs,
|
|
220
|
+
BABYSITTER_STDOUT_PATH: stdoutAbs,
|
|
221
|
+
BABYSITTER_STDERR_PATH: stderrAbs,
|
|
222
|
+
BABYSITTER_EFFECT_ID: effectId,
|
|
223
|
+
};
|
|
224
|
+
// Spawn the node process
|
|
225
|
+
const startedAt = new Date().toISOString();
|
|
226
|
+
const startMs = Date.now();
|
|
227
|
+
const spawnResult = await spawnNodeTask({
|
|
228
|
+
entry: entryAbs,
|
|
229
|
+
args: nodeArgs,
|
|
230
|
+
cwd: cwdAbs,
|
|
231
|
+
env: taskEnv,
|
|
232
|
+
stdoutPath: stdoutAbs,
|
|
233
|
+
stderrPath: stderrAbs,
|
|
234
|
+
timeout: taskTimeout,
|
|
235
|
+
});
|
|
236
|
+
const finishedAt = new Date().toISOString();
|
|
237
|
+
const durationMs = Date.now() - startMs;
|
|
238
|
+
// Write stdout and stderr to disk
|
|
239
|
+
await node_fs_1.promises.writeFile(stdoutAbs, spawnResult.stdout, "utf8");
|
|
240
|
+
await node_fs_1.promises.writeFile(stderrAbs, spawnResult.stderr, "utf8");
|
|
241
|
+
const stdoutRelRef = toRunRelative(runDir, stdoutAbs);
|
|
242
|
+
const stderrRelRef = toRunRelative(runDir, stderrAbs);
|
|
243
|
+
// Commit the result
|
|
244
|
+
if (spawnResult.exitCode === 0) {
|
|
245
|
+
// Read the output file produced by the task (if any)
|
|
246
|
+
let value = undefined;
|
|
247
|
+
try {
|
|
248
|
+
const outputContents = await node_fs_1.promises.readFile(outputAbs, "utf8");
|
|
249
|
+
const trimmed = outputContents.trim();
|
|
250
|
+
if (trimmed.length > 0) {
|
|
251
|
+
value = JSON.parse(trimmed);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// Output file may not exist; that is acceptable
|
|
256
|
+
}
|
|
257
|
+
const committed = await (0, commitEffectResult_1.commitEffectResult)({
|
|
258
|
+
runDir,
|
|
259
|
+
effectId,
|
|
260
|
+
invocationKey: record.invocationKey,
|
|
261
|
+
result: {
|
|
262
|
+
status: "ok",
|
|
263
|
+
value,
|
|
264
|
+
stdoutRef: stdoutRelRef,
|
|
265
|
+
stderrRef: stderrRelRef,
|
|
266
|
+
startedAt,
|
|
267
|
+
finishedAt,
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
log(verbose, `Posted result: ${effectId} (ok, ${durationMs}ms)`);
|
|
271
|
+
return {
|
|
272
|
+
effectId,
|
|
273
|
+
label,
|
|
274
|
+
status: "ok",
|
|
275
|
+
exitCode: spawnResult.exitCode,
|
|
276
|
+
durationMs,
|
|
277
|
+
stdoutRef: stdoutRelRef,
|
|
278
|
+
stderrRef: stderrRelRef,
|
|
279
|
+
resultRef: committed.resultRef ?? null,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
const errorPayload = {
|
|
284
|
+
name: "Error",
|
|
285
|
+
message: `Node task exited non-zero (exitCode=${spawnResult.exitCode})`,
|
|
286
|
+
};
|
|
287
|
+
const committed = await (0, commitEffectResult_1.commitEffectResult)({
|
|
288
|
+
runDir,
|
|
289
|
+
effectId,
|
|
290
|
+
invocationKey: record.invocationKey,
|
|
291
|
+
result: {
|
|
292
|
+
status: "error",
|
|
293
|
+
error: errorPayload,
|
|
294
|
+
stdoutRef: stdoutRelRef,
|
|
295
|
+
stderrRef: stderrRelRef,
|
|
296
|
+
startedAt,
|
|
297
|
+
finishedAt,
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
log(verbose, `Posted error: ${effectId} (exitCode=${spawnResult.exitCode}, ${durationMs}ms)`);
|
|
301
|
+
return {
|
|
302
|
+
effectId,
|
|
303
|
+
label,
|
|
304
|
+
status: "error",
|
|
305
|
+
exitCode: spawnResult.exitCode,
|
|
306
|
+
durationMs,
|
|
307
|
+
stdoutRef: stdoutRelRef,
|
|
308
|
+
stderrRef: stderrRelRef,
|
|
309
|
+
resultRef: committed.resultRef ?? null,
|
|
310
|
+
error: errorPayload,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async function runExecuteTasks(options) {
|
|
315
|
+
const { runDir, verbose, dryRun, } = options;
|
|
316
|
+
const maxTasks = options.maxTasks ?? 3;
|
|
317
|
+
const kind = options.kind ?? "node";
|
|
318
|
+
const config = (0, defaults_1.getConfig)();
|
|
319
|
+
const timeout = options.timeout ?? config.nodeTaskTimeout;
|
|
320
|
+
log(verbose, `Scanning for pending ${kind} tasks in ${runDir} (maxTasks=${maxTasks})`);
|
|
321
|
+
// Build the effect index from journal
|
|
322
|
+
const effectIndex = await (0, effectIndex_1.buildEffectIndex)({ runDir });
|
|
323
|
+
const pendingEffects = effectIndex.listPendingEffects();
|
|
324
|
+
if (pendingEffects.length === 0) {
|
|
325
|
+
log(verbose, "No pending effects found");
|
|
326
|
+
return {
|
|
327
|
+
action: "none",
|
|
328
|
+
count: 0,
|
|
329
|
+
reason: "no-pending-effects",
|
|
330
|
+
tasks: [],
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// Filter by kind
|
|
334
|
+
const matchingEffects = pendingEffects.filter((record) => record.kind?.toLowerCase() === kind.toLowerCase());
|
|
335
|
+
if (matchingEffects.length === 0) {
|
|
336
|
+
log(verbose, `No pending effects matching kind="${kind}"`);
|
|
337
|
+
return {
|
|
338
|
+
action: "none",
|
|
339
|
+
count: 0,
|
|
340
|
+
reason: "no-matching-tasks",
|
|
341
|
+
tasks: [],
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
// Slice to maxTasks
|
|
345
|
+
const tasksToExecute = matchingEffects.slice(0, maxTasks);
|
|
346
|
+
log(verbose, `Found ${matchingEffects.length} matching tasks, executing ${tasksToExecute.length}`);
|
|
347
|
+
if (dryRun) {
|
|
348
|
+
const dryRunTasks = tasksToExecute.map((record) => ({
|
|
349
|
+
effectId: record.effectId,
|
|
350
|
+
label: record.label ?? null,
|
|
351
|
+
status: "ok",
|
|
352
|
+
exitCode: 0,
|
|
353
|
+
durationMs: 0,
|
|
354
|
+
stdoutRef: null,
|
|
355
|
+
stderrRef: null,
|
|
356
|
+
resultRef: null,
|
|
357
|
+
}));
|
|
358
|
+
return {
|
|
359
|
+
action: "none",
|
|
360
|
+
count: tasksToExecute.length,
|
|
361
|
+
reason: "dry-run",
|
|
362
|
+
tasks: dryRunTasks,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// Execute tasks sequentially
|
|
366
|
+
const taskSummaries = [];
|
|
367
|
+
for (const record of tasksToExecute) {
|
|
368
|
+
const summary = await executeOneTask(runDir, record, timeout, verbose);
|
|
369
|
+
taskSummaries.push(summary);
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
action: "executed-tasks",
|
|
373
|
+
count: taskSummaries.length,
|
|
374
|
+
reason: "auto-runnable-tasks",
|
|
375
|
+
tasks: taskSummaries,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
@@ -15,20 +15,24 @@ export interface RunIterateOptions {
|
|
|
15
15
|
iteration?: number;
|
|
16
16
|
verbose?: boolean;
|
|
17
17
|
json?: boolean;
|
|
18
|
+
pluginRoot?: string;
|
|
18
19
|
}
|
|
19
20
|
export interface RunIterateResult {
|
|
20
21
|
iteration: number;
|
|
22
|
+
iterationCount: number;
|
|
21
23
|
status: "executed" | "waiting" | "completed" | "failed" | "none";
|
|
22
24
|
action?: string;
|
|
23
25
|
reason?: string;
|
|
24
26
|
count?: number;
|
|
25
27
|
until?: number;
|
|
26
28
|
nextActions?: EffectAction[];
|
|
27
|
-
|
|
29
|
+
completionProof?: string;
|
|
28
30
|
metadata?: {
|
|
29
31
|
runId: string;
|
|
30
32
|
processId: string;
|
|
31
33
|
hookStatus?: string;
|
|
34
|
+
discoveredSkills?: string[];
|
|
35
|
+
discoveredAgents?: string[];
|
|
32
36
|
};
|
|
33
37
|
}
|
|
34
38
|
export declare function runIterate(options: RunIterateOptions): Promise<RunIterateResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runIterate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/runIterate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"runIterate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/runIterate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKxD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,YAAY,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE;QACT,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC7B,CAAC;CACH;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+JtF"}
|
|
@@ -47,17 +47,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
47
47
|
exports.runIterate = runIterate;
|
|
48
48
|
const path = __importStar(require("path"));
|
|
49
49
|
const runFiles_1 = require("../../storage/runFiles");
|
|
50
|
+
const journal_1 = require("../../storage/journal");
|
|
51
|
+
const stateCache_1 = require("../../runtime/replay/stateCache");
|
|
50
52
|
const runtime_1 = require("../../runtime/hooks/runtime");
|
|
51
53
|
const orchestrateIteration_1 = require("../../runtime/orchestrateIteration");
|
|
52
|
-
const
|
|
54
|
+
const completionProof_1 = require("../completionProof");
|
|
55
|
+
const skill_1 = require("./skill");
|
|
53
56
|
async function runIterate(options) {
|
|
54
57
|
const { runDir, verbose } = options;
|
|
55
58
|
// Read run metadata
|
|
56
59
|
const metadata = await (0, runFiles_1.readRunMetadata)(runDir);
|
|
57
60
|
const runId = metadata.runId;
|
|
58
|
-
// Determine iteration number
|
|
59
|
-
|
|
60
|
-
const iteration = options.iteration ?? 1;
|
|
61
|
+
// Determine iteration number from state cache or journal
|
|
62
|
+
const iterationCount = await detectIterationCount(runDir);
|
|
63
|
+
const iteration = options.iteration ?? (iterationCount + 1);
|
|
61
64
|
const projectRoot = path.dirname(path.dirname(path.dirname(runDir)));
|
|
62
65
|
if (verbose) {
|
|
63
66
|
console.error(`[run:iterate] Starting iteration ${iteration} for run ${runId}`);
|
|
@@ -66,7 +69,7 @@ async function runIterate(options) {
|
|
|
66
69
|
// This is what creates EFFECT_REQUESTED entries that hooks can observe via task:list.
|
|
67
70
|
const iterationResult = await (0, orchestrateIteration_1.orchestrateIteration)({ runDir });
|
|
68
71
|
if (iterationResult.status === "completed") {
|
|
69
|
-
const
|
|
72
|
+
const completionProof = (0, completionProof_1.resolveCompletionProof)(metadata);
|
|
70
73
|
await (0, runtime_1.callRuntimeHook)("on-iteration-end", {
|
|
71
74
|
runId,
|
|
72
75
|
iteration,
|
|
@@ -77,10 +80,11 @@ async function runIterate(options) {
|
|
|
77
80
|
}, { cwd: projectRoot, logger: verbose ? ((msg) => console.error(msg)) : undefined });
|
|
78
81
|
return {
|
|
79
82
|
iteration,
|
|
83
|
+
iterationCount,
|
|
80
84
|
status: "completed",
|
|
81
85
|
action: "none",
|
|
82
86
|
reason: "completed",
|
|
83
|
-
|
|
87
|
+
completionProof,
|
|
84
88
|
metadata: { runId, processId: metadata.processId, hookStatus: "executed" },
|
|
85
89
|
};
|
|
86
90
|
}
|
|
@@ -95,6 +99,7 @@ async function runIterate(options) {
|
|
|
95
99
|
}, { cwd: projectRoot, logger: verbose ? ((msg) => console.error(msg)) : undefined });
|
|
96
100
|
return {
|
|
97
101
|
iteration,
|
|
102
|
+
iterationCount,
|
|
98
103
|
status: "failed",
|
|
99
104
|
action: "none",
|
|
100
105
|
reason: "failed",
|
|
@@ -155,6 +160,7 @@ async function runIterate(options) {
|
|
|
155
160
|
// Return result
|
|
156
161
|
const result = {
|
|
157
162
|
iteration,
|
|
163
|
+
iterationCount,
|
|
158
164
|
status,
|
|
159
165
|
action,
|
|
160
166
|
reason,
|
|
@@ -167,8 +173,71 @@ async function runIterate(options) {
|
|
|
167
173
|
hookStatus: hookResult.executedHooks?.length > 0 ? "executed" : "none",
|
|
168
174
|
},
|
|
169
175
|
};
|
|
176
|
+
// Discover available skills and agents
|
|
177
|
+
const pluginRoot = options.pluginRoot;
|
|
178
|
+
if (pluginRoot && result.metadata) {
|
|
179
|
+
try {
|
|
180
|
+
const discoverResult = await (0, skill_1.discoverSkillsInternal)({ pluginRoot, runId });
|
|
181
|
+
result.metadata.discoveredSkills = discoverResult.skills.map(s => s.name);
|
|
182
|
+
result.metadata.discoveredAgents = discoverResult.agents.map(a => a.name);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// Non-fatal
|
|
186
|
+
}
|
|
187
|
+
}
|
|
170
188
|
return result;
|
|
171
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Detect the current iteration count from state cache or journal.
|
|
192
|
+
*
|
|
193
|
+
* The iteration count represents how many iterations have been completed so far.
|
|
194
|
+
* This is used to determine the next iteration number when --iteration is not specified.
|
|
195
|
+
*
|
|
196
|
+
* Strategy:
|
|
197
|
+
* 1. Read from state.json (stateCache snapshot) if available - uses stateVersion
|
|
198
|
+
* which tracks the journal sequence number
|
|
199
|
+
* 2. Fall back to counting RUN_ITERATION events in the journal
|
|
200
|
+
* 3. Return 0 if neither is available (fresh run)
|
|
201
|
+
*/
|
|
202
|
+
async function detectIterationCount(runDir) {
|
|
203
|
+
// Strategy 1: Read from state cache
|
|
204
|
+
try {
|
|
205
|
+
const stateCache = await (0, stateCache_1.readStateCache)(runDir);
|
|
206
|
+
if (stateCache && typeof stateCache.stateVersion === "number" && stateCache.stateVersion > 0) {
|
|
207
|
+
// stateVersion tracks journal sequence, which correlates to iteration progress
|
|
208
|
+
// We derive iteration count from the number of completed iteration cycles
|
|
209
|
+
// Each iteration typically produces multiple journal events, so we use
|
|
210
|
+
// RUN_ITERATION event count as the more accurate measure
|
|
211
|
+
const iterationCountFromJournal = await countIterationsFromJournal(runDir);
|
|
212
|
+
if (iterationCountFromJournal > 0) {
|
|
213
|
+
return iterationCountFromJournal;
|
|
214
|
+
}
|
|
215
|
+
// If no RUN_ITERATION events but we have state, estimate from stateVersion
|
|
216
|
+
// This provides backward compatibility for runs that don't log RUN_ITERATION
|
|
217
|
+
return Math.max(0, Math.floor(stateCache.stateVersion / 2));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// State cache not available, fall through to journal
|
|
222
|
+
}
|
|
223
|
+
// Strategy 2: Count RUN_ITERATION events in journal
|
|
224
|
+
try {
|
|
225
|
+
return await countIterationsFromJournal(runDir);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Journal not available
|
|
229
|
+
}
|
|
230
|
+
// Strategy 3: Default to 0 for fresh runs
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Count the number of RUN_ITERATION events in the journal.
|
|
235
|
+
* Each RUN_ITERATION event represents one completed iteration.
|
|
236
|
+
*/
|
|
237
|
+
async function countIterationsFromJournal(runDir) {
|
|
238
|
+
const events = await (0, journal_1.loadJournal)(runDir);
|
|
239
|
+
return events.filter((event) => event.type === "RUN_ITERATION").length;
|
|
240
|
+
}
|
|
172
241
|
function parseHookDecision(output) {
|
|
173
242
|
const record = parseMaybeJsonRecord(output);
|
|
174
243
|
if (!record)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management CLI commands.
|
|
3
|
+
* Replaces bash logic from babysitter plugin shell scripts.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Parsed arguments for session commands.
|
|
7
|
+
*/
|
|
8
|
+
export interface SessionCommandArgs {
|
|
9
|
+
sessionId?: string;
|
|
10
|
+
stateDir?: string;
|
|
11
|
+
maxIterations?: number;
|
|
12
|
+
runId?: string;
|
|
13
|
+
prompt?: string;
|
|
14
|
+
iteration?: number;
|
|
15
|
+
lastIterationAt?: string;
|
|
16
|
+
iterationTimes?: string;
|
|
17
|
+
delete?: boolean;
|
|
18
|
+
json: boolean;
|
|
19
|
+
runsDir?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Handle session:init command.
|
|
23
|
+
* Initializes a new session state file.
|
|
24
|
+
*/
|
|
25
|
+
export declare function handleSessionInit(args: SessionCommandArgs): Promise<number>;
|
|
26
|
+
/**
|
|
27
|
+
* Handle session:associate command.
|
|
28
|
+
* Associates a session with a run ID.
|
|
29
|
+
*/
|
|
30
|
+
export declare function handleSessionAssociate(args: SessionCommandArgs): Promise<number>;
|
|
31
|
+
/**
|
|
32
|
+
* Handle session:resume command.
|
|
33
|
+
* Resumes an existing run in a new session.
|
|
34
|
+
*/
|
|
35
|
+
export declare function handleSessionResume(args: SessionCommandArgs): Promise<number>;
|
|
36
|
+
/**
|
|
37
|
+
* Handle session:state command.
|
|
38
|
+
* Reads and returns session state.
|
|
39
|
+
*/
|
|
40
|
+
export declare function handleSessionState(args: SessionCommandArgs): Promise<number>;
|
|
41
|
+
/**
|
|
42
|
+
* Handle session:update command.
|
|
43
|
+
* Updates session state fields.
|
|
44
|
+
*/
|
|
45
|
+
export declare function handleSessionUpdate(args: SessionCommandArgs): Promise<number>;
|
|
46
|
+
/**
|
|
47
|
+
* Handle session:check-iteration command.
|
|
48
|
+
* Checks if iteration should continue based on timing and limits.
|
|
49
|
+
*/
|
|
50
|
+
export declare function handleSessionCheckIteration(args: SessionCommandArgs): Promise<number>;
|
|
51
|
+
export interface SessionLastMessageArgs {
|
|
52
|
+
transcriptPath: string;
|
|
53
|
+
json: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface SessionLastMessageResult {
|
|
56
|
+
found: boolean;
|
|
57
|
+
text: string | null;
|
|
58
|
+
hasPromise: boolean;
|
|
59
|
+
promiseValue: string | null;
|
|
60
|
+
error?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse a JSONL transcript file and extract the last assistant text message.
|
|
64
|
+
* Also detects <promise>...</promise> tags in the text.
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseTranscriptLastAssistantMessage(content: string): {
|
|
67
|
+
found: boolean;
|
|
68
|
+
text: string | null;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Extract content from first <promise>...</promise> tag.
|
|
72
|
+
* Trims whitespace and collapses internal whitespace to single spaces.
|
|
73
|
+
*/
|
|
74
|
+
export declare function extractPromiseTag(text: string): string | null;
|
|
75
|
+
export declare function handleSessionLastMessage(args: SessionLastMessageArgs): number;
|
|
76
|
+
export interface SessionIterationMessageArgs {
|
|
77
|
+
runId?: string;
|
|
78
|
+
iteration?: number;
|
|
79
|
+
runsDir: string;
|
|
80
|
+
pluginRoot?: string;
|
|
81
|
+
json: boolean;
|
|
82
|
+
}
|
|
83
|
+
export interface SessionIterationMessageResult {
|
|
84
|
+
systemMessage: string;
|
|
85
|
+
runState: string | null;
|
|
86
|
+
completionProof: string | null;
|
|
87
|
+
pendingKinds: string | null;
|
|
88
|
+
skillContext: string | null;
|
|
89
|
+
iteration: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Handle session:iteration-message command.
|
|
93
|
+
* Generates the formatted system message for the next babysitter iteration.
|
|
94
|
+
* Replaces ~22 lines of bash branching + skill-context-resolver call in babysitter-stop-hook.sh.
|
|
95
|
+
*/
|
|
96
|
+
export declare function handleSessionIterationMessage(args: SessionIterationMessageArgs): Promise<number>;
|
|
97
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuBH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0GjF;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoGtF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0InF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmElF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwGnF;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyG3F;AAID,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,MAAM,GAAG;IACpE,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAoDA;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI7D;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,sBAAsB,GAC3B,MAAM,CA+CR;AAID,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,6BAA6B;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAcD;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,MAAM,CAAC,CA4GjB"}
|