@useorgx/openclaw-plugin 0.7.11 → 0.7.16
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 +94 -122
- package/dashboard/dist/assets/8pbG6uLK.js +1 -0
- package/dashboard/dist/assets/8pbG6uLK.js.br +0 -0
- package/dashboard/dist/assets/8pbG6uLK.js.gz +0 -0
- package/dashboard/dist/assets/B1LENRC8.js +212 -0
- package/dashboard/dist/assets/B1LENRC8.js.br +0 -0
- package/dashboard/dist/assets/B1LENRC8.js.gz +0 -0
- package/dashboard/dist/assets/{tcEHYcbW.js → BCudUvwg.js} +1 -1
- package/dashboard/dist/assets/BCudUvwg.js.br +0 -0
- package/dashboard/dist/assets/BCudUvwg.js.gz +0 -0
- package/dashboard/dist/assets/{Du1wfrXa.js → BM75sh1f.js} +2 -2
- package/dashboard/dist/assets/BM75sh1f.js.br +0 -0
- package/dashboard/dist/assets/BM75sh1f.js.gz +0 -0
- package/dashboard/dist/assets/BV0BcV1u.js +53 -0
- package/dashboard/dist/assets/BV0BcV1u.js.br +0 -0
- package/dashboard/dist/assets/BV0BcV1u.js.gz +0 -0
- package/dashboard/dist/assets/BYVYH9CH.js +1 -0
- package/dashboard/dist/assets/BYVYH9CH.js.br +0 -0
- package/dashboard/dist/assets/BYVYH9CH.js.gz +0 -0
- package/dashboard/dist/assets/BjK42gtU.js +1 -0
- package/dashboard/dist/assets/BjK42gtU.js.br +0 -0
- package/dashboard/dist/assets/BjK42gtU.js.gz +0 -0
- package/dashboard/dist/assets/BkMrrjAv.js +1 -0
- package/dashboard/dist/assets/BkMrrjAv.js.br +0 -0
- package/dashboard/dist/assets/BkMrrjAv.js.gz +0 -0
- package/dashboard/dist/assets/BpF7v1Dk.js +1 -0
- package/dashboard/dist/assets/BpF7v1Dk.js.br +0 -0
- package/dashboard/dist/assets/BpF7v1Dk.js.gz +0 -0
- package/dashboard/dist/assets/{AqVoI3SF.js → Bv_86bUY.js} +1 -1
- package/dashboard/dist/assets/Bv_86bUY.js.br +0 -0
- package/dashboard/dist/assets/Bv_86bUY.js.gz +0 -0
- package/dashboard/dist/assets/C-MOJWHs.js +1 -0
- package/dashboard/dist/assets/C-MOJWHs.js.br +0 -0
- package/dashboard/dist/assets/C-MOJWHs.js.gz +0 -0
- package/dashboard/dist/assets/C3PrI8L7.js +1 -0
- package/dashboard/dist/assets/C3PrI8L7.js.br +0 -0
- package/dashboard/dist/assets/C3PrI8L7.js.gz +0 -0
- package/dashboard/dist/assets/{CD-q5mdP.js → C6AqbA9J.js} +1 -1
- package/dashboard/dist/assets/C6AqbA9J.js.br +0 -0
- package/dashboard/dist/assets/C6AqbA9J.js.gz +0 -0
- package/dashboard/dist/assets/CSlBSRyv.js +1 -0
- package/dashboard/dist/assets/CSlBSRyv.js.br +0 -0
- package/dashboard/dist/assets/CSlBSRyv.js.gz +0 -0
- package/dashboard/dist/assets/{beHYBbh6.js → CUXb_4F3.js} +2 -2
- package/dashboard/dist/assets/CUXb_4F3.js.br +0 -0
- package/dashboard/dist/assets/CUXb_4F3.js.gz +0 -0
- package/dashboard/dist/assets/Cn8sRTkO.js +1 -0
- package/dashboard/dist/assets/Cn8sRTkO.js.br +0 -0
- package/dashboard/dist/assets/Cn8sRTkO.js.gz +0 -0
- package/dashboard/dist/assets/D5IgXoTj.js +1 -0
- package/dashboard/dist/assets/D5IgXoTj.js.br +0 -0
- package/dashboard/dist/assets/D5IgXoTj.js.gz +0 -0
- package/dashboard/dist/assets/DMKyYAtD.js +1 -0
- package/dashboard/dist/assets/DMKyYAtD.js.br +0 -0
- package/dashboard/dist/assets/DMKyYAtD.js.gz +0 -0
- package/dashboard/dist/assets/{DCP-C7fn.js → DXzpQUC0.js} +1 -1
- package/dashboard/dist/assets/DXzpQUC0.js.br +0 -0
- package/dashboard/dist/assets/DXzpQUC0.js.gz +0 -0
- package/dashboard/dist/assets/Dj2k1r16.js +8 -0
- package/dashboard/dist/assets/Dj2k1r16.js.br +0 -0
- package/dashboard/dist/assets/Dj2k1r16.js.gz +0 -0
- package/dashboard/dist/assets/JDPvhd68.js +1 -0
- package/dashboard/dist/assets/JDPvhd68.js.br +0 -0
- package/dashboard/dist/assets/JDPvhd68.js.gz +0 -0
- package/dashboard/dist/assets/R6N_VVqm.js +1 -0
- package/dashboard/dist/assets/R6N_VVqm.js.br +0 -0
- package/dashboard/dist/assets/R6N_VVqm.js.gz +0 -0
- package/dashboard/dist/assets/cEP7N1dn.js +1 -0
- package/dashboard/dist/assets/cEP7N1dn.js.br +0 -0
- package/dashboard/dist/assets/cEP7N1dn.js.gz +0 -0
- package/dashboard/dist/assets/cX2e-TLi.js +1 -0
- package/dashboard/dist/assets/cX2e-TLi.js.br +0 -0
- package/dashboard/dist/assets/cX2e-TLi.js.gz +0 -0
- package/dashboard/dist/assets/eeHXe_OQ.js +9 -0
- package/dashboard/dist/assets/eeHXe_OQ.js.br +0 -0
- package/dashboard/dist/assets/eeHXe_OQ.js.gz +0 -0
- package/dashboard/dist/assets/iLnvdWmW.css +1 -0
- package/dashboard/dist/assets/iLnvdWmW.css.br +0 -0
- package/dashboard/dist/assets/iLnvdWmW.css.gz +0 -0
- package/dashboard/dist/assets/konqMbVI.js +1 -0
- package/dashboard/dist/assets/konqMbVI.js.br +0 -0
- package/dashboard/dist/assets/konqMbVI.js.gz +0 -0
- package/dashboard/dist/assets/wc6cgXzV.js +1 -0
- package/dashboard/dist/assets/wc6cgXzV.js.br +0 -0
- package/dashboard/dist/assets/wc6cgXzV.js.gz +0 -0
- package/dashboard/dist/brand/control-tower.png +0 -0
- package/dashboard/dist/brand/design-codex.png +0 -0
- package/dashboard/dist/brand/engineering-autopilot.png +0 -0
- package/dashboard/dist/brand/launch-captain.png +0 -0
- package/dashboard/dist/brand/orgx-logo.png +0 -0
- package/dashboard/dist/brand/pipeline-intelligence.png +0 -0
- package/dashboard/dist/brand/product-orchestrator.png +0 -0
- package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
- package/dashboard/dist/index.html +8 -6
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/hash-utils.d.ts +1 -0
- package/dist/hash-utils.js +4 -0
- package/dist/http/helpers/auto-continue-engine.d.ts +15 -0
- package/dist/http/helpers/auto-continue-engine.js +289 -67
- package/dist/http/helpers/autopilot-slice-utils.js +112 -66
- package/dist/http/helpers/hash-utils.d.ts +1 -1
- package/dist/http/helpers/hash-utils.js +1 -1
- package/dist/http/helpers/mission-control.d.ts +3 -0
- package/dist/http/helpers/mission-control.js +43 -9
- package/dist/http/helpers/queue-constants.d.ts +37 -0
- package/dist/http/helpers/queue-constants.js +34 -0
- package/dist/http/helpers/slice-experience-v2.js +2 -5
- package/dist/http/helpers/slice-run-projections.js +2 -5
- package/dist/http/helpers/value-utils.d.ts +1 -1
- package/dist/http/helpers/value-utils.js +5 -2
- package/dist/http/helpers/workspace-scope.js +4 -3
- package/dist/http/index.js +104 -61
- package/dist/http/routes/chat.d.ts +1 -1
- package/dist/http/routes/chat.js +3 -23
- package/dist/http/routes/entities.js +60 -2
- package/dist/http/routes/entity-dynamic.js +49 -9
- package/dist/http/routes/live-snapshot.d.ts +10 -1
- package/dist/http/routes/live-snapshot.js +15 -26
- package/dist/http/routes/mission-control-actions.d.ts +6 -0
- package/dist/http/routes/mission-control-actions.js +35 -18
- package/dist/http/routes/mission-control-read.js +4 -107
- package/dist/lib/type-coercion.d.ts +10 -0
- package/dist/lib/type-coercion.js +82 -0
- package/dist/mcp-http-handler.js +14 -2
- package/dist/openclaw.plugin.json +1 -1
- package/dist/services/experiment-randomization.js +9 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -2
- package/dashboard/dist/assets/AqVoI3SF.js.br +0 -0
- package/dashboard/dist/assets/AqVoI3SF.js.gz +0 -0
- package/dashboard/dist/assets/BC4WvnHJ.js +0 -1
- package/dashboard/dist/assets/BC4WvnHJ.js.br +0 -0
- package/dashboard/dist/assets/BC4WvnHJ.js.gz +0 -0
- package/dashboard/dist/assets/BG5mwTkg.js +0 -1
- package/dashboard/dist/assets/BG5mwTkg.js.br +0 -0
- package/dashboard/dist/assets/BG5mwTkg.js.gz +0 -0
- package/dashboard/dist/assets/BJgZIVUQ.js +0 -53
- package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
- package/dashboard/dist/assets/BNh-XYPV.js +0 -1
- package/dashboard/dist/assets/BNh-XYPV.js.br +0 -0
- package/dashboard/dist/assets/BNh-XYPV.js.gz +0 -0
- package/dashboard/dist/assets/BTAEErUY.js +0 -1
- package/dashboard/dist/assets/BTAEErUY.js.br +0 -0
- package/dashboard/dist/assets/BTAEErUY.js.gz +0 -0
- package/dashboard/dist/assets/BepW_590.js +0 -1
- package/dashboard/dist/assets/BepW_590.js.br +0 -0
- package/dashboard/dist/assets/BepW_590.js.gz +0 -0
- package/dashboard/dist/assets/BerAfzjq.js +0 -1
- package/dashboard/dist/assets/BerAfzjq.js.br +0 -0
- package/dashboard/dist/assets/BerAfzjq.js.gz +0 -0
- package/dashboard/dist/assets/Bp3N-QL5.js +0 -212
- package/dashboard/dist/assets/Bp3N-QL5.js.br +0 -0
- package/dashboard/dist/assets/Bp3N-QL5.js.gz +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js +0 -1
- package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
- package/dashboard/dist/assets/C3dZRz9P.css +0 -1
- package/dashboard/dist/assets/C3dZRz9P.css.br +0 -0
- package/dashboard/dist/assets/C3dZRz9P.css.gz +0 -0
- package/dashboard/dist/assets/CD-q5mdP.js.br +0 -0
- package/dashboard/dist/assets/CD-q5mdP.js.gz +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js +0 -1
- package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
- package/dashboard/dist/assets/CdvjC9G9.js +0 -1
- package/dashboard/dist/assets/CdvjC9G9.js.br +0 -0
- package/dashboard/dist/assets/CdvjC9G9.js.gz +0 -0
- package/dashboard/dist/assets/Ck2agw-s.js +0 -1
- package/dashboard/dist/assets/Ck2agw-s.js.br +0 -0
- package/dashboard/dist/assets/Ck2agw-s.js.gz +0 -0
- package/dashboard/dist/assets/CxQ08qFN.js +0 -9
- package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
- package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
- package/dashboard/dist/assets/D2CH1H6k.js +0 -1
- package/dashboard/dist/assets/D2CH1H6k.js.br +0 -0
- package/dashboard/dist/assets/D2CH1H6k.js.gz +0 -0
- package/dashboard/dist/assets/D9esz7jd.js +0 -1
- package/dashboard/dist/assets/D9esz7jd.js.br +0 -0
- package/dashboard/dist/assets/D9esz7jd.js.gz +0 -0
- package/dashboard/dist/assets/DCP-C7fn.js.br +0 -0
- package/dashboard/dist/assets/DCP-C7fn.js.gz +0 -0
- package/dashboard/dist/assets/DJASCd69.js +0 -1
- package/dashboard/dist/assets/DJASCd69.js.br +0 -0
- package/dashboard/dist/assets/DJASCd69.js.gz +0 -0
- package/dashboard/dist/assets/Dm9AybAp.js +0 -1
- package/dashboard/dist/assets/Dm9AybAp.js.br +0 -0
- package/dashboard/dist/assets/Dm9AybAp.js.gz +0 -0
- package/dashboard/dist/assets/Du1wfrXa.js.br +0 -0
- package/dashboard/dist/assets/Du1wfrXa.js.gz +0 -0
- package/dashboard/dist/assets/beHYBbh6.js.br +0 -0
- package/dashboard/dist/assets/beHYBbh6.js.gz +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js +0 -8
- package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
- package/dashboard/dist/assets/tcEHYcbW.js.br +0 -0
- package/dashboard/dist/assets/tcEHYcbW.js.gz +0 -0
|
@@ -15,6 +15,7 @@ import { buildMissionControlGraph, DEFAULT_TOKEN_BUDGET_ASSUMPTIONS, dedupeStrin
|
|
|
15
15
|
import { createAutopilotRuntime } from "./autopilot-runtime.js";
|
|
16
16
|
import { buildScopeDirective, buildSliceOutputInstructions, buildWorkstreamSlicePrompt, createCodexBinResolver, ensureAutopilotSliceSchemaPath, extractSessionIdFromLog, extractSessionIdFromOutput, fileUpdatedAtEpochMs, parseSliceResult, readFileTailSafe, readSliceOutputFile, } from "./autopilot-slice-utils.js";
|
|
17
17
|
import { pickString } from "./value-utils.js";
|
|
18
|
+
import { LaneState, RunStatus, } from "./queue-constants.js";
|
|
18
19
|
function resolveAutopilotDefaultCwd(filename) {
|
|
19
20
|
let cursor = dirname(filename);
|
|
20
21
|
for (let i = 0; i < 12; i += 1) {
|
|
@@ -1029,7 +1030,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1029
1030
|
}
|
|
1030
1031
|
const existing = run.laneByWorkstreamId[normalizedWorkstreamId] ?? {
|
|
1031
1032
|
workstreamId: normalizedWorkstreamId,
|
|
1032
|
-
state:
|
|
1033
|
+
state: LaneState.IDLE,
|
|
1033
1034
|
activeRunId: null,
|
|
1034
1035
|
activeTaskIds: [],
|
|
1035
1036
|
blockedReason: null,
|
|
@@ -1081,12 +1082,12 @@ export function createAutoContinueEngine(deps) {
|
|
|
1081
1082
|
if (lane && lane.activeRunId === sliceRunId) {
|
|
1082
1083
|
setLaneState(run, {
|
|
1083
1084
|
workstreamId: normalizedWorkstreamId,
|
|
1084
|
-
state: lane.state ===
|
|
1085
|
+
state: lane.state === LaneState.BLOCKED ? "blocked" : "idle",
|
|
1085
1086
|
activeRunId: null,
|
|
1086
1087
|
activeTaskIds: [],
|
|
1087
1088
|
retryAt: lane.retryAt ?? null,
|
|
1088
1089
|
waitingOnWorkstreamIds: lane.waitingOnWorkstreamIds ?? [],
|
|
1089
|
-
blockedReason: lane.state ===
|
|
1090
|
+
blockedReason: lane.state === LaneState.BLOCKED ? lane.blockedReason : null,
|
|
1090
1091
|
});
|
|
1091
1092
|
}
|
|
1092
1093
|
}
|
|
@@ -1389,7 +1390,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1389
1390
|
updated_at: lane.updatedAt,
|
|
1390
1391
|
}));
|
|
1391
1392
|
const patch = {
|
|
1392
|
-
auto_continue_enabled: input.run.status ===
|
|
1393
|
+
auto_continue_enabled: input.run.status === RunStatus.RUNNING || input.run.status === RunStatus.STOPPING,
|
|
1393
1394
|
auto_continue_status: input.run.status,
|
|
1394
1395
|
auto_continue_stop_reason: input.run.stopReason,
|
|
1395
1396
|
auto_continue_started_at: input.run.startedAt,
|
|
@@ -1428,7 +1429,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1428
1429
|
const now = new Date().toISOString();
|
|
1429
1430
|
ensureRunInternals(input.run);
|
|
1430
1431
|
const activeRunIds = listActiveSliceRunIds(input.run);
|
|
1431
|
-
input.run.status =
|
|
1432
|
+
input.run.status = RunStatus.STOPPED;
|
|
1432
1433
|
input.run.stopReason = input.reason;
|
|
1433
1434
|
input.run.stoppedAt = now;
|
|
1434
1435
|
input.run.updatedAt = now;
|
|
@@ -1442,7 +1443,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1442
1443
|
if (lane.activeRunId || lane.activeTaskIds.length > 0) {
|
|
1443
1444
|
setLaneState(input.run, {
|
|
1444
1445
|
workstreamId: lane.workstreamId,
|
|
1445
|
-
state: lane.state ===
|
|
1446
|
+
state: lane.state === LaneState.BLOCKED ? "blocked" : "idle",
|
|
1446
1447
|
activeRunId: null,
|
|
1447
1448
|
activeTaskIds: [],
|
|
1448
1449
|
});
|
|
@@ -1553,6 +1554,20 @@ export function createAutoContinueEngine(deps) {
|
|
|
1553
1554
|
decision_count: decisionIds.length,
|
|
1554
1555
|
last_error: input.run.lastError,
|
|
1555
1556
|
error_location: errorLocation,
|
|
1557
|
+
...(input.reason === "blocked" || input.reason === "error"
|
|
1558
|
+
? {
|
|
1559
|
+
blocker: {
|
|
1560
|
+
kind: decisionRequired ? "decision_required" : "error",
|
|
1561
|
+
summary: input.error ?? input.run.lastError ?? "Execution blocked",
|
|
1562
|
+
required_actor: decisionRequired ? "user" : "system",
|
|
1563
|
+
required_action: decisionRequired
|
|
1564
|
+
? "Resolve the pending decision in Decisions panel"
|
|
1565
|
+
: "Review the error and retry",
|
|
1566
|
+
can_skip: true,
|
|
1567
|
+
skip_route: "/orgx/api/autopilot/skip",
|
|
1568
|
+
},
|
|
1569
|
+
}
|
|
1570
|
+
: {}),
|
|
1556
1571
|
},
|
|
1557
1572
|
});
|
|
1558
1573
|
// Emit autopilot_transition event for state observers.
|
|
@@ -1572,7 +1587,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1572
1587
|
event: "autopilot_transition",
|
|
1573
1588
|
actionType: "run_state_transition",
|
|
1574
1589
|
}),
|
|
1575
|
-
old_state:
|
|
1590
|
+
old_state: LaneState.RUNNING,
|
|
1576
1591
|
new_state: input.reason === "completed" || input.reason === "stopped" ? "idle" : input.reason === "blocked" ? "blocked" : input.reason === "error" ? "error" : "idle",
|
|
1577
1592
|
reason: input.reason,
|
|
1578
1593
|
workspace_id: input.run.allowedWorkstreamIds?.[0] ?? null,
|
|
@@ -1774,7 +1789,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1774
1789
|
});
|
|
1775
1790
|
setLaneState(run, {
|
|
1776
1791
|
workstreamId: slice.workstreamId,
|
|
1777
|
-
state:
|
|
1792
|
+
state: LaneState.BLOCKED,
|
|
1778
1793
|
activeRunId: null,
|
|
1779
1794
|
activeTaskIds: [],
|
|
1780
1795
|
blockedReason: slice.lastError,
|
|
@@ -1816,6 +1831,9 @@ export function createAutoContinueEngine(deps) {
|
|
|
1816
1831
|
clearAutoContinueSliceTransientState(slice.runId);
|
|
1817
1832
|
const event = killDecision.kind === "timeout" ? "autopilot_slice_timeout" : "autopilot_slice_log_stall";
|
|
1818
1833
|
const humanLabel = killDecision.kind === "timeout" ? "timed out" : "stalled";
|
|
1834
|
+
const stallDecisionTitle = killDecision.kind === "timeout"
|
|
1835
|
+
? `Autopilot slice timed out: ${slice.workstreamTitle ?? slice.workstreamId}`
|
|
1836
|
+
: `Autopilot slice stalled: ${slice.workstreamTitle ?? slice.workstreamId}`;
|
|
1819
1837
|
await emitActivitySafe({
|
|
1820
1838
|
initiativeId: run.initiativeId,
|
|
1821
1839
|
runId: slice.runId,
|
|
@@ -1849,7 +1867,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1849
1867
|
const decisionResult = await requestDecisionQueued({
|
|
1850
1868
|
initiativeId: run.initiativeId,
|
|
1851
1869
|
correlationId: slice.runId,
|
|
1852
|
-
title:
|
|
1870
|
+
title: stallDecisionTitle,
|
|
1853
1871
|
summary: humanizeSliceFailureSummary(slice.lastError ?? `Autopilot slice ${humanLabel}`),
|
|
1854
1872
|
urgency: "high",
|
|
1855
1873
|
options: [
|
|
@@ -1902,7 +1920,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
1902
1920
|
});
|
|
1903
1921
|
setLaneState(run, {
|
|
1904
1922
|
workstreamId: slice.workstreamId,
|
|
1905
|
-
state:
|
|
1923
|
+
state: LaneState.BLOCKED,
|
|
1906
1924
|
activeRunId: null,
|
|
1907
1925
|
activeTaskIds: [],
|
|
1908
1926
|
blockedReason: slice.lastError,
|
|
@@ -1968,13 +1986,25 @@ export function createAutoContinueEngine(deps) {
|
|
|
1968
1986
|
const decisions = allDecisions.filter((item) => !isParserSyntheticFallbackDecision(item));
|
|
1969
1987
|
const normalizedBlockingDecisionCount = allDecisions.filter((item) => typeof item.blocking === "boolean" ? item.blocking : defaultDecisionBlocking).length;
|
|
1970
1988
|
const normalizedNonBlockingDecisionCount = Math.max(0, allDecisions.length - normalizedBlockingDecisionCount);
|
|
1971
|
-
const
|
|
1989
|
+
const operationalParsedStatus = parsedStatus === "completed" && normalizedBlockingDecisionCount > 0
|
|
1972
1990
|
? "needs_decision"
|
|
1973
1991
|
: parsedStatus;
|
|
1992
|
+
const parsedSummarySignal = String(parsed?.summary ?? "").toLowerCase();
|
|
1993
|
+
const parsedLooksLikeNoOutcomeCompletion = operationalParsedStatus === "error" &&
|
|
1994
|
+
(parsedSummarySignal.includes("without verifiable outcomes") ||
|
|
1995
|
+
parsedSummarySignal.includes("without output") ||
|
|
1996
|
+
parsedSummarySignal.includes("without artifacts") ||
|
|
1997
|
+
parsedSummarySignal.includes("did not report artifacts") ||
|
|
1998
|
+
(parsedSummarySignal.includes("did not report") &&
|
|
1999
|
+
parsedSummarySignal.includes("status updates")) ||
|
|
2000
|
+
parsedSummarySignal.includes("produced nothing"));
|
|
2001
|
+
const reportedParsedStatus = parsedLooksLikeNoOutcomeCompletion
|
|
2002
|
+
? "completed"
|
|
2003
|
+
: operationalParsedStatus;
|
|
1974
2004
|
slice.status =
|
|
1975
|
-
|
|
2005
|
+
operationalParsedStatus === "completed"
|
|
1976
2006
|
? "completed"
|
|
1977
|
-
:
|
|
2007
|
+
: operationalParsedStatus === "blocked" || operationalParsedStatus === "needs_decision"
|
|
1978
2008
|
? "blocked"
|
|
1979
2009
|
: "error";
|
|
1980
2010
|
slice.finishedAt = now;
|
|
@@ -2054,7 +2084,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2054
2084
|
: [];
|
|
2055
2085
|
const resultEnvelope = {
|
|
2056
2086
|
summary: userSummary,
|
|
2057
|
-
parsed_status:
|
|
2087
|
+
parsed_status: reportedParsedStatus,
|
|
2058
2088
|
task_updates: taskUpdates,
|
|
2059
2089
|
milestone_updates: milestoneUpdates,
|
|
2060
2090
|
next_actions: nextActions,
|
|
@@ -2245,7 +2275,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2245
2275
|
correlation_id: slice.runId,
|
|
2246
2276
|
requested_by_agent_id: run.agentId,
|
|
2247
2277
|
requested_by_agent_name: run.agentName,
|
|
2248
|
-
status:
|
|
2278
|
+
status: reportedParsedStatus,
|
|
2249
2279
|
artifacts: artifacts.length,
|
|
2250
2280
|
decisions: allDecisions.length,
|
|
2251
2281
|
blocking_decisions: normalizedBlockingDecisionCount,
|
|
@@ -2262,12 +2292,53 @@ export function createAutoContinueEngine(deps) {
|
|
|
2262
2292
|
...mockMeta(slice),
|
|
2263
2293
|
user_summary: userSummary,
|
|
2264
2294
|
next_actions: nextActions,
|
|
2295
|
+
outcomes: {
|
|
2296
|
+
pr_url: typeof parsed?.pr_url === "string" ? parsed.pr_url : null,
|
|
2297
|
+
pr_number: typeof parsed?.pr_number === "number" ? parsed.pr_number : null,
|
|
2298
|
+
commit_sha: typeof parsed?.commit_sha === "string" ? parsed.commit_sha : null,
|
|
2299
|
+
commit_url: typeof parsed?.commit_url === "string" ? parsed.commit_url : null,
|
|
2300
|
+
tests: null,
|
|
2301
|
+
artifact_ids: artifacts.map((a) => a.name).filter(Boolean),
|
|
2302
|
+
task_updates: taskUpdates?.length ?? 0,
|
|
2303
|
+
},
|
|
2265
2304
|
},
|
|
2266
2305
|
});
|
|
2267
2306
|
}
|
|
2268
2307
|
catch {
|
|
2269
2308
|
// best effort
|
|
2270
2309
|
}
|
|
2310
|
+
// Emit explicit session completion event for canonical agent panel state
|
|
2311
|
+
if (slice.status === "completed" || reportedParsedStatus === "completed") {
|
|
2312
|
+
await emitActivitySafe({
|
|
2313
|
+
initiativeId: run.initiativeId,
|
|
2314
|
+
runId: slice.runId,
|
|
2315
|
+
correlationId: slice.runId,
|
|
2316
|
+
phase: "completed",
|
|
2317
|
+
level: "info",
|
|
2318
|
+
message: userSummary ?? `Completed work on ${slice.workstreamTitle ?? "task"}`,
|
|
2319
|
+
metadata: {
|
|
2320
|
+
...buildSliceEnrichment({
|
|
2321
|
+
run,
|
|
2322
|
+
slice,
|
|
2323
|
+
workstreamId: slice.workstreamId,
|
|
2324
|
+
workstreamTitle: slice.workstreamTitle ?? null,
|
|
2325
|
+
domain: slice.domain,
|
|
2326
|
+
requiredSkills: slice.requiredSkills,
|
|
2327
|
+
userSummary,
|
|
2328
|
+
event: "session_completed",
|
|
2329
|
+
}),
|
|
2330
|
+
session_id: slice.cliSessionId ?? null,
|
|
2331
|
+
source_client: slice.sourceClient,
|
|
2332
|
+
workstream_title: slice.workstreamTitle ?? null,
|
|
2333
|
+
task_title: slice.workstreamTitle ?? slice.workstreamId,
|
|
2334
|
+
duration_ms: slice.finishedAt
|
|
2335
|
+
? new Date(slice.finishedAt).getTime() - new Date(slice.startedAt).getTime()
|
|
2336
|
+
: null,
|
|
2337
|
+
outcome: reportedParsedStatus ?? slice.status,
|
|
2338
|
+
artifacts_produced: artifacts.length,
|
|
2339
|
+
},
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2271
2342
|
if (slice.status === "completed") {
|
|
2272
2343
|
await emitActivitySafe({
|
|
2273
2344
|
initiativeId: run.initiativeId,
|
|
@@ -2289,7 +2360,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2289
2360
|
userSummary,
|
|
2290
2361
|
event: "autopilot_slice_handoff",
|
|
2291
2362
|
extra: {
|
|
2292
|
-
parsed_status:
|
|
2363
|
+
parsed_status: reportedParsedStatus,
|
|
2293
2364
|
artifacts: artifacts.length,
|
|
2294
2365
|
decisions: decisions.length,
|
|
2295
2366
|
decision_ids: decisionIds,
|
|
@@ -2335,7 +2406,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2335
2406
|
behavior_config_hash: slice.behaviorConfigHash,
|
|
2336
2407
|
policy_source: slice.behaviorPolicySource,
|
|
2337
2408
|
behavior_automation_level: slice.behaviorAutomationLevel,
|
|
2338
|
-
parsed_status:
|
|
2409
|
+
parsed_status: reportedParsedStatus,
|
|
2339
2410
|
has_output: Boolean(parsed),
|
|
2340
2411
|
artifacts: artifacts.length,
|
|
2341
2412
|
decisions: allDecisions.length,
|
|
@@ -2344,7 +2415,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2344
2415
|
decision_ids: decisionIds,
|
|
2345
2416
|
blocking_decision_ids: Array.from(new Set(blockingDecisionIds)),
|
|
2346
2417
|
non_blocking_decision_ids: Array.from(new Set(nonBlockingDecisionIds)),
|
|
2347
|
-
decision_required: blockingDecisionQueued ||
|
|
2418
|
+
decision_required: blockingDecisionQueued || operationalParsedStatus === "needs_decision",
|
|
2348
2419
|
status_updates_applied: statusUpdateResult.applied,
|
|
2349
2420
|
status_updates_buffered: statusUpdateResult.buffered,
|
|
2350
2421
|
reported_skill_evidence_count: skillEvidence.length,
|
|
@@ -2382,20 +2453,63 @@ export function createAutoContinueEngine(deps) {
|
|
|
2382
2453
|
queued: false,
|
|
2383
2454
|
decisionIds: [],
|
|
2384
2455
|
};
|
|
2456
|
+
const fallbackRawError = parsed?.summary ??
|
|
2457
|
+
slice.lastError ??
|
|
2458
|
+
(slice.status === "blocked"
|
|
2459
|
+
? "Execution is blocked and needs intervention."
|
|
2460
|
+
: "Agent process exited without a valid output contract.");
|
|
2461
|
+
const fallbackHumanized = humanizeSliceFailure(fallbackRawError);
|
|
2462
|
+
const fallbackErrorSignal = [
|
|
2463
|
+
parsed?.summary ?? null,
|
|
2464
|
+
slice.lastError ?? null,
|
|
2465
|
+
fallbackRawError,
|
|
2466
|
+
]
|
|
2467
|
+
.filter((entry) => typeof entry === "string" && entry.trim().length > 0)
|
|
2468
|
+
.join(" ")
|
|
2469
|
+
.toLowerCase();
|
|
2470
|
+
const looksLikeNoOutcome = fallbackErrorSignal.includes("without verifiable outcomes") ||
|
|
2471
|
+
fallbackErrorSignal.includes("without output") ||
|
|
2472
|
+
fallbackErrorSignal.includes("without artifacts") ||
|
|
2473
|
+
fallbackErrorSignal.includes("did not report artifacts") ||
|
|
2474
|
+
(fallbackErrorSignal.includes("did not report") &&
|
|
2475
|
+
fallbackErrorSignal.includes("status updates")) ||
|
|
2476
|
+
fallbackErrorSignal.includes("produced nothing");
|
|
2477
|
+
const looksLikeStall = fallbackErrorSignal.includes("stall") ||
|
|
2478
|
+
fallbackErrorSignal.includes("stopped making progress");
|
|
2479
|
+
const looksLikeTimeout = fallbackErrorSignal.includes("timeout") ||
|
|
2480
|
+
fallbackErrorSignal.includes("timed out") ||
|
|
2481
|
+
fallbackErrorSignal.includes("ran out of time");
|
|
2482
|
+
const blockedLike = slice.status === "blocked" ||
|
|
2483
|
+
looksLikeNoOutcome ||
|
|
2484
|
+
looksLikeStall ||
|
|
2485
|
+
looksLikeTimeout;
|
|
2486
|
+
const decisionConflictSource = looksLikeNoOutcome
|
|
2487
|
+
? "slice_completed_without_outcome"
|
|
2488
|
+
: looksLikeTimeout
|
|
2489
|
+
? "slice_timeout"
|
|
2490
|
+
: looksLikeStall
|
|
2491
|
+
? "slice_stall_no_output"
|
|
2492
|
+
: blockedLike
|
|
2493
|
+
? "slice_missing_blocking_decision"
|
|
2494
|
+
: "slice_invalid_output";
|
|
2495
|
+
const fallbackDecisionTitle = looksLikeNoOutcome
|
|
2496
|
+
? `Autopilot slice needs verification: ${slice.workstreamTitle ?? slice.workstreamId}`
|
|
2497
|
+
: looksLikeStall
|
|
2498
|
+
? `Autopilot slice stalled: ${slice.workstreamTitle ?? slice.workstreamId}`
|
|
2499
|
+
: looksLikeTimeout
|
|
2500
|
+
? `Autopilot slice timed out: ${slice.workstreamTitle ?? slice.workstreamId}`
|
|
2501
|
+
: blockedLike
|
|
2502
|
+
? `Autopilot slice blocked: ${slice.workstreamTitle ?? slice.workstreamId}`
|
|
2503
|
+
: `Autopilot slice failed: ${slice.workstreamTitle ?? slice.workstreamId}`;
|
|
2504
|
+
const fallbackDecisionSummary = looksLikeNoOutcome
|
|
2505
|
+
? "The slice reported completion but did not produce artifacts or status updates. Decide whether to retry, request stronger output, or mark tasks manually."
|
|
2506
|
+
: fallbackHumanized.explanation;
|
|
2385
2507
|
if (!blockingDecisionQueued) {
|
|
2386
|
-
const blockedLike = slice.status === "blocked";
|
|
2387
|
-
const fallbackRawError = parsed?.summary ?? slice.lastError ??
|
|
2388
|
-
(blockedLike
|
|
2389
|
-
? "Execution is blocked and needs intervention."
|
|
2390
|
-
: "Agent process exited without a valid output contract.");
|
|
2391
|
-
const fallbackHumanized = humanizeSliceFailure(fallbackRawError);
|
|
2392
2508
|
fallbackDecisionResult = await requestDecisionQueued({
|
|
2393
2509
|
initiativeId: run.initiativeId,
|
|
2394
2510
|
correlationId: slice.runId,
|
|
2395
|
-
title:
|
|
2396
|
-
|
|
2397
|
-
: `${fallbackHumanized.headline}: ${slice.workstreamTitle ?? slice.workstreamId}`,
|
|
2398
|
-
summary: fallbackHumanized.explanation,
|
|
2511
|
+
title: fallbackDecisionTitle,
|
|
2512
|
+
summary: fallbackDecisionSummary,
|
|
2399
2513
|
urgency: "high",
|
|
2400
2514
|
options: [
|
|
2401
2515
|
"Retry this workstream slice",
|
|
@@ -2403,18 +2517,20 @@ export function createAutoContinueEngine(deps) {
|
|
|
2403
2517
|
"Skip this workstream for now",
|
|
2404
2518
|
],
|
|
2405
2519
|
blocking: true,
|
|
2406
|
-
decisionType:
|
|
2520
|
+
decisionType: looksLikeNoOutcome
|
|
2521
|
+
? "autopilot_completed_without_outcome"
|
|
2522
|
+
: blockedLike
|
|
2523
|
+
? "autopilot_blocked_without_decision"
|
|
2524
|
+
: "autopilot_failure",
|
|
2407
2525
|
workstreamId: slice.workstreamId,
|
|
2408
2526
|
agentId: slice.agentId,
|
|
2409
2527
|
sourceSystem: "orgx-autopilot",
|
|
2410
|
-
conflictSource:
|
|
2411
|
-
? "slice_missing_blocking_decision"
|
|
2412
|
-
: "slice_invalid_output",
|
|
2528
|
+
conflictSource: decisionConflictSource,
|
|
2413
2529
|
dedupeKey: [
|
|
2414
2530
|
"autopilot",
|
|
2415
2531
|
run.initiativeId,
|
|
2416
2532
|
slice.workstreamId,
|
|
2417
|
-
|
|
2533
|
+
decisionConflictSource,
|
|
2418
2534
|
].join(":"),
|
|
2419
2535
|
recommendedAction: nextActions[0] ??
|
|
2420
2536
|
"Review the output contract and logs, then retry or pause autopilot until the blocker is resolved.",
|
|
@@ -2422,19 +2538,17 @@ export function createAutoContinueEngine(deps) {
|
|
|
2422
2538
|
sourceRef: {
|
|
2423
2539
|
run_id: slice.runId,
|
|
2424
2540
|
workstream_id: slice.workstreamId,
|
|
2425
|
-
parsed_status:
|
|
2541
|
+
parsed_status: reportedParsedStatus,
|
|
2426
2542
|
},
|
|
2427
2543
|
evidenceRefs: [
|
|
2428
2544
|
{
|
|
2429
2545
|
evidence_type: "slice_output_validation",
|
|
2430
2546
|
title: "Slice output requires fallback decision",
|
|
2431
|
-
summary:
|
|
2432
|
-
slice.lastError ??
|
|
2433
|
-
"Slice did not provide a blocking decision payload.",
|
|
2547
|
+
summary: fallbackDecisionSummary,
|
|
2434
2548
|
source_pointer: slice.outputPath,
|
|
2435
2549
|
payload: {
|
|
2436
2550
|
log_path: slice.logPath,
|
|
2437
|
-
parsed_status:
|
|
2551
|
+
parsed_status: reportedParsedStatus,
|
|
2438
2552
|
},
|
|
2439
2553
|
},
|
|
2440
2554
|
...artifactEvidenceRefs,
|
|
@@ -2443,12 +2557,12 @@ export function createAutoContinueEngine(deps) {
|
|
|
2443
2557
|
}
|
|
2444
2558
|
setLaneState(run, {
|
|
2445
2559
|
workstreamId: slice.workstreamId,
|
|
2446
|
-
state:
|
|
2560
|
+
state: LaneState.BLOCKED,
|
|
2447
2561
|
activeRunId: null,
|
|
2448
2562
|
activeTaskIds: [],
|
|
2449
2563
|
blockedReason: parsed?.summary ??
|
|
2450
2564
|
slice.lastError ??
|
|
2451
|
-
`Slice returned status: ${
|
|
2565
|
+
`Slice returned status: ${reportedParsedStatus}`,
|
|
2452
2566
|
waitingOnWorkstreamIds: [],
|
|
2453
2567
|
retryAt: null,
|
|
2454
2568
|
});
|
|
@@ -2457,10 +2571,8 @@ export function createAutoContinueEngine(deps) {
|
|
|
2457
2571
|
}
|
|
2458
2572
|
await stopAutoContinueRun({
|
|
2459
2573
|
run,
|
|
2460
|
-
reason:
|
|
2461
|
-
error:
|
|
2462
|
-
slice.lastError ??
|
|
2463
|
-
`Slice returned status: ${effectiveParsedStatus}`,
|
|
2574
|
+
reason: blockedLike ? "blocked" : "error",
|
|
2575
|
+
error: fallbackRawError,
|
|
2464
2576
|
decisionRequired: blockingDecisionQueued || fallbackDecisionResult.queued,
|
|
2465
2577
|
decisionIds: Array.from(new Set([...decisionIds, ...fallbackDecisionResult.decisionIds])),
|
|
2466
2578
|
});
|
|
@@ -2533,7 +2645,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2533
2645
|
});
|
|
2534
2646
|
setLaneState(run, {
|
|
2535
2647
|
workstreamId: slice.workstreamId,
|
|
2536
|
-
state:
|
|
2648
|
+
state: LaneState.BLOCKED,
|
|
2537
2649
|
activeRunId: null,
|
|
2538
2650
|
activeTaskIds: [],
|
|
2539
2651
|
blockedReason: slice.lastError ??
|
|
@@ -2567,7 +2679,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2567
2679
|
});
|
|
2568
2680
|
setLaneState(run, {
|
|
2569
2681
|
workstreamId: slice.workstreamId,
|
|
2570
|
-
state:
|
|
2682
|
+
state: LaneState.COMPLETED,
|
|
2571
2683
|
activeRunId: null,
|
|
2572
2684
|
activeTaskIds: [],
|
|
2573
2685
|
blockedReason: null,
|
|
@@ -2645,7 +2757,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2645
2757
|
}
|
|
2646
2758
|
syncLegacyRunPointers(run);
|
|
2647
2759
|
if (run.stopRequested) {
|
|
2648
|
-
run.status =
|
|
2760
|
+
run.status = RunStatus.STOPPING;
|
|
2649
2761
|
run.updatedAt = now;
|
|
2650
2762
|
await stopAutoContinueRun({ run, reason: "stopped" });
|
|
2651
2763
|
return;
|
|
@@ -2697,8 +2809,11 @@ export function createAutoContinueEngine(deps) {
|
|
|
2697
2809
|
};
|
|
2698
2810
|
// Select the next eligible workstream by scanning ordered todos.
|
|
2699
2811
|
let selectedWorkstreamId = null;
|
|
2812
|
+
let selectedQueueRank = 0;
|
|
2700
2813
|
let deferredBySpawnGuardRateLimit = 0;
|
|
2814
|
+
let queueScanIndex = 0;
|
|
2701
2815
|
for (const taskId of graph.recentTodos) {
|
|
2816
|
+
queueScanIndex++;
|
|
2702
2817
|
const node = nodeById.get(taskId);
|
|
2703
2818
|
if (!node || node.type !== "task")
|
|
2704
2819
|
continue;
|
|
@@ -2740,6 +2855,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2740
2855
|
continue;
|
|
2741
2856
|
}
|
|
2742
2857
|
selectedWorkstreamId = node.workstreamId;
|
|
2858
|
+
selectedQueueRank = queueScanIndex + 1;
|
|
2743
2859
|
break;
|
|
2744
2860
|
}
|
|
2745
2861
|
if (!selectedWorkstreamId) {
|
|
@@ -2777,7 +2893,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
2777
2893
|
for (const [workstreamId, waitingOnWorkstreamIds] of waitingByWorkstream.entries()) {
|
|
2778
2894
|
setLaneState(run, {
|
|
2779
2895
|
workstreamId,
|
|
2780
|
-
state:
|
|
2896
|
+
state: LaneState.WAITING_DEPENDENCY,
|
|
2781
2897
|
activeRunId: null,
|
|
2782
2898
|
activeTaskIds: [],
|
|
2783
2899
|
blockedReason: null,
|
|
@@ -3028,7 +3144,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
3028
3144
|
}
|
|
3029
3145
|
setLaneState(run, {
|
|
3030
3146
|
workstreamId: selectedWorkstreamId,
|
|
3031
|
-
state:
|
|
3147
|
+
state: LaneState.BLOCKED,
|
|
3032
3148
|
activeRunId: null,
|
|
3033
3149
|
activeTaskIds: [],
|
|
3034
3150
|
blockedReason,
|
|
@@ -3123,7 +3239,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
3123
3239
|
}
|
|
3124
3240
|
setLaneState(run, {
|
|
3125
3241
|
workstreamId: selectedWorkstreamId,
|
|
3126
|
-
state:
|
|
3242
|
+
state: LaneState.BLOCKED,
|
|
3127
3243
|
activeRunId: null,
|
|
3128
3244
|
activeTaskIds: [],
|
|
3129
3245
|
blockedReason,
|
|
@@ -3188,7 +3304,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
3188
3304
|
});
|
|
3189
3305
|
setLaneState(run, {
|
|
3190
3306
|
workstreamId: selectedWorkstreamId,
|
|
3191
|
-
state:
|
|
3307
|
+
state: LaneState.RATE_LIMITED,
|
|
3192
3308
|
activeRunId: null,
|
|
3193
3309
|
activeTaskIds: [],
|
|
3194
3310
|
blockedReason,
|
|
@@ -3271,7 +3387,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
3271
3387
|
run.updatedAt = now;
|
|
3272
3388
|
setLaneState(run, {
|
|
3273
3389
|
workstreamId: selectedWorkstreamId,
|
|
3274
|
-
state:
|
|
3390
|
+
state: LaneState.IDLE,
|
|
3275
3391
|
activeRunId: null,
|
|
3276
3392
|
activeTaskIds: [],
|
|
3277
3393
|
blockedReason: null,
|
|
@@ -3379,7 +3495,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
3379
3495
|
}
|
|
3380
3496
|
setLaneState(run, {
|
|
3381
3497
|
workstreamId: selectedWorkstreamId,
|
|
3382
|
-
state:
|
|
3498
|
+
state: LaneState.BLOCKED,
|
|
3383
3499
|
activeRunId: null,
|
|
3384
3500
|
activeTaskIds: [],
|
|
3385
3501
|
blockedReason,
|
|
@@ -3566,7 +3682,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
3566
3682
|
behaviorAutomationLevel,
|
|
3567
3683
|
sourceClient: executorSourceClient,
|
|
3568
3684
|
pid: spawned.pid,
|
|
3569
|
-
status:
|
|
3685
|
+
status: RunStatus.RUNNING,
|
|
3570
3686
|
startedAt: now,
|
|
3571
3687
|
finishedAt: null,
|
|
3572
3688
|
updatedAt: now,
|
|
@@ -3618,6 +3734,10 @@ export function createAutoContinueEngine(deps) {
|
|
|
3618
3734
|
scope_milestone_ids: slice.scopeMilestoneIds,
|
|
3619
3735
|
log_path: logPath,
|
|
3620
3736
|
output_path: outputPath,
|
|
3737
|
+
dispatch_queue_rank: selectedQueueRank > 0 ? selectedQueueRank : null,
|
|
3738
|
+
dispatch_workstream_title: workstreamTitle ?? null,
|
|
3739
|
+
dispatch_task_title: primaryTask.title ?? null,
|
|
3740
|
+
dispatch_selection_reason: "top_of_queue",
|
|
3621
3741
|
...mockMeta(slice),
|
|
3622
3742
|
},
|
|
3623
3743
|
});
|
|
@@ -3657,6 +3777,10 @@ export function createAutoContinueEngine(deps) {
|
|
|
3657
3777
|
scope_milestone_ids: slice.scopeMilestoneIds,
|
|
3658
3778
|
log_path: logPath,
|
|
3659
3779
|
output_path: outputPath,
|
|
3780
|
+
dispatch_queue_rank: selectedQueueRank > 0 ? selectedQueueRank : null,
|
|
3781
|
+
dispatch_workstream_title: workstreamTitle ?? null,
|
|
3782
|
+
dispatch_task_title: primaryTask.title ?? null,
|
|
3783
|
+
dispatch_selection_reason: "top_of_queue",
|
|
3660
3784
|
...mockMeta(slice),
|
|
3661
3785
|
},
|
|
3662
3786
|
});
|
|
@@ -3684,7 +3808,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
3684
3808
|
run.activeTaskIds = dedupeStrings([...run.activeTaskIds, ...slice.taskIds]);
|
|
3685
3809
|
setLaneState(run, {
|
|
3686
3810
|
workstreamId: selectedWorkstreamId,
|
|
3687
|
-
state:
|
|
3811
|
+
state: LaneState.RUNNING,
|
|
3688
3812
|
activeRunId: sliceRunId,
|
|
3689
3813
|
activeTaskIds: slice.taskIds,
|
|
3690
3814
|
blockedReason: null,
|
|
@@ -3763,16 +3887,16 @@ export function createAutoContinueEngine(deps) {
|
|
|
3763
3887
|
}
|
|
3764
3888
|
const lane = run.laneByWorkstreamId[workstreamId] ?? null;
|
|
3765
3889
|
if (lane &&
|
|
3766
|
-
(lane.state ===
|
|
3767
|
-
lane.state ===
|
|
3768
|
-
lane.state ===
|
|
3769
|
-
lane.state ===
|
|
3890
|
+
(lane.state === LaneState.RUNNING ||
|
|
3891
|
+
lane.state === LaneState.BLOCKED ||
|
|
3892
|
+
lane.state === LaneState.WAITING_DEPENDENCY ||
|
|
3893
|
+
lane.state === LaneState.RATE_LIMITED)) {
|
|
3770
3894
|
return run;
|
|
3771
3895
|
}
|
|
3772
3896
|
if (Array.isArray(run.allowedWorkstreamIds) &&
|
|
3773
3897
|
run.allowedWorkstreamIds.length > 0 &&
|
|
3774
3898
|
run.allowedWorkstreamIds.includes(workstreamId) &&
|
|
3775
|
-
(run.status ===
|
|
3899
|
+
(run.status === RunStatus.RUNNING || run.status === RunStatus.STOPPING)) {
|
|
3776
3900
|
return run;
|
|
3777
3901
|
}
|
|
3778
3902
|
return null;
|
|
@@ -3856,13 +3980,13 @@ export function createAutoContinueEngine(deps) {
|
|
|
3856
3980
|
const existingRun = autoContinueRuns.get(initiativeId) ?? null;
|
|
3857
3981
|
if (existingRun &&
|
|
3858
3982
|
(existingRun.stopRequested ||
|
|
3859
|
-
existingRun.status ===
|
|
3983
|
+
existingRun.status === RunStatus.STOPPING ||
|
|
3860
3984
|
existingRun.stopReason === "stopped")) {
|
|
3861
3985
|
await emitSkip("paused_by_user");
|
|
3862
3986
|
return;
|
|
3863
3987
|
}
|
|
3864
3988
|
if (existingRun &&
|
|
3865
|
-
(existingRun.status ===
|
|
3989
|
+
(existingRun.status === RunStatus.RUNNING || existingRun.status === RunStatus.STOPPING) &&
|
|
3866
3990
|
listActiveSliceRunIds(existingRun).length > 0) {
|
|
3867
3991
|
const activeRunIds = listActiveSliceRunIds(existingRun);
|
|
3868
3992
|
await emitSkip("already_running", {
|
|
@@ -4075,7 +4199,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
4075
4199
|
async function startAutoContinueRun(input) {
|
|
4076
4200
|
const now = new Date().toISOString();
|
|
4077
4201
|
const existing = autoContinueRuns.get(input.initiativeId) ?? null;
|
|
4078
|
-
const existingIsLive = existing?.status ===
|
|
4202
|
+
const existingIsLive = existing?.status === RunStatus.RUNNING || existing?.status === RunStatus.STOPPING;
|
|
4079
4203
|
const run = existing ??
|
|
4080
4204
|
{
|
|
4081
4205
|
initiativeId: input.initiativeId,
|
|
@@ -4090,7 +4214,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
4090
4214
|
scope: "task",
|
|
4091
4215
|
tokenBudget: defaultAutoContinueTokenBudget(),
|
|
4092
4216
|
tokensUsed: 0,
|
|
4093
|
-
status:
|
|
4217
|
+
status: RunStatus.RUNNING,
|
|
4094
4218
|
stopReason: null,
|
|
4095
4219
|
stopRequested: false,
|
|
4096
4220
|
startedAt: now,
|
|
@@ -4133,7 +4257,7 @@ export function createAutoContinueEngine(deps) {
|
|
|
4133
4257
|
? normalizeTokenBudget(run.tokenBudget, defaultAutoContinueTokenBudget())
|
|
4134
4258
|
: defaultAutoContinueTokenBudget();
|
|
4135
4259
|
}
|
|
4136
|
-
run.status =
|
|
4260
|
+
run.status = RunStatus.RUNNING;
|
|
4137
4261
|
run.stopReason = null;
|
|
4138
4262
|
run.stopRequested = false;
|
|
4139
4263
|
run.stoppedAt = null;
|
|
@@ -4216,8 +4340,8 @@ export function createAutoContinueEngine(deps) {
|
|
|
4216
4340
|
event: "autopilot_transition",
|
|
4217
4341
|
actionType: "run_state_transition",
|
|
4218
4342
|
}),
|
|
4219
|
-
old_state:
|
|
4220
|
-
new_state:
|
|
4343
|
+
old_state: LaneState.IDLE,
|
|
4344
|
+
new_state: LaneState.RUNNING,
|
|
4221
4345
|
reason: "started",
|
|
4222
4346
|
workspace_id: run.allowedWorkstreamIds?.[0] ?? null,
|
|
4223
4347
|
},
|
|
@@ -4229,6 +4353,102 @@ export function createAutoContinueEngine(deps) {
|
|
|
4229
4353
|
}
|
|
4230
4354
|
return run;
|
|
4231
4355
|
}
|
|
4356
|
+
async function skipCurrentWorkstream(initiativeId, workstreamId, reason) {
|
|
4357
|
+
const run = autoContinueRuns.get(initiativeId) ?? null;
|
|
4358
|
+
if (!run) {
|
|
4359
|
+
return { ok: false, skippedWorkstreamId: workstreamId };
|
|
4360
|
+
}
|
|
4361
|
+
ensureRunInternals(run);
|
|
4362
|
+
if (!run.blockedWorkstreamIds.includes(workstreamId)) {
|
|
4363
|
+
run.blockedWorkstreamIds.push(workstreamId);
|
|
4364
|
+
}
|
|
4365
|
+
setLaneState(run, {
|
|
4366
|
+
workstreamId,
|
|
4367
|
+
state: LaneState.BLOCKED,
|
|
4368
|
+
activeRunId: null,
|
|
4369
|
+
activeTaskIds: [],
|
|
4370
|
+
blockedReason: reason ?? "Skipped by user",
|
|
4371
|
+
waitingOnWorkstreamIds: [],
|
|
4372
|
+
retryAt: null,
|
|
4373
|
+
});
|
|
4374
|
+
run.updatedAt = new Date().toISOString();
|
|
4375
|
+
try {
|
|
4376
|
+
await emitActivitySafe({
|
|
4377
|
+
initiativeId,
|
|
4378
|
+
runId: run.lastRunId ?? undefined,
|
|
4379
|
+
correlationId: run.lastRunId ?? undefined,
|
|
4380
|
+
phase: "review",
|
|
4381
|
+
level: "info",
|
|
4382
|
+
message: `Workstream ${workstreamId} skipped${reason ? `: ${reason}` : ""}.`,
|
|
4383
|
+
metadata: {
|
|
4384
|
+
...buildSliceEnrichment({
|
|
4385
|
+
run,
|
|
4386
|
+
workstreamId,
|
|
4387
|
+
event: "autopilot_item_skipped",
|
|
4388
|
+
}),
|
|
4389
|
+
skipped_workstream_id: workstreamId,
|
|
4390
|
+
skip_reason: reason ?? null,
|
|
4391
|
+
},
|
|
4392
|
+
});
|
|
4393
|
+
}
|
|
4394
|
+
catch {
|
|
4395
|
+
// best effort
|
|
4396
|
+
}
|
|
4397
|
+
// Re-enable the run if it was stopped due to the blocked workstream.
|
|
4398
|
+
if (run.status === RunStatus.STOPPED && run.stopReason === "blocked") {
|
|
4399
|
+
run.status = RunStatus.RUNNING;
|
|
4400
|
+
run.stopReason = null;
|
|
4401
|
+
run.stoppedAt = null;
|
|
4402
|
+
run.stopRequested = false;
|
|
4403
|
+
run.lastError = null;
|
|
4404
|
+
}
|
|
4405
|
+
// Trigger the next tick to pick up a different workstream.
|
|
4406
|
+
try {
|
|
4407
|
+
await tickAutoContinueRun(run);
|
|
4408
|
+
}
|
|
4409
|
+
catch {
|
|
4410
|
+
// best effort
|
|
4411
|
+
}
|
|
4412
|
+
// Determine what the next workstream is, if any.
|
|
4413
|
+
const nextLane = Object.values(run.laneByWorkstreamId ?? {}).find((lane) => lane.state === LaneState.RUNNING && lane.workstreamId !== workstreamId) ?? null;
|
|
4414
|
+
return {
|
|
4415
|
+
ok: true,
|
|
4416
|
+
skippedWorkstreamId: workstreamId,
|
|
4417
|
+
nextWorkstreamId: nextLane?.workstreamId ?? undefined,
|
|
4418
|
+
nextWorkstreamTitle: undefined,
|
|
4419
|
+
};
|
|
4420
|
+
}
|
|
4421
|
+
function getCanonicalAutopilotState(initiativeId) {
|
|
4422
|
+
const run = autoContinueRuns.get(initiativeId) ?? null;
|
|
4423
|
+
if (!run)
|
|
4424
|
+
return null;
|
|
4425
|
+
const canonicalState = run.status === RunStatus.RUNNING
|
|
4426
|
+
? "running"
|
|
4427
|
+
: run.status === RunStatus.STOPPING
|
|
4428
|
+
? "stopping"
|
|
4429
|
+
: run.stopReason === "blocked" || run.stopReason === "error"
|
|
4430
|
+
? "blocked"
|
|
4431
|
+
: "idle";
|
|
4432
|
+
const reason = canonicalState === "blocked"
|
|
4433
|
+
? run.lastError ?? run.stopReason ?? null
|
|
4434
|
+
: canonicalState === "stopping"
|
|
4435
|
+
? "stop_requested"
|
|
4436
|
+
: null;
|
|
4437
|
+
// Find the first active slice to identify the current workstream.
|
|
4438
|
+
const activeSliceRunId = (run.activeSliceRunIds ?? [])[0] ?? run.activeRunId ?? null;
|
|
4439
|
+
const activeSlice = activeSliceRunId
|
|
4440
|
+
? autoContinueSliceRuns.get(activeSliceRunId) ?? null
|
|
4441
|
+
: null;
|
|
4442
|
+
return {
|
|
4443
|
+
state: canonicalState,
|
|
4444
|
+
reason,
|
|
4445
|
+
activeRunId: activeSliceRunId,
|
|
4446
|
+
activeWorkstreamId: activeSlice?.workstreamId ?? null,
|
|
4447
|
+
activeWorkstreamTitle: activeSlice?.workstreamTitle ?? null,
|
|
4448
|
+
queueHeadTitle: activeSlice?.workstreamTitle ?? null,
|
|
4449
|
+
lastTransitionAt: run.updatedAt ?? run.startedAt,
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4232
4452
|
return {
|
|
4233
4453
|
autoContinueRuns,
|
|
4234
4454
|
autoContinueSliceRuns,
|
|
@@ -4250,6 +4470,8 @@ export function createAutoContinueEngine(deps) {
|
|
|
4250
4470
|
getAutoContinueLaneForWorkstream,
|
|
4251
4471
|
scheduleAutoFixForWorkstream,
|
|
4252
4472
|
startAutoContinueRun,
|
|
4473
|
+
skipCurrentWorkstream,
|
|
4474
|
+
getCanonicalAutopilotState,
|
|
4253
4475
|
// Session store (for resume support)
|
|
4254
4476
|
workstreamSessionStore,
|
|
4255
4477
|
getWorkstreamSession,
|