u-foo 3.0.0 → 3.0.2
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/package.json +1 -1
- package/src/agents/prompts/native/tasks.js +4 -1
- package/src/app/chat/commandExecutor.js +111 -1
- package/src/app/chat/commands.js +2 -1
- package/src/app/chat/daemonMessageRouter.js +1 -1
- package/src/app/chat/inputSubmitHandler.js +3 -2
- package/src/code/agent.js +17 -3
- package/src/code/commands.js +3 -3
- package/src/code/context/executionSegment.js +5 -0
- package/src/code/context/planMode.js +8 -1
- package/src/code/context/promptLayers.js +10 -9
- package/src/code/dispatch.js +4 -0
- package/src/code/index.js +2 -0
- package/src/code/modelCommand.js +199 -23
- package/src/code/nativeRunner.js +299 -225
- package/src/code/protocol/controlPlane.js +93 -0
- package/src/code/protocol/faultHarness.js +90 -0
- package/src/code/protocol/index.js +20 -0
- package/src/code/protocol/loopEvents.js +102 -0
- package/src/code/protocol/materialize.js +107 -0
- package/src/code/protocol/messageFixtures.js +116 -0
- package/src/code/protocol/ownership.js +147 -0
- package/src/code/protocol/protocolValidator.js +165 -0
- package/src/code/protocol/suspension.js +173 -0
- package/src/code/protocol/toolCallLedger.js +222 -0
- package/src/code/protocol/transitions.js +97 -0
- package/src/code/providers/anthropicMessagesTransport.js +93 -0
- package/src/code/providers/index.js +8 -0
- package/src/code/providers/modelsCatalog.js +304 -0
- package/src/code/providers/openaiChatTransport.js +98 -0
- package/src/code/providers/transportContract.js +46 -0
- package/src/code/repl.js +45 -29
- package/src/code/runtime/taskControl.js +177 -53
- package/src/code/runtime/taskFocus.js +30 -10
- package/src/code/runtime/taskLoop.js +25 -3
- package/src/code/runtime/taskRun.js +172 -2
- package/src/code/runtime/workspaceLease.js +41 -0
- package/src/code/sessionStore.js +1 -0
- package/src/code/taskRoute.js +73 -0
- package/src/code/thinkingLevels.js +132 -0
- package/src/code/tools/taskRun.js +118 -0
- package/src/config.js +10 -1
- package/src/ui/format/index.js +48 -3
- package/src/ui/ink/ChatApp.js +137 -25
- package/src/ui/ink/UcodeApp.js +38 -30
- package/src/ui/ink/chatLogModel.js +238 -32
- package/src/ui/ink/chatReducer.js +18 -6
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Control-plane TaskRun lifecycle:
|
|
5
|
+
* - startTask: graph-bound TaskRun from a plan_graph task_loop node
|
|
6
|
+
* - startStandaloneTask: single-point TaskRun (no plan graph / Plan Mode required)
|
|
7
|
+
* - cancel/fail/complete by nodeId or taskRunId
|
|
6
8
|
*
|
|
7
9
|
* complete_task:
|
|
8
10
|
* - taskRunId → owning TaskLoop submitting TaskRun result
|
|
@@ -59,6 +61,44 @@ function dependenciesSatisfied(parent = null, node = null) {
|
|
|
59
61
|
return { ok: unmet.length === 0, dependencies: unmet };
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
function rejectMaxConcurrent(executionState = null) {
|
|
65
|
+
const activeCount = listActiveWritingTaskRuns(executionState).length;
|
|
66
|
+
const leaseCount = countWriteLeases(executionState);
|
|
67
|
+
if (activeCount >= MAX_CONCURRENT_WRITE_LEASES || leaseCount >= MAX_CONCURRENT_WRITE_LEASES) {
|
|
68
|
+
return {
|
|
69
|
+
status: "rejected",
|
|
70
|
+
ok: false,
|
|
71
|
+
errors: [{
|
|
72
|
+
code: "MAX_CONCURRENT_TASKS",
|
|
73
|
+
message: `At most ${MAX_CONCURRENT_WRITE_LEASES} concurrent writing TaskRuns`,
|
|
74
|
+
max: MAX_CONCURRENT_WRITE_LEASES,
|
|
75
|
+
current: Math.max(activeCount, leaseCount),
|
|
76
|
+
}],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveActiveRun(executionState = null, {
|
|
83
|
+
nodeId = "",
|
|
84
|
+
taskRunId = "",
|
|
85
|
+
} = {}) {
|
|
86
|
+
const runId = String(taskRunId || "").trim();
|
|
87
|
+
if (runId) {
|
|
88
|
+
const run = getTaskRun(executionState, runId);
|
|
89
|
+
if (!run) return { run: null, errorCode: "TASK_RUN_NOT_FOUND" };
|
|
90
|
+
if (run.status === "queued" || run.status === "running" || run.status === "cancelling") {
|
|
91
|
+
return { run, errorCode: "" };
|
|
92
|
+
}
|
|
93
|
+
return { run, errorCode: "TASK_ALREADY_TERMINAL" };
|
|
94
|
+
}
|
|
95
|
+
const id = String(nodeId || "").trim();
|
|
96
|
+
if (!id) return { run: null, errorCode: "TASK_NOT_RUNNING" };
|
|
97
|
+
const active = findActiveTaskRunForNode(executionState, id);
|
|
98
|
+
if (active) return { run: active, errorCode: "" };
|
|
99
|
+
return { run: null, errorCode: "TASK_NOT_RUNNING" };
|
|
100
|
+
}
|
|
101
|
+
|
|
62
102
|
function startTask(executionState = null, {
|
|
63
103
|
nodeId = "",
|
|
64
104
|
commandId = "",
|
|
@@ -125,20 +165,8 @@ function startTask(executionState = null, {
|
|
|
125
165
|
};
|
|
126
166
|
}
|
|
127
167
|
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
if (activeCount >= MAX_CONCURRENT_WRITE_LEASES || leaseCount >= MAX_CONCURRENT_WRITE_LEASES) {
|
|
131
|
-
return {
|
|
132
|
-
status: "rejected",
|
|
133
|
-
ok: false,
|
|
134
|
-
errors: [{
|
|
135
|
-
code: "MAX_CONCURRENT_TASKS",
|
|
136
|
-
message: `At most ${MAX_CONCURRENT_WRITE_LEASES} concurrent writing TaskRuns`,
|
|
137
|
-
max: MAX_CONCURRENT_WRITE_LEASES,
|
|
138
|
-
current: Math.max(activeCount, leaseCount),
|
|
139
|
-
}],
|
|
140
|
-
};
|
|
141
|
-
}
|
|
168
|
+
const limited = rejectMaxConcurrent(executionState);
|
|
169
|
+
if (limited) return limited;
|
|
142
170
|
|
|
143
171
|
// Freeze spec snapshot on node.runtime
|
|
144
172
|
if (!node.runtime || typeof node.runtime !== "object") node.runtime = {};
|
|
@@ -151,16 +179,20 @@ function startTask(executionState = null, {
|
|
|
151
179
|
: { kind: "task_loop" },
|
|
152
180
|
};
|
|
153
181
|
|
|
182
|
+
const objective = node.objective || node.title || id;
|
|
154
183
|
const run = createTaskRun({
|
|
184
|
+
kind: "graph_node",
|
|
155
185
|
parentGraphId: parent.graphId || "",
|
|
156
186
|
parentNodeId: id,
|
|
157
187
|
attempt: (Number(node.attempt) || 0) + 1,
|
|
188
|
+
objective,
|
|
189
|
+
title: node.title || objective,
|
|
158
190
|
});
|
|
159
191
|
const child = createChildGraphState({
|
|
160
192
|
parentGraphId: parent.graphId || "",
|
|
161
193
|
parentNodeId: id,
|
|
162
194
|
taskRunId: run.id,
|
|
163
|
-
objective
|
|
195
|
+
objective,
|
|
164
196
|
});
|
|
165
197
|
run.childGraphId = child.graphId;
|
|
166
198
|
putTaskRun(executionState, run);
|
|
@@ -192,37 +224,116 @@ function startTask(executionState = null, {
|
|
|
192
224
|
return payload;
|
|
193
225
|
}
|
|
194
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Start a TaskRun that is not attached to any plan_graph node.
|
|
229
|
+
* Orthogonal to Plan Mode: never enters or requires Plan Mode.
|
|
230
|
+
*/
|
|
231
|
+
function startStandaloneTask(executionState = null, {
|
|
232
|
+
objective = "",
|
|
233
|
+
title = "",
|
|
234
|
+
commandId = "",
|
|
235
|
+
runTool = null,
|
|
236
|
+
knownTools = null,
|
|
237
|
+
processImmediately = true,
|
|
238
|
+
} = {}) {
|
|
239
|
+
const cached = getCachedControlCommand(executionState, commandId);
|
|
240
|
+
if (cached) return { ...cached, idempotentReplay: true };
|
|
241
|
+
|
|
242
|
+
ensureGraphs(executionState);
|
|
243
|
+
|
|
244
|
+
const goal = String(objective || title || "").trim();
|
|
245
|
+
if (!goal) {
|
|
246
|
+
return {
|
|
247
|
+
status: "rejected",
|
|
248
|
+
ok: false,
|
|
249
|
+
errors: [{ code: "OBJECTIVE_REQUIRED", message: "standalone task requires objective" }],
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const limited = rejectMaxConcurrent(executionState);
|
|
254
|
+
if (limited) return limited;
|
|
255
|
+
|
|
256
|
+
const run = createTaskRun({
|
|
257
|
+
kind: "standalone",
|
|
258
|
+
parentGraphId: "",
|
|
259
|
+
parentNodeId: "",
|
|
260
|
+
attempt: 1,
|
|
261
|
+
objective: goal,
|
|
262
|
+
title: String(title || goal).trim(),
|
|
263
|
+
});
|
|
264
|
+
const child = createChildGraphState({
|
|
265
|
+
parentGraphId: "",
|
|
266
|
+
parentNodeId: "",
|
|
267
|
+
taskRunId: run.id,
|
|
268
|
+
objective: goal,
|
|
269
|
+
});
|
|
270
|
+
run.childGraphId = child.graphId;
|
|
271
|
+
putTaskRun(executionState, run);
|
|
272
|
+
setGraph(executionState, child);
|
|
273
|
+
|
|
274
|
+
const payload = {
|
|
275
|
+
status: "started",
|
|
276
|
+
ok: true,
|
|
277
|
+
kind: "standalone",
|
|
278
|
+
graphId: "",
|
|
279
|
+
nodeId: "",
|
|
280
|
+
taskRunId: run.id,
|
|
281
|
+
childGraphId: child.graphId,
|
|
282
|
+
objective: goal,
|
|
283
|
+
title: run.title,
|
|
284
|
+
parentNodeStatus: "",
|
|
285
|
+
};
|
|
286
|
+
cacheControlCommand(executionState, commandId, payload);
|
|
287
|
+
|
|
288
|
+
enqueueTaskEvent(executionState, run.id, { kind: "advance" });
|
|
289
|
+
|
|
290
|
+
if (processImmediately) {
|
|
291
|
+
processTaskRun(executionState, run.id, { runTool, knownTools });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return payload;
|
|
295
|
+
}
|
|
296
|
+
|
|
195
297
|
function cancelTask(executionState = null, {
|
|
196
298
|
nodeId = "",
|
|
299
|
+
taskRunId = "",
|
|
197
300
|
reason = "",
|
|
198
301
|
commandId = "",
|
|
199
302
|
} = {}) {
|
|
200
303
|
const cached = getCachedControlCommand(executionState, commandId);
|
|
201
304
|
if (cached) return { ...cached, idempotentReplay: true };
|
|
202
305
|
|
|
203
|
-
const
|
|
204
|
-
const active =
|
|
205
|
-
if (!active) {
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
306
|
+
const resolved = resolveActiveRun(executionState, { nodeId, taskRunId });
|
|
307
|
+
const active = resolved.run;
|
|
308
|
+
if (!active || resolved.errorCode === "TASK_RUN_NOT_FOUND") {
|
|
309
|
+
const id = String(nodeId || "").trim();
|
|
310
|
+
if (id) {
|
|
311
|
+
const { node } = findParentNode(executionState, id);
|
|
312
|
+
if (node && (node.status === "succeeded" || node.status === "failed" || node.status === "cancelled")) {
|
|
313
|
+
return {
|
|
314
|
+
status: "rejected",
|
|
315
|
+
ok: false,
|
|
316
|
+
errors: [{
|
|
317
|
+
code: "TASK_ALREADY_TERMINAL",
|
|
318
|
+
message: `task ${id} already ${node.status}`,
|
|
319
|
+
currentStatus: node.status,
|
|
320
|
+
}],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
217
323
|
}
|
|
218
324
|
return {
|
|
219
325
|
status: "rejected",
|
|
220
326
|
ok: false,
|
|
221
|
-
errors: [{
|
|
327
|
+
errors: [{
|
|
328
|
+
code: resolved.errorCode || "TASK_NOT_RUNNING",
|
|
329
|
+
message: taskRunId
|
|
330
|
+
? `no active run for taskRunId ${taskRunId}`
|
|
331
|
+
: `no active run for ${nodeId || "(missing id)"}`,
|
|
332
|
+
}],
|
|
222
333
|
};
|
|
223
334
|
}
|
|
224
335
|
|
|
225
|
-
if (isTerminalTaskRun(active)) {
|
|
336
|
+
if (isTerminalTaskRun(active) || resolved.errorCode === "TASK_ALREADY_TERMINAL") {
|
|
226
337
|
return {
|
|
227
338
|
status: "rejected",
|
|
228
339
|
ok: false,
|
|
@@ -252,7 +363,7 @@ function cancelTask(executionState = null, {
|
|
|
252
363
|
const payload = {
|
|
253
364
|
status: "accepted",
|
|
254
365
|
ok: Boolean(done.ok),
|
|
255
|
-
nodeId:
|
|
366
|
+
nodeId: active.parentNodeId || "",
|
|
256
367
|
taskRunId: active.id,
|
|
257
368
|
parentNodeStatus: done.run ? done.run.status : "cancelled",
|
|
258
369
|
};
|
|
@@ -262,34 +373,43 @@ function cancelTask(executionState = null, {
|
|
|
262
373
|
|
|
263
374
|
function failTask(executionState = null, {
|
|
264
375
|
nodeId = "",
|
|
376
|
+
taskRunId = "",
|
|
265
377
|
reason = "",
|
|
266
378
|
commandId = "",
|
|
267
379
|
} = {}) {
|
|
268
380
|
const cached = getCachedControlCommand(executionState, commandId);
|
|
269
381
|
if (cached) return { ...cached, idempotentReplay: true };
|
|
270
382
|
|
|
271
|
-
const
|
|
272
|
-
const active =
|
|
273
|
-
if (!active) {
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
383
|
+
const resolved = resolveActiveRun(executionState, { nodeId, taskRunId });
|
|
384
|
+
const active = resolved.run;
|
|
385
|
+
if (!active || resolved.errorCode === "TASK_RUN_NOT_FOUND") {
|
|
386
|
+
const id = String(nodeId || "").trim();
|
|
387
|
+
if (id) {
|
|
388
|
+
const { node } = findParentNode(executionState, id);
|
|
389
|
+
if (node && (node.status === "succeeded" || node.status === "failed" || node.status === "cancelled")) {
|
|
390
|
+
return {
|
|
391
|
+
status: "rejected",
|
|
392
|
+
ok: false,
|
|
393
|
+
errors: [{
|
|
394
|
+
code: "TASK_ALREADY_TERMINAL",
|
|
395
|
+
message: `task ${id} already ${node.status}`,
|
|
396
|
+
currentStatus: node.status,
|
|
397
|
+
}],
|
|
398
|
+
};
|
|
399
|
+
}
|
|
285
400
|
}
|
|
286
401
|
return {
|
|
287
402
|
status: "rejected",
|
|
288
403
|
ok: false,
|
|
289
|
-
errors: [{
|
|
404
|
+
errors: [{
|
|
405
|
+
code: resolved.errorCode || "TASK_NOT_RUNNING",
|
|
406
|
+
message: taskRunId
|
|
407
|
+
? `no active run for taskRunId ${taskRunId}`
|
|
408
|
+
: `no active run for ${nodeId || "(missing id)"}`,
|
|
409
|
+
}],
|
|
290
410
|
};
|
|
291
411
|
}
|
|
292
|
-
if (isTerminalTaskRun(active)) {
|
|
412
|
+
if (isTerminalTaskRun(active) || resolved.errorCode === "TASK_ALREADY_TERMINAL") {
|
|
293
413
|
return {
|
|
294
414
|
status: "rejected",
|
|
295
415
|
ok: false,
|
|
@@ -313,7 +433,7 @@ function failTask(executionState = null, {
|
|
|
313
433
|
const payload = {
|
|
314
434
|
status: done.ok ? "accepted" : "rejected",
|
|
315
435
|
ok: Boolean(done.ok),
|
|
316
|
-
nodeId:
|
|
436
|
+
nodeId: active.parentNodeId || "",
|
|
317
437
|
taskRunId: active.id,
|
|
318
438
|
parentNodeStatus: done.run ? done.run.status : "failed",
|
|
319
439
|
errors: done.ok ? undefined : [{ code: done.code || "CAS_FAILED", currentStatus: done.currentStatus }],
|
|
@@ -485,14 +605,16 @@ function runControlActions(executionState = null, {
|
|
|
485
605
|
} else if (op === "cancel_task") {
|
|
486
606
|
results.push(cancelTask(executionState, {
|
|
487
607
|
nodeId: action.nodeId,
|
|
608
|
+
taskRunId: action.taskRunId,
|
|
488
609
|
reason: action.reason,
|
|
489
|
-
commandId: commandId && list.length === 1 ? commandId : `${commandId}:${op}:${action.nodeId}`,
|
|
610
|
+
commandId: commandId && list.length === 1 ? commandId : `${commandId}:${op}:${action.nodeId || action.taskRunId}`,
|
|
490
611
|
}));
|
|
491
612
|
} else if (op === "fail_task" || op === "mark_task_failed") {
|
|
492
613
|
results.push(failTask(executionState, {
|
|
493
614
|
nodeId: action.nodeId,
|
|
615
|
+
taskRunId: action.taskRunId,
|
|
494
616
|
reason: action.reason,
|
|
495
|
-
commandId: commandId && list.length === 1 ? commandId : `${commandId}:${op}:${action.nodeId}`,
|
|
617
|
+
commandId: commandId && list.length === 1 ? commandId : `${commandId}:${op}:${action.nodeId || action.taskRunId}`,
|
|
496
618
|
}));
|
|
497
619
|
} else if (op === "complete_task") {
|
|
498
620
|
const taskRunId = String(action.taskRunId || "").trim();
|
|
@@ -528,6 +650,7 @@ function runControlActions(executionState = null, {
|
|
|
528
650
|
} else if (op === "fail_current_task") {
|
|
529
651
|
results.push(failTask(executionState, {
|
|
530
652
|
nodeId: action.nodeId || (getTaskRun(executionState, action.taskRunId) || {}).parentNodeId,
|
|
653
|
+
taskRunId: action.taskRunId,
|
|
531
654
|
reason: action.reason,
|
|
532
655
|
commandId: commandId && list.length === 1 ? commandId : `${commandId}:${op}`,
|
|
533
656
|
}));
|
|
@@ -554,6 +677,7 @@ function runControlActions(executionState = null, {
|
|
|
554
677
|
|
|
555
678
|
module.exports = {
|
|
556
679
|
startTask,
|
|
680
|
+
startStandaloneTask,
|
|
557
681
|
cancelTask,
|
|
558
682
|
failTask,
|
|
559
683
|
completeTaskFromLoop,
|
|
@@ -82,6 +82,7 @@ function buildTaskFocus({
|
|
|
82
82
|
currentNodeId = "",
|
|
83
83
|
taskRunsById = {},
|
|
84
84
|
recentlyChangedFiles = [],
|
|
85
|
+
standaloneTask = null,
|
|
85
86
|
} = {}) {
|
|
86
87
|
const byId = new Map((Array.isArray(nodes) ? nodes : []).map((n) => [n.id, n]));
|
|
87
88
|
const current = byId.get(currentNodeId);
|
|
@@ -96,19 +97,38 @@ function buildTaskFocus({
|
|
|
96
97
|
});
|
|
97
98
|
const writers = Object.values(runs)
|
|
98
99
|
.filter((r) => r && (r.status === "running" || r.status === "cancelling"))
|
|
99
|
-
.map((r) => r.parentNodeId)
|
|
100
|
+
.map((r) => r.parentNodeId || r.id)
|
|
100
101
|
.filter(Boolean);
|
|
101
102
|
|
|
103
|
+
let currentTask;
|
|
104
|
+
if (standaloneTask && typeof standaloneTask === "object") {
|
|
105
|
+
currentTask = {
|
|
106
|
+
id: standaloneTask.id || currentNodeId || "standalone",
|
|
107
|
+
objective: standaloneTask.objective || standaloneTask.title || "",
|
|
108
|
+
title: standaloneTask.title || standaloneTask.objective || standaloneTask.id || "standalone",
|
|
109
|
+
status: standaloneTask.status || (currentRun && currentRun.status) || "running",
|
|
110
|
+
};
|
|
111
|
+
} else if (current) {
|
|
112
|
+
currentTask = {
|
|
113
|
+
id: current.id,
|
|
114
|
+
objective: current.objective || current.title || "",
|
|
115
|
+
title: current.title || current.objective || current.id,
|
|
116
|
+
status: (currentRun && currentRun.status) || current.status,
|
|
117
|
+
};
|
|
118
|
+
} else {
|
|
119
|
+
currentTask = {
|
|
120
|
+
id: currentNodeId || "standalone",
|
|
121
|
+
objective: "",
|
|
122
|
+
title: currentNodeId || "standalone",
|
|
123
|
+
status: "unknown",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
102
127
|
return {
|
|
103
|
-
currentTask
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
title: current.title || current.objective || current.id,
|
|
108
|
-
status: (currentRun && currentRun.status) || current.status,
|
|
109
|
-
}
|
|
110
|
-
: { id: currentNodeId, objective: "", title: currentNodeId, status: "unknown" },
|
|
111
|
-
dependencies: listDependencySummaries(nodes, currentNodeId, runs),
|
|
128
|
+
currentTask,
|
|
129
|
+
dependencies: currentNodeId
|
|
130
|
+
? listDependencySummaries(nodes, currentNodeId, runs)
|
|
131
|
+
: [],
|
|
112
132
|
parallelSiblings: siblings,
|
|
113
133
|
workspace: {
|
|
114
134
|
concurrentWriters: writers,
|
|
@@ -242,12 +242,23 @@ function processTaskRun(executionState = null, taskRunId = "", options = {}) {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
const parent =
|
|
245
|
+
const parent = live.parentGraphId
|
|
246
|
+
? (getGraph(executionState, live.parentGraphId) || executionState.planGraph)
|
|
247
|
+
: null;
|
|
248
|
+
const standalone = !live.parentNodeId;
|
|
246
249
|
const focus = buildTaskFocus({
|
|
247
250
|
nodes: parent && parent.nodes ? parent.nodes : [],
|
|
248
251
|
currentNodeId: live.parentNodeId,
|
|
249
252
|
taskRunsById: (executionState.taskRuns && executionState.taskRuns.byId) || {},
|
|
250
253
|
recentlyChangedFiles: executionState.modifiedFiles || [],
|
|
254
|
+
standaloneTask: standalone
|
|
255
|
+
? {
|
|
256
|
+
id: live.id,
|
|
257
|
+
objective: live.objective || live.title || "",
|
|
258
|
+
title: live.title || live.objective || live.id,
|
|
259
|
+
status: live.status,
|
|
260
|
+
}
|
|
261
|
+
: null,
|
|
251
262
|
});
|
|
252
263
|
live.lastFocusText = renderTaskFocusText(focus);
|
|
253
264
|
putTaskRun(executionState, live);
|
|
@@ -357,17 +368,28 @@ function processTaskRun(executionState = null, taskRunId = "", options = {}) {
|
|
|
357
368
|
|
|
358
369
|
/**
|
|
359
370
|
* Resume queued/running TaskRuns after process restart (executionState restored).
|
|
371
|
+
* Requeues interrupted mid-tool/model runs, releases stale leases, then advances.
|
|
360
372
|
*/
|
|
361
373
|
function resumePersistedTaskRuns(executionState = null, options = {}) {
|
|
362
374
|
const state = ensureGraphs(executionState);
|
|
375
|
+
const {
|
|
376
|
+
recoverTaskRunsAfterRestart,
|
|
377
|
+
} = require("./taskRun");
|
|
378
|
+
const { releaseStaleWriteLeases } = require("./workspaceLease");
|
|
379
|
+
const recovery = recoverTaskRunsAfterRestart(state);
|
|
380
|
+
const leases = releaseStaleWriteLeases(state, {
|
|
381
|
+
maxAgeMs: options.leaseStaleMs,
|
|
382
|
+
});
|
|
363
383
|
const byId = state.taskRuns && state.taskRuns.byId ? state.taskRuns.byId : {};
|
|
364
384
|
const results = [];
|
|
365
385
|
for (const run of Object.values(byId)) {
|
|
366
386
|
if (!run) continue;
|
|
367
|
-
if (run.status !== "queued" && run.status !== "running")
|
|
387
|
+
if (run.status !== "queued" && run.status !== "running" && run.status !== "cancelling") {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
368
390
|
results.push(processTaskRun(state, run.id, options));
|
|
369
391
|
}
|
|
370
|
-
return results;
|
|
392
|
+
return { recovery, leases, results };
|
|
371
393
|
}
|
|
372
394
|
|
|
373
395
|
module.exports = {
|