@united-workforce/cli 0.5.0 → 0.6.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/.build-fingerprint +1 -1
- package/dist/__tests__/config-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/config-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/config-text-renderer.test.js +137 -0
- package/dist/__tests__/config-text-renderer.test.js.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js +1 -1
- package/dist/__tests__/thread-agent-failure-suspended.test.d.ts +2 -0
- package/dist/__tests__/thread-agent-failure-suspended.test.d.ts.map +1 -0
- package/dist/__tests__/thread-agent-failure-suspended.test.js +332 -0
- package/dist/__tests__/thread-agent-failure-suspended.test.js.map +1 -0
- package/dist/__tests__/thread-join.test.d.ts +2 -0
- package/dist/__tests__/thread-join.test.d.ts.map +1 -0
- package/dist/__tests__/thread-join.test.js +77 -0
- package/dist/__tests__/thread-join.test.js.map +1 -0
- package/dist/__tests__/thread-poke.test.js +4 -1
- package/dist/__tests__/thread-poke.test.js.map +1 -1
- package/dist/__tests__/workflow-paths.test.d.ts +2 -0
- package/dist/__tests__/workflow-paths.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-paths.test.js +261 -0
- package/dist/__tests__/workflow-paths.test.js.map +1 -0
- package/dist/cli.js +18 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +69 -3
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/thread.d.ts +12 -0
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +183 -8
- package/dist/commands/thread.js.map +1 -1
- package/dist/commands/workflow.d.ts +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +24 -4
- package/dist/commands/workflow.js.map +1 -1
- package/dist/output-mappers.d.ts.map +1 -1
- package/dist/output-mappers.js +1 -1
- package/dist/output-mappers.js.map +1 -1
- package/dist/store.d.ts +11 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +20 -1
- package/dist/store.js.map +1 -1
- package/package.json +11 -11
- package/src/__tests__/config-text-renderer.test.ts +156 -0
- package/src/__tests__/issue-180-workflow-ref-removed.test.ts +1 -1
- package/src/__tests__/thread-agent-failure-suspended.test.ts +406 -0
- package/src/__tests__/thread-join.test.ts +103 -0
- package/src/__tests__/thread-poke.test.ts +4 -1
- package/src/__tests__/workflow-paths.test.ts +337 -0
- package/src/cli.ts +19 -0
- package/src/commands/config.ts +74 -3
- package/src/commands/thread.ts +233 -8
- package/src/commands/workflow.ts +29 -4
- package/src/output-mappers.ts +2 -1
- package/src/store.ts +25 -1
- package/LICENSE +0 -21
package/dist/commands/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
3
4
|
import { parse, stringify } from "yaml";
|
|
4
5
|
/**
|
|
5
6
|
* Valid configuration key schema. Engine config is LLM-free — providers,
|
|
@@ -22,6 +23,7 @@ const VALID_CONFIG_KEYS = {
|
|
|
22
23
|
knownFields: ["maxRunning"],
|
|
23
24
|
minDepth: 2,
|
|
24
25
|
},
|
|
26
|
+
workflowPaths: { nested: false },
|
|
25
27
|
};
|
|
26
28
|
/**
|
|
27
29
|
* Validate a config key path against the known schema
|
|
@@ -188,6 +190,29 @@ function parseArgsValue(value) {
|
|
|
188
190
|
}
|
|
189
191
|
throw new Error("Value for 'args' key must be a JSON array starting with '['");
|
|
190
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Parse value for a top-level string array key (must be JSON array of strings).
|
|
195
|
+
*/
|
|
196
|
+
function parseStringArrayValue(value, keyName) {
|
|
197
|
+
if (value.startsWith("[")) {
|
|
198
|
+
try {
|
|
199
|
+
const parsed = JSON.parse(value);
|
|
200
|
+
if (!Array.isArray(parsed)) {
|
|
201
|
+
throw new Error("Value must be an array");
|
|
202
|
+
}
|
|
203
|
+
for (const item of parsed) {
|
|
204
|
+
if (typeof item !== "string") {
|
|
205
|
+
throw new Error(`All items must be strings, got ${typeof item}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return parsed;
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
throw new Error(`Invalid JSON array for ${keyName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
throw new Error(`Value for '${keyName}' must be a JSON array starting with '['`);
|
|
215
|
+
}
|
|
191
216
|
/**
|
|
192
217
|
* Validate that we're not setting a property on a non-object
|
|
193
218
|
*/
|
|
@@ -217,9 +242,12 @@ export async function cmdConfigSet(storageRoot, key, value) {
|
|
|
217
242
|
// Validate the key path
|
|
218
243
|
validateConfigKey(path);
|
|
219
244
|
const lastSegment = path[path.length - 1];
|
|
220
|
-
// Parse value if it's for an array key (args)
|
|
245
|
+
// Parse value if it's for an array key (args, workflowPaths)
|
|
221
246
|
let parsedValue = value;
|
|
222
|
-
if (
|
|
247
|
+
if (path[0] === "workflowPaths") {
|
|
248
|
+
parsedValue = parseStringArrayValue(value, "workflowPaths");
|
|
249
|
+
}
|
|
250
|
+
else if (lastSegment === "args") {
|
|
223
251
|
parsedValue = parseArgsValue(value);
|
|
224
252
|
}
|
|
225
253
|
else if (lastSegment === "maxRunning") {
|
|
@@ -235,4 +263,42 @@ export async function cmdConfigSet(storageRoot, key, value) {
|
|
|
235
263
|
saveConfig(configPath, config);
|
|
236
264
|
return { key, value: parsedValue };
|
|
237
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Expand leading `~/` in a path to the user's home directory.
|
|
268
|
+
*/
|
|
269
|
+
function expandTilde(p) {
|
|
270
|
+
if (p.startsWith("~/") || p === "~") {
|
|
271
|
+
return join(homedir(), p.slice(1));
|
|
272
|
+
}
|
|
273
|
+
return p;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Load workflowPaths from config and resolve to absolute paths.
|
|
277
|
+
* Returns empty array if config doesn't exist or key is missing.
|
|
278
|
+
*/
|
|
279
|
+
export function loadWorkflowPaths(storageRoot) {
|
|
280
|
+
const configPath = getConfigPath(storageRoot);
|
|
281
|
+
if (!existsSync(configPath)) {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
let config;
|
|
285
|
+
try {
|
|
286
|
+
config = loadConfig(configPath);
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
const raw = config.workflowPaths;
|
|
292
|
+
if (!Array.isArray(raw)) {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
const result = [];
|
|
296
|
+
for (const item of raw) {
|
|
297
|
+
if (typeof item === "string" && item.trim() !== "") {
|
|
298
|
+
const expanded = expandTilde(item.trim());
|
|
299
|
+
result.push(resolvePath(expanded));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
238
304
|
//# sourceMappingURL=config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,18 @@ export declare function cmdThreadResume(storageRoot: string, threadId: ThreadId,
|
|
|
27
27
|
export declare function cmdThreadPoke(storageRoot: string, threadId: ThreadId, prompt: string, agentOverride: string | null): Promise<StepOutput>;
|
|
28
28
|
export declare function validateCount(count: number): void;
|
|
29
29
|
export declare function cmdThreadExec(storageRoot: string, threadId: ThreadId, agentOverride: string | null, count: number, background: boolean, backgroundWorker: boolean): Promise<StepOutput[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Block until a running thread finishes (marker disappears), then return the
|
|
32
|
+
* final thread state in the same `StepOutput[]` format that `cmdThreadExec`
|
|
33
|
+
* produces.
|
|
34
|
+
*
|
|
35
|
+
* - If the thread is currently running → poll until it stops, then return
|
|
36
|
+
* its final state.
|
|
37
|
+
* - If the thread is not running → return its current state immediately.
|
|
38
|
+
*
|
|
39
|
+
* An optional `timeoutMs` aborts the wait with an error when exceeded.
|
|
40
|
+
*/
|
|
41
|
+
export declare function cmdThreadJoin(storageRoot: string, threadId: ThreadId, timeoutMs: number | null): Promise<StepOutput[]>;
|
|
30
42
|
export declare function cmdThreadRead(storageRoot: string, threadId: ThreadId, quota?: number, before?: CasRef | null, showStart?: boolean): Promise<string>;
|
|
31
43
|
export type StopOutput = {
|
|
32
44
|
thread: ThreadId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/commands/thread.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,MAAM,EAEN,WAAW,EAEX,UAAU,EACV,QAAQ,EAER,cAAc,EACd,YAAY,EAIb,MAAM,4BAA4B,CAAC;AA4BpC,OAAO,EAUL,KAAK,QAAQ,EAEd,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/commands/thread.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/thread.js
CHANGED
|
@@ -14,7 +14,7 @@ import { evaluate } from "../moderator/index.js";
|
|
|
14
14
|
import { completeThread, createUwfStore, findRegistryName, getThread, loadActiveThreads, loadHistoryThreads, loadWorkflowRegistry, resolveWorkflowHash, setThread, } from "../store.js";
|
|
15
15
|
import { checkWorkflowFilenameConsistency, isCasRef, parseWorkflowPayload } from "../validate.js";
|
|
16
16
|
import { validateWorkflow } from "../validate-semantic.js";
|
|
17
|
-
import { getConfigPath, getNestedValue, loadConfig, parseDotPath } from "./config.js";
|
|
17
|
+
import { getConfigPath, getNestedValue, loadConfig, loadWorkflowPaths, parseDotPath, } from "./config.js";
|
|
18
18
|
import { collectOrderedSteps, expandOutput, fail, walkChain, } from "./shared.js";
|
|
19
19
|
import { materializeWorkflowPayload } from "./workflow.js";
|
|
20
20
|
const END_ROLE = "$END";
|
|
@@ -81,6 +81,12 @@ async function resolveActiveThreadStatus(storageRoot, threadId, uwf, head) {
|
|
|
81
81
|
if (runningMarker !== null) {
|
|
82
82
|
return "running";
|
|
83
83
|
}
|
|
84
|
+
// Check the persisted entry status first — agent failure suspends the thread
|
|
85
|
+
// via markThreadSuspended() without producing a $SUSPEND output in CAS.
|
|
86
|
+
const entry = getThread(uwf.varStore, threadId);
|
|
87
|
+
if (entry !== null && entry.status === "suspended") {
|
|
88
|
+
return "suspended";
|
|
89
|
+
}
|
|
84
90
|
const chain = walkChain(uwf, head);
|
|
85
91
|
const { lastOutput } = resolveEvaluateArgs(uwf, chain);
|
|
86
92
|
if (readSuspendReason(lastOutput) !== null) {
|
|
@@ -124,9 +130,19 @@ function buildResumePrompt(graphPrompt, supplement) {
|
|
|
124
130
|
}
|
|
125
131
|
return `${graphPrompt}\n\n${supplement}`;
|
|
126
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Error thrown by failStep so that callers can catch it, persist
|
|
135
|
+
* thread state (e.g. suspend), and then re-throw / exit.
|
|
136
|
+
*/
|
|
137
|
+
class StepFailureError extends Error {
|
|
138
|
+
constructor(message) {
|
|
139
|
+
super(message);
|
|
140
|
+
this.name = "StepFailureError";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
127
143
|
function failStep(plog, message) {
|
|
128
144
|
plog.log(PL_STEP_ERROR, message, null);
|
|
129
|
-
|
|
145
|
+
throw new StepFailureError(message);
|
|
130
146
|
}
|
|
131
147
|
/**
|
|
132
148
|
* Check if a string looks like a file path (contains path separators or has .yaml/.yml extension).
|
|
@@ -232,6 +248,45 @@ async function findWorkflowInParents(startDir, name) {
|
|
|
232
248
|
}
|
|
233
249
|
return null;
|
|
234
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Search for a workflow by name directly in a directory (not inside .workflows/).
|
|
253
|
+
* Used for workflowPaths resolution — each path dir contains YAMLs at top level.
|
|
254
|
+
* Checks flat files (<name>.yaml/.yml) and folder layout (<name>/index.yaml/.yml).
|
|
255
|
+
*/
|
|
256
|
+
async function findWorkflowInPath(dir, name) {
|
|
257
|
+
// Check flat YAML files
|
|
258
|
+
for (const ext of [".yaml", ".yml"]) {
|
|
259
|
+
const result = await workflowFileExists(dir, name, ext);
|
|
260
|
+
if (result !== null) {
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Check folder-based layout (<name>/index.yaml)
|
|
265
|
+
for (const indexName of ["index.yaml", "index.yml"]) {
|
|
266
|
+
const candidate = resolvePath(dir, name, indexName);
|
|
267
|
+
try {
|
|
268
|
+
await access(candidate);
|
|
269
|
+
return candidate;
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
/* not found */
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Search workflowPaths directories for a workflow by name.
|
|
279
|
+
* Searches each directory in order; first match wins.
|
|
280
|
+
*/
|
|
281
|
+
async function findWorkflowInPaths(dirs, name) {
|
|
282
|
+
for (const dir of dirs) {
|
|
283
|
+
const found = await findWorkflowInPath(dir, name);
|
|
284
|
+
if (found !== null) {
|
|
285
|
+
return found;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
235
290
|
async function materializeLocalWorkflow(uwf, filePath) {
|
|
236
291
|
let text;
|
|
237
292
|
try {
|
|
@@ -294,6 +349,12 @@ async function resolveWorkflowCasRef(uwf, workflowId, projectRoot) {
|
|
|
294
349
|
if (localPath !== null) {
|
|
295
350
|
return materializeLocalWorkflow(uwf, localPath);
|
|
296
351
|
}
|
|
352
|
+
// Strategy 3.5: workflowPaths global directories
|
|
353
|
+
const workflowPaths = loadWorkflowPaths(uwf.storageRoot);
|
|
354
|
+
const pathsFile = await findWorkflowInPaths(workflowPaths, trimmed);
|
|
355
|
+
if (pathsFile !== null) {
|
|
356
|
+
return materializeLocalWorkflow(uwf, pathsFile);
|
|
357
|
+
}
|
|
297
358
|
// Strategy 4: Global registry fallback
|
|
298
359
|
const registry = loadWorkflowRegistry(uwf.varStore);
|
|
299
360
|
const hash = resolveWorkflowHash(registry, trimmed);
|
|
@@ -986,7 +1047,19 @@ export async function cmdThreadPoke(storageRoot, threadId, prompt, agentOverride
|
|
|
986
1047
|
// Spawn the agent. The agent will create a new StepNode with prev=oldHead (it reads
|
|
987
1048
|
// the active thread head). After the agent returns, we rewrite that node's prev so
|
|
988
1049
|
// that the new head replaces the old head instead of appending after it.
|
|
989
|
-
|
|
1050
|
+
let agentResult;
|
|
1051
|
+
try {
|
|
1052
|
+
agentResult = spawnAgent(plog, agent, threadId, role, prompt, effectiveCwd);
|
|
1053
|
+
}
|
|
1054
|
+
catch (e) {
|
|
1055
|
+
if (e instanceof StepFailureError) {
|
|
1056
|
+
// Fatal agent failure in poke — persist suspended state before propagating
|
|
1057
|
+
const uwfErr = await createUwfStore(storageRoot);
|
|
1058
|
+
const errEntry = getThread(uwfErr.varStore, threadId) ?? entry;
|
|
1059
|
+
setThread(uwfErr.varStore, threadId, markThreadSuspended(errEntry, role, e.message));
|
|
1060
|
+
}
|
|
1061
|
+
throw e;
|
|
1062
|
+
}
|
|
990
1063
|
const agentStepHash = agentResult.stepHash;
|
|
991
1064
|
plog.log(PL_AGENT_DONE, `agent returned head=${agentStepHash}`, null);
|
|
992
1065
|
const uwfAfter = await createUwfStore(storageRoot);
|
|
@@ -1094,6 +1167,82 @@ export async function cmdThreadExec(storageRoot, threadId, agentOverride, count,
|
|
|
1094
1167
|
await deleteMarker(storageRoot, threadId);
|
|
1095
1168
|
}
|
|
1096
1169
|
}
|
|
1170
|
+
const JOIN_POLL_INTERVAL_MS = 1000;
|
|
1171
|
+
/**
|
|
1172
|
+
* Block until a running thread finishes (marker disappears), then return the
|
|
1173
|
+
* final thread state in the same `StepOutput[]` format that `cmdThreadExec`
|
|
1174
|
+
* produces.
|
|
1175
|
+
*
|
|
1176
|
+
* - If the thread is currently running → poll until it stops, then return
|
|
1177
|
+
* its final state.
|
|
1178
|
+
* - If the thread is not running → return its current state immediately.
|
|
1179
|
+
*
|
|
1180
|
+
* An optional `timeoutMs` aborts the wait with an error when exceeded.
|
|
1181
|
+
*/
|
|
1182
|
+
export async function cmdThreadJoin(storageRoot, threadId, timeoutMs) {
|
|
1183
|
+
const uwf = await createUwfStore(storageRoot);
|
|
1184
|
+
const entry = getThread(uwf.varStore, threadId);
|
|
1185
|
+
if (entry === null) {
|
|
1186
|
+
fail(`thread not found: ${threadId}`);
|
|
1187
|
+
}
|
|
1188
|
+
// Wait for running marker to disappear
|
|
1189
|
+
const deadline = timeoutMs !== null ? Date.now() + timeoutMs : null;
|
|
1190
|
+
while ((await isThreadRunning(storageRoot, threadId)) !== null) {
|
|
1191
|
+
if (deadline !== null && Date.now() >= deadline) {
|
|
1192
|
+
fail(`join timed out after ${timeoutMs}ms — thread ${threadId} is still running`);
|
|
1193
|
+
}
|
|
1194
|
+
await new Promise((resolve) => {
|
|
1195
|
+
setTimeout(resolve, JOIN_POLL_INTERVAL_MS);
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
// Thread is no longer running — read final state.
|
|
1199
|
+
// Re-open the store to get the latest state written by the worker.
|
|
1200
|
+
const freshUwf = await createUwfStore(storageRoot);
|
|
1201
|
+
const freshEntry = getThread(freshUwf.varStore, threadId);
|
|
1202
|
+
if (freshEntry === null) {
|
|
1203
|
+
fail(`thread disappeared after join: ${threadId}`);
|
|
1204
|
+
}
|
|
1205
|
+
const activeHead = freshEntry.head;
|
|
1206
|
+
const workflowHash = resolveWorkflowFromHead(freshUwf, activeHead);
|
|
1207
|
+
if (workflowHash === null) {
|
|
1208
|
+
fail(`failed to resolve workflow from head: ${activeHead}`);
|
|
1209
|
+
}
|
|
1210
|
+
// Build the StepOutput matching exec's format
|
|
1211
|
+
if (freshEntry.status === "end" || freshEntry.status === "cancelled") {
|
|
1212
|
+
return [
|
|
1213
|
+
{
|
|
1214
|
+
workflow: workflowHash,
|
|
1215
|
+
thread: threadId,
|
|
1216
|
+
head: activeHead,
|
|
1217
|
+
status: freshEntry.status,
|
|
1218
|
+
currentRole: null,
|
|
1219
|
+
suspendedRole: null,
|
|
1220
|
+
suspendMessage: null,
|
|
1221
|
+
done: true,
|
|
1222
|
+
background: null,
|
|
1223
|
+
error: null,
|
|
1224
|
+
},
|
|
1225
|
+
];
|
|
1226
|
+
}
|
|
1227
|
+
// Active thread — resolve detailed status
|
|
1228
|
+
const status = await resolveActiveThreadStatus(storageRoot, threadId, freshUwf, activeHead);
|
|
1229
|
+
const currentRole = resolveCurrentRole(freshUwf, activeHead, workflowHash);
|
|
1230
|
+
const suspendFields = resolveSuspendFieldsForShow(freshEntry, status, freshUwf, activeHead);
|
|
1231
|
+
return [
|
|
1232
|
+
{
|
|
1233
|
+
workflow: workflowHash,
|
|
1234
|
+
thread: threadId,
|
|
1235
|
+
head: activeHead,
|
|
1236
|
+
status,
|
|
1237
|
+
currentRole,
|
|
1238
|
+
suspendedRole: suspendFields.suspendedRole,
|
|
1239
|
+
suspendMessage: suspendFields.suspendMessage,
|
|
1240
|
+
done: false,
|
|
1241
|
+
background: null,
|
|
1242
|
+
error: null,
|
|
1243
|
+
},
|
|
1244
|
+
];
|
|
1245
|
+
}
|
|
1097
1246
|
async function resolveActiveThreadWorkflowHash(storageRoot, threadId) {
|
|
1098
1247
|
const uwf = await createUwfStore(storageRoot);
|
|
1099
1248
|
const entry = getThread(uwf.varStore, threadId);
|
|
@@ -1250,6 +1399,28 @@ async function cmdThreadStepOnce(storageRoot, threadId, agentOverride, plog, res
|
|
|
1250
1399
|
args: [...agent.args, threadId, role].join(" "),
|
|
1251
1400
|
});
|
|
1252
1401
|
loadDotenv({ path: getEnvPath(storageRoot) });
|
|
1402
|
+
// Wrap agent execution in a try-catch: when the agent command crashes
|
|
1403
|
+
// (non-zero exit, unparseable output, invalid CAS node, etc.), failStep throws
|
|
1404
|
+
// StepFailureError. We catch it to persist suspended state before re-throwing
|
|
1405
|
+
// so the CLI still exits non-zero.
|
|
1406
|
+
try {
|
|
1407
|
+
return await executeAndProcessAgentStep(storageRoot, threadId, headHash, workflowHash, workflow, role, edgePrompt, effectiveCwd, agent, plog);
|
|
1408
|
+
}
|
|
1409
|
+
catch (e) {
|
|
1410
|
+
if (e instanceof StepFailureError) {
|
|
1411
|
+
// Fatal agent failure — persist suspended state before propagating
|
|
1412
|
+
const uwfErr = await createUwfStore(storageRoot);
|
|
1413
|
+
const errEntry = getThread(uwfErr.varStore, threadId) ?? createThreadIndexEntry(headHash);
|
|
1414
|
+
setThread(uwfErr.varStore, threadId, markThreadSuspended(errEntry, role, e.message));
|
|
1415
|
+
}
|
|
1416
|
+
throw e;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Execute the agent command and process the result. Separated from cmdThreadStepOnce
|
|
1421
|
+
* so that fatal failures (StepFailureError) can be caught and handled by the caller.
|
|
1422
|
+
*/
|
|
1423
|
+
async function executeAndProcessAgentStep(storageRoot, threadId, headHash, workflowHash, workflow, role, edgePrompt, effectiveCwd, agent, plog) {
|
|
1253
1424
|
const agentResult = spawnAgent(plog, agent, threadId, role, edgePrompt, effectiveCwd);
|
|
1254
1425
|
const newHead = agentResult.stepHash;
|
|
1255
1426
|
plog.log(PL_AGENT_DONE, `agent returned head=${newHead}`, null);
|
|
@@ -1264,18 +1435,22 @@ async function cmdThreadStepOnce(storageRoot, threadId, agentOverride, plog, res
|
|
|
1264
1435
|
// next exec (until eventual success records `previousAttempts` linking the
|
|
1265
1436
|
// failed step hashes).
|
|
1266
1437
|
if (agentResult.isError === true) {
|
|
1267
|
-
|
|
1438
|
+
const errorMsg = agentResult.errorMessage ?? "agent reported error";
|
|
1439
|
+
plog.log(PL_AGENT_ERROR, `agent reported recoverable failure stepHash=${newHead} message=${errorMsg}`, null);
|
|
1440
|
+
// Persist suspended state so `thread list --status suspended` reflects the failure.
|
|
1441
|
+
const priorEntry = getThread(uwfAfter.varStore, threadId) ?? createThreadIndexEntry(headHash);
|
|
1442
|
+
setThread(uwfAfter.varStore, threadId, markThreadSuspended(priorEntry, role, errorMsg));
|
|
1268
1443
|
return {
|
|
1269
1444
|
workflow: workflowHash,
|
|
1270
1445
|
thread: threadId,
|
|
1271
1446
|
head: headHash,
|
|
1272
|
-
status: "
|
|
1447
|
+
status: "suspended",
|
|
1273
1448
|
currentRole: role,
|
|
1274
|
-
suspendedRole:
|
|
1275
|
-
suspendMessage:
|
|
1449
|
+
suspendedRole: role,
|
|
1450
|
+
suspendMessage: errorMsg,
|
|
1276
1451
|
done: false,
|
|
1277
1452
|
background: null,
|
|
1278
|
-
error: { stepHash: newHead, message:
|
|
1453
|
+
error: { stepHash: newHead, message: errorMsg },
|
|
1279
1454
|
};
|
|
1280
1455
|
}
|
|
1281
1456
|
return finalizeAgentStep(storageRoot, threadId, workflowHash, workflow, newHead, uwfAfter, plog);
|