@useorgx/openclaw-plugin 0.4.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dashboard/dist/assets/BJgZIVUQ.js +53 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
- package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
- package/dashboard/dist/assets/BXWDRGm-.js +1 -0
- package/dashboard/dist/assets/BXWDRGm-.js.br +0 -0
- package/dashboard/dist/assets/BXWDRGm-.js.gz +0 -0
- package/dashboard/dist/assets/BgOYB78t.js +4 -0
- package/dashboard/dist/assets/BgOYB78t.js.br +0 -0
- package/dashboard/dist/assets/BgOYB78t.js.gz +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
- package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
- package/dashboard/dist/assets/CE38zU4U.js +1 -0
- package/dashboard/dist/assets/CE38zU4U.js.br +0 -0
- package/dashboard/dist/assets/CE38zU4U.js.gz +0 -0
- package/dashboard/dist/assets/CFGKRAzG.js +1 -0
- package/dashboard/dist/assets/CFGKRAzG.js.br +0 -0
- package/dashboard/dist/assets/CFGKRAzG.js.gz +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js +1 -0
- package/dashboard/dist/assets/CGGR2GZh.js.br +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js.gz +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js +1 -0
- package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
- package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js +8 -0
- package/dashboard/dist/assets/CPFiTmlw.js.br +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js.gz +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js +1 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.br +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.gz +0 -0
- package/dashboard/dist/assets/{CpJsfbXo.js → CxQ08qFN.js} +2 -2
- package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
- package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
- package/dashboard/dist/assets/D-bf6hEI.js +213 -0
- package/dashboard/dist/assets/D-bf6hEI.js.br +0 -0
- package/dashboard/dist/assets/D-bf6hEI.js.gz +0 -0
- package/dashboard/dist/assets/DG6y9wJI.js +2 -0
- package/dashboard/dist/assets/DG6y9wJI.js.br +0 -0
- package/dashboard/dist/assets/DG6y9wJI.js.gz +0 -0
- package/dashboard/dist/assets/DNxKz-GV.js +1 -0
- package/dashboard/dist/assets/DNxKz-GV.js.br +0 -0
- package/dashboard/dist/assets/DNxKz-GV.js.gz +0 -0
- package/dashboard/dist/assets/DW_rKUic.js +11 -0
- package/dashboard/dist/assets/DW_rKUic.js.br +0 -0
- package/dashboard/dist/assets/DW_rKUic.js.gz +0 -0
- package/dashboard/dist/assets/DbNoijHm.js +1 -0
- package/dashboard/dist/assets/DbNoijHm.js.br +0 -0
- package/dashboard/dist/assets/DbNoijHm.js.gz +0 -0
- package/dashboard/dist/assets/DjcdE6jC.js +2 -0
- package/dashboard/dist/assets/DjcdE6jC.js.br +0 -0
- package/dashboard/dist/assets/DjcdE6jC.js.gz +0 -0
- package/dashboard/dist/assets/FZYuCDnt.js +1 -0
- package/dashboard/dist/assets/FZYuCDnt.js.br +0 -0
- package/dashboard/dist/assets/FZYuCDnt.js.gz +0 -0
- package/dashboard/dist/assets/PAUiij_z.js +1 -0
- package/dashboard/dist/assets/PAUiij_z.js.br +0 -0
- package/dashboard/dist/assets/PAUiij_z.js.gz +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js +8 -0
- package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
- package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
- package/dashboard/dist/assets/h5biQs2I.css +1 -0
- package/dashboard/dist/assets/h5biQs2I.css.br +0 -0
- package/dashboard/dist/assets/h5biQs2I.css.gz +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js +1 -0
- package/dashboard/dist/assets/ic2FaMnh.js.br +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js.gz +0 -0
- package/dashboard/dist/assets/nByHNHoW.js +1 -0
- package/dashboard/dist/assets/nByHNHoW.js.br +0 -0
- package/dashboard/dist/assets/nByHNHoW.js.gz +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css +1 -0
- package/dashboard/dist/assets/qm8xLgv-.css.br +0 -0
- package/dashboard/dist/assets/qm8xLgv-.css.gz +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js +1 -0
- package/dashboard/dist/assets/tS9mbYZi.js.br +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js.gz +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.br +0 -0
- package/dashboard/dist/brand/anthropic-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openai-mark.svg.br +0 -0
- package/dashboard/dist/brand/openai-mark.svg.gz +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.br +0 -0
- package/dashboard/dist/brand/openclaw-mark.svg.gz +0 -0
- package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
- package/dashboard/dist/index.html +7 -5
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/activity-actor-fields.js +26 -4
- package/dist/activity-store.js +34 -8
- package/dist/agent-context-store.js +79 -17
- package/dist/agent-run-store.js +44 -3
- package/dist/agent-suite.d.ts +9 -0
- package/dist/agent-suite.js +149 -9
- package/dist/artifacts/artifact-domain-schemas.d.ts +66 -0
- package/dist/artifacts/artifact-domain-schemas.js +357 -0
- package/dist/artifacts/register-artifact.d.ts +4 -3
- package/dist/artifacts/register-artifact.js +170 -57
- package/dist/chat-store.d.ts +157 -0
- package/dist/chat-store.js +586 -0
- package/dist/cli/orgx.js +11 -0
- package/dist/contracts/client.d.ts +43 -3
- package/dist/contracts/client.js +159 -30
- package/dist/contracts/retro-schema.d.ts +81 -0
- package/dist/contracts/retro-schema.js +80 -0
- package/dist/contracts/shared-types.d.ts +159 -0
- package/dist/contracts/shared-types.js +177 -1
- package/dist/contracts/skill-pack-schema.d.ts +192 -0
- package/dist/contracts/skill-pack-schema.js +180 -0
- package/dist/contracts/types.d.ts +227 -2
- package/dist/entities/auto-assignment.js +43 -17
- package/dist/event-sanitization.d.ts +11 -0
- package/dist/event-sanitization.js +113 -0
- package/dist/fs-utils.js +13 -1
- package/dist/gateway-watchdog.d.ts +5 -0
- package/dist/gateway-watchdog.js +50 -0
- package/dist/hooks/post-reporting-event.mjs +1 -5
- package/dist/http/helpers/activity-headline.js +13 -132
- package/dist/http/helpers/auto-continue-engine.d.ts +198 -10
- package/dist/http/helpers/auto-continue-engine.js +2531 -186
- package/dist/http/helpers/autopilot-operations.d.ts +19 -0
- package/dist/http/helpers/autopilot-operations.js +182 -31
- package/dist/http/helpers/autopilot-runtime.d.ts +1 -0
- package/dist/http/helpers/autopilot-runtime.js +308 -20
- package/dist/http/helpers/autopilot-slice-utils.d.ts +18 -0
- package/dist/http/helpers/autopilot-slice-utils.js +516 -93
- package/dist/http/helpers/decision-mapper.d.ts +40 -0
- package/dist/http/helpers/decision-mapper.js +223 -7
- package/dist/http/helpers/dispatch-lifecycle.d.ts +19 -2
- package/dist/http/helpers/dispatch-lifecycle.js +242 -37
- package/dist/http/helpers/kickoff-context.js +74 -0
- package/dist/http/helpers/llm-client.d.ts +47 -0
- package/dist/http/helpers/llm-client.js +256 -0
- package/dist/http/helpers/mission-control.d.ts +102 -3
- package/dist/http/helpers/mission-control.js +498 -9
- package/dist/http/helpers/sentinel-catalog.d.ts +23 -0
- package/dist/http/helpers/sentinel-catalog.js +193 -0
- package/dist/http/helpers/session-classification.d.ts +9 -0
- package/dist/http/helpers/session-classification.js +564 -0
- package/dist/http/helpers/slice-experience-v2.d.ts +137 -0
- package/dist/http/helpers/slice-experience-v2.js +677 -0
- package/dist/http/helpers/slice-run-projections.d.ts +72 -0
- package/dist/http/helpers/slice-run-projections.js +860 -0
- package/dist/http/helpers/triage-mapper.d.ts +43 -0
- package/dist/http/helpers/triage-mapper.js +549 -0
- package/dist/http/helpers/value-utils.js +7 -2
- package/dist/http/helpers/workspace-scope.d.ts +15 -0
- package/dist/http/helpers/workspace-scope.js +170 -0
- package/dist/http/index.js +1354 -97
- package/dist/http/routes/agent-suite.d.ts +9 -0
- package/dist/http/routes/agent-suite.js +207 -8
- package/dist/http/routes/agents-catalog.js +64 -19
- package/dist/http/routes/chat.d.ts +19 -0
- package/dist/http/routes/chat.js +522 -0
- package/dist/http/routes/decision-actions.d.ts +8 -1
- package/dist/http/routes/decision-actions.js +42 -5
- package/dist/http/routes/dispatch-gateway-envelope.d.ts +25 -0
- package/dist/http/routes/dispatch-gateway-envelope.js +26 -0
- package/dist/http/routes/entities.d.ts +16 -0
- package/dist/http/routes/entities.js +294 -6
- package/dist/http/routes/live-legacy.d.ts +5 -0
- package/dist/http/routes/live-legacy.js +23 -509
- package/dist/http/routes/live-misc.d.ts +12 -0
- package/dist/http/routes/live-misc.js +251 -31
- package/dist/http/routes/live-snapshot.d.ts +48 -2
- package/dist/http/routes/live-snapshot.js +638 -19
- package/dist/http/routes/live-terminal.d.ts +11 -0
- package/dist/http/routes/live-terminal.js +261 -0
- package/dist/http/routes/live-triage.d.ts +61 -0
- package/dist/http/routes/live-triage.js +248 -0
- package/dist/http/routes/mission-control-actions.d.ts +49 -1
- package/dist/http/routes/mission-control-actions.js +1334 -84
- package/dist/http/routes/mission-control-read.d.ts +48 -3
- package/dist/http/routes/mission-control-read.js +1593 -20
- package/dist/http/routes/realtime-orchestrator.d.ts +10 -0
- package/dist/http/routes/realtime-orchestrator.js +74 -0
- package/dist/http/routes/run-control.d.ts +5 -2
- package/dist/http/routes/run-control.js +10 -0
- package/dist/http/routes/sentinels-catalog.d.ts +7 -0
- package/dist/http/routes/sentinels-catalog.js +24 -0
- package/dist/http/routes/summary.js +10 -3
- package/dist/http/routes/usage.d.ts +24 -0
- package/dist/http/routes/usage.js +362 -0
- package/dist/http/routes/work-artifacts.js +28 -9
- package/dist/index.js +165 -27
- package/dist/local-openclaw.js +29 -6
- package/dist/mcp-client-setup.js +3 -3
- package/dist/mcp-http-handler.js +33 -59
- package/dist/next-up-queue-store.d.ts +16 -1
- package/dist/next-up-queue-store.js +89 -7
- package/dist/outbox.d.ts +5 -0
- package/dist/outbox.js +113 -9
- package/dist/paths.js +24 -5
- package/dist/reporting/rollups.d.ts +53 -0
- package/dist/reporting/rollups.js +148 -0
- package/dist/retro/domain-templates.d.ts +45 -0
- package/dist/retro/domain-templates.js +297 -0
- package/dist/retro/quality-rubric.d.ts +33 -0
- package/dist/retro/quality-rubric.js +213 -0
- package/dist/runtime-cleanup.d.ts +18 -0
- package/dist/runtime-cleanup.js +87 -0
- package/dist/services/background.d.ts +11 -0
- package/dist/services/background.js +22 -0
- package/dist/services/experiment-randomization.d.ts +21 -0
- package/dist/services/experiment-randomization.js +63 -0
- package/dist/skill-pack-state.d.ts +36 -5
- package/dist/skill-pack-state.js +273 -29
- package/dist/sync/local-agent-telemetry.d.ts +13 -0
- package/dist/sync/local-agent-telemetry.js +128 -0
- package/dist/sync/outbox-replay.js +131 -24
- package/dist/team-context-store.d.ts +23 -0
- package/dist/team-context-store.js +116 -0
- package/dist/telemetry/posthog.js +4 -2
- package/dist/tools/core-tools.d.ts +10 -14
- package/dist/tools/core-tools.js +1289 -24
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -0
- package/dist/worker-supervisor.js +23 -0
- package/package.json +14 -4
- package/dashboard/dist/assets/B3ziCA02.js +0 -8
- package/dashboard/dist/assets/B5NEElEI.css +0 -1
- package/dashboard/dist/assets/BhapSNAs.js +0 -215
- package/dashboard/dist/assets/iFdvE7lx.js +0 -1
- package/dashboard/dist/assets/jRJsmpYM.js +0 -1
- package/dashboard/dist/assets/sAhvFnpk.js +0 -4
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
const TERMINAL_STATES = new Set([
|
|
2
|
+
"completed",
|
|
3
|
+
"needs_review",
|
|
4
|
+
"failed",
|
|
5
|
+
"archived",
|
|
6
|
+
]);
|
|
7
|
+
const RUN_LIKE_STATUS = new Set(["dispatching", "running"]);
|
|
8
|
+
function asRecord(value) {
|
|
9
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
10
|
+
return null;
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function normalizeText(value) {
|
|
14
|
+
if (typeof value !== "string")
|
|
15
|
+
return null;
|
|
16
|
+
const trimmed = value.trim();
|
|
17
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
18
|
+
}
|
|
19
|
+
function toIso(value) {
|
|
20
|
+
if (!value)
|
|
21
|
+
return null;
|
|
22
|
+
const epoch = Date.parse(value);
|
|
23
|
+
if (!Number.isFinite(epoch))
|
|
24
|
+
return null;
|
|
25
|
+
return new Date(epoch).toISOString();
|
|
26
|
+
}
|
|
27
|
+
function toEpoch(value) {
|
|
28
|
+
if (!value)
|
|
29
|
+
return 0;
|
|
30
|
+
const parsed = Date.parse(value);
|
|
31
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
32
|
+
}
|
|
33
|
+
function metadataString(metadata, keys) {
|
|
34
|
+
if (!metadata)
|
|
35
|
+
return null;
|
|
36
|
+
for (const key of keys) {
|
|
37
|
+
const value = normalizeText(metadata[key]);
|
|
38
|
+
if (value)
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function metadataNumber(metadata, keys) {
|
|
44
|
+
if (!metadata)
|
|
45
|
+
return null;
|
|
46
|
+
for (const key of keys) {
|
|
47
|
+
const raw = metadata[key];
|
|
48
|
+
if (typeof raw === "number" && Number.isFinite(raw))
|
|
49
|
+
return raw;
|
|
50
|
+
if (typeof raw === "string") {
|
|
51
|
+
const parsed = Number(raw);
|
|
52
|
+
if (Number.isFinite(parsed))
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function metadataBoolean(metadata, keys) {
|
|
59
|
+
if (!metadata)
|
|
60
|
+
return null;
|
|
61
|
+
for (const key of keys) {
|
|
62
|
+
const raw = metadata[key];
|
|
63
|
+
if (typeof raw === "boolean")
|
|
64
|
+
return raw;
|
|
65
|
+
if (typeof raw === "string") {
|
|
66
|
+
const normalized = raw.trim().toLowerCase();
|
|
67
|
+
if (normalized === "true")
|
|
68
|
+
return true;
|
|
69
|
+
if (normalized === "false")
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
function metadataStringArray(metadata, keys) {
|
|
76
|
+
if (!metadata)
|
|
77
|
+
return [];
|
|
78
|
+
for (const key of keys) {
|
|
79
|
+
const raw = metadata[key];
|
|
80
|
+
if (Array.isArray(raw)) {
|
|
81
|
+
return dedupeStrings(raw
|
|
82
|
+
.map((entry) => normalizeText(entry))
|
|
83
|
+
.filter((entry) => Boolean(entry)));
|
|
84
|
+
}
|
|
85
|
+
if (typeof raw === "string") {
|
|
86
|
+
return dedupeStrings(raw
|
|
87
|
+
.split(/[\n,;]+/g)
|
|
88
|
+
.map((entry) => entry.trim())
|
|
89
|
+
.filter(Boolean));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
function dedupeStrings(values) {
|
|
95
|
+
const seen = new Set();
|
|
96
|
+
const output = [];
|
|
97
|
+
for (const value of values) {
|
|
98
|
+
const normalized = value.trim();
|
|
99
|
+
if (!normalized)
|
|
100
|
+
continue;
|
|
101
|
+
const key = normalized.toLowerCase();
|
|
102
|
+
if (seen.has(key))
|
|
103
|
+
continue;
|
|
104
|
+
seen.add(key);
|
|
105
|
+
output.push(normalized);
|
|
106
|
+
}
|
|
107
|
+
return output;
|
|
108
|
+
}
|
|
109
|
+
function mergeScopedIds(existing, scalar, additions) {
|
|
110
|
+
const merged = dedupeStrings([
|
|
111
|
+
...(Array.isArray(existing) ? existing : []),
|
|
112
|
+
...(scalar ? [scalar] : []),
|
|
113
|
+
...additions,
|
|
114
|
+
]);
|
|
115
|
+
return merged;
|
|
116
|
+
}
|
|
117
|
+
function resolveEventName(metadata) {
|
|
118
|
+
const fromMeta = metadataString(metadata, ["event"]);
|
|
119
|
+
if (fromMeta)
|
|
120
|
+
return fromMeta.toLowerCase();
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
function resolveSliceRunId(item, metadata) {
|
|
124
|
+
const direct = metadataString(metadata, [
|
|
125
|
+
"slice_run_id",
|
|
126
|
+
"sliceRunId",
|
|
127
|
+
"active_run_id",
|
|
128
|
+
"activeRunId",
|
|
129
|
+
"run_id",
|
|
130
|
+
"runId",
|
|
131
|
+
"correlation_id",
|
|
132
|
+
"correlationId",
|
|
133
|
+
]);
|
|
134
|
+
if (direct)
|
|
135
|
+
return direct;
|
|
136
|
+
if (item.runId && item.runId.trim().length > 0)
|
|
137
|
+
return item.runId.trim();
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
function resolveCorrelationId(item, metadata) {
|
|
141
|
+
return (metadataString(metadata, ["correlation_id", "correlationId"]) ??
|
|
142
|
+
metadataString(metadata, ["run_id", "runId"]) ??
|
|
143
|
+
item.runId ??
|
|
144
|
+
null);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Determines whether an activity item is relevant to slice run projections.
|
|
148
|
+
* Every LiveActivityType variant MUST be handled explicitly to prevent
|
|
149
|
+
* silent gaps (like the run_completed bug where accepts were ignored).
|
|
150
|
+
*/
|
|
151
|
+
function resolveRelevantActivity(item, event, metadata, knownSliceIds) {
|
|
152
|
+
// Event-name based relevance (metadata.event prefix matches)
|
|
153
|
+
if (event.startsWith("autopilot_slice_"))
|
|
154
|
+
return true;
|
|
155
|
+
if (event.startsWith("auto_continue_spawn_guard_"))
|
|
156
|
+
return true;
|
|
157
|
+
if (event === "next_up_manual_dispatch_started")
|
|
158
|
+
return true;
|
|
159
|
+
if (event === "auto_continue_stopped")
|
|
160
|
+
return true;
|
|
161
|
+
// Type-based relevance — exhaustive over LiveActivityType
|
|
162
|
+
const type = item.type;
|
|
163
|
+
switch (type) {
|
|
164
|
+
case "artifact_created": {
|
|
165
|
+
const source = metadataString(metadata, ["source"]);
|
|
166
|
+
if ((source ?? "").toLowerCase() === "autopilot_slice")
|
|
167
|
+
return true;
|
|
168
|
+
const runId = resolveSliceRunId(item, metadata);
|
|
169
|
+
return Boolean(runId && knownSliceIds.has(runId));
|
|
170
|
+
}
|
|
171
|
+
case "decision_requested":
|
|
172
|
+
case "decision_resolved":
|
|
173
|
+
case "run_completed":
|
|
174
|
+
case "run_failed":
|
|
175
|
+
case "run_started":
|
|
176
|
+
case "delegation":
|
|
177
|
+
case "milestone_completed":
|
|
178
|
+
case "blocker_created": {
|
|
179
|
+
const runId = resolveSliceRunId(item, metadata);
|
|
180
|
+
return Boolean(runId && knownSliceIds.has(runId));
|
|
181
|
+
}
|
|
182
|
+
case "handoff_requested":
|
|
183
|
+
case "handoff_claimed":
|
|
184
|
+
case "handoff_fulfilled":
|
|
185
|
+
// Handoff types are not relevant to slice projections today
|
|
186
|
+
return false;
|
|
187
|
+
default: {
|
|
188
|
+
// Exhaustiveness guard: if a new LiveActivityType is added and not
|
|
189
|
+
// handled above, this will be a compile error.
|
|
190
|
+
const _exhaustive = type;
|
|
191
|
+
void _exhaustive;
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function defaultExplainer(status) {
|
|
197
|
+
switch (status) {
|
|
198
|
+
case "queued":
|
|
199
|
+
return "Queued and waiting to dispatch.";
|
|
200
|
+
case "dispatching":
|
|
201
|
+
return "Dispatch acknowledged. Waiting for runtime start.";
|
|
202
|
+
case "running":
|
|
203
|
+
return "Work is actively executing.";
|
|
204
|
+
case "awaiting_input":
|
|
205
|
+
return "Needs your input to proceed.";
|
|
206
|
+
case "completed":
|
|
207
|
+
return "Completed with verified output artifacts.";
|
|
208
|
+
case "needs_review":
|
|
209
|
+
return "Completed signal received without verifiable output.";
|
|
210
|
+
case "failed":
|
|
211
|
+
return "Execution failed before producing a valid result.";
|
|
212
|
+
case "archived":
|
|
213
|
+
return "Archived due to missing execution evidence.";
|
|
214
|
+
default:
|
|
215
|
+
return "Status unavailable.";
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function defaultPrimaryAction(status) {
|
|
219
|
+
switch (status) {
|
|
220
|
+
case "completed":
|
|
221
|
+
return "open_artifact";
|
|
222
|
+
case "awaiting_input":
|
|
223
|
+
return "resolve_decision";
|
|
224
|
+
case "needs_review":
|
|
225
|
+
return "review_output";
|
|
226
|
+
case "failed":
|
|
227
|
+
return "retry_slice";
|
|
228
|
+
default:
|
|
229
|
+
return "none";
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function createProjection(sliceRunId) {
|
|
233
|
+
return {
|
|
234
|
+
id: sliceRunId,
|
|
235
|
+
sliceRunId,
|
|
236
|
+
runId: sliceRunId,
|
|
237
|
+
initiativeId: null,
|
|
238
|
+
initiativeIds: [],
|
|
239
|
+
workstreamId: null,
|
|
240
|
+
workstreamIds: [],
|
|
241
|
+
iwmtId: null,
|
|
242
|
+
iwmtIds: [],
|
|
243
|
+
workstreamTitle: null,
|
|
244
|
+
taskIds: [],
|
|
245
|
+
milestoneIds: [],
|
|
246
|
+
status: "queued",
|
|
247
|
+
statusExplainer: defaultExplainer("queued"),
|
|
248
|
+
primaryAction: defaultPrimaryAction("queued"),
|
|
249
|
+
hasArtifact: false,
|
|
250
|
+
artifactCount: 0,
|
|
251
|
+
artifacts: [],
|
|
252
|
+
decisionCount: 0,
|
|
253
|
+
blockingDecisionCount: 0,
|
|
254
|
+
decisionOptions: [],
|
|
255
|
+
sourceClient: null,
|
|
256
|
+
runtimeState: null,
|
|
257
|
+
startedAt: null,
|
|
258
|
+
updatedAt: null,
|
|
259
|
+
completedAt: null,
|
|
260
|
+
failedAt: null,
|
|
261
|
+
archivedAt: null,
|
|
262
|
+
lastEventAt: null,
|
|
263
|
+
lastEventSummary: null,
|
|
264
|
+
correlationId: null,
|
|
265
|
+
confidence: "low",
|
|
266
|
+
_terminal: false,
|
|
267
|
+
_statusUpdatedEpoch: 0,
|
|
268
|
+
_updatedEpoch: 0,
|
|
269
|
+
_artifactIds: new Set(),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function upsertProjection(map, sliceRunId) {
|
|
273
|
+
const existing = map.get(sliceRunId);
|
|
274
|
+
if (existing)
|
|
275
|
+
return existing;
|
|
276
|
+
const created = createProjection(sliceRunId);
|
|
277
|
+
map.set(sliceRunId, created);
|
|
278
|
+
return created;
|
|
279
|
+
}
|
|
280
|
+
function setStatus(input) {
|
|
281
|
+
const atEpoch = toEpoch(input.atIso);
|
|
282
|
+
const nextTerminal = TERMINAL_STATES.has(input.status);
|
|
283
|
+
const currentTerminal = input.projection._terminal;
|
|
284
|
+
if (currentTerminal && !nextTerminal && !input.force) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (!input.force && atEpoch > 0 && atEpoch < input.projection._statusUpdatedEpoch) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
input.projection.status = input.status;
|
|
291
|
+
input.projection._statusUpdatedEpoch = atEpoch;
|
|
292
|
+
input.projection._terminal = nextTerminal;
|
|
293
|
+
if (input.explainer && input.explainer.trim().length > 0) {
|
|
294
|
+
input.projection.statusExplainer = input.explainer.trim();
|
|
295
|
+
}
|
|
296
|
+
else if (!input.projection.statusExplainer || input.projection.statusExplainer.trim().length === 0) {
|
|
297
|
+
input.projection.statusExplainer = defaultExplainer(input.status);
|
|
298
|
+
}
|
|
299
|
+
input.projection.primaryAction = defaultPrimaryAction(input.status);
|
|
300
|
+
if (input.status === "completed") {
|
|
301
|
+
input.projection.completedAt = input.atIso ?? input.projection.completedAt;
|
|
302
|
+
input.projection.failedAt = null;
|
|
303
|
+
input.projection.archivedAt = null;
|
|
304
|
+
}
|
|
305
|
+
if (input.status === "failed") {
|
|
306
|
+
input.projection.failedAt = input.atIso ?? input.projection.failedAt;
|
|
307
|
+
}
|
|
308
|
+
if (input.status === "archived") {
|
|
309
|
+
input.projection.archivedAt = input.atIso ?? input.projection.archivedAt;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function updateProjectionContext(projection, item, metadata) {
|
|
313
|
+
const initiativeIdFromMetadata = metadataString(metadata, [
|
|
314
|
+
"initiative_id",
|
|
315
|
+
"initiativeId",
|
|
316
|
+
]);
|
|
317
|
+
const initiativeIds = mergeScopedIds(projection.initiativeIds, projection.initiativeId, [
|
|
318
|
+
...(item.initiativeId ? [item.initiativeId] : []),
|
|
319
|
+
...metadataStringArray(metadata, ["initiative_ids", "initiativeIds"]),
|
|
320
|
+
...(initiativeIdFromMetadata ? [initiativeIdFromMetadata] : []),
|
|
321
|
+
].filter((entry) => typeof entry === "string" && entry.trim().length > 0));
|
|
322
|
+
projection.initiativeIds = initiativeIds;
|
|
323
|
+
projection.initiativeId = projection.initiativeId ?? initiativeIds[0] ?? null;
|
|
324
|
+
const workstreamIdFromMetadata = metadataString(metadata, [
|
|
325
|
+
"workstream_id",
|
|
326
|
+
"workstreamId",
|
|
327
|
+
]);
|
|
328
|
+
const workstreamIds = mergeScopedIds(projection.workstreamIds, projection.workstreamId, [
|
|
329
|
+
...metadataStringArray(metadata, ["workstream_ids", "workstreamIds"]),
|
|
330
|
+
...(workstreamIdFromMetadata ? [workstreamIdFromMetadata] : []),
|
|
331
|
+
].filter((entry) => typeof entry === "string" && entry.trim().length > 0));
|
|
332
|
+
projection.workstreamIds = workstreamIds;
|
|
333
|
+
projection.workstreamId = projection.workstreamId ?? workstreamIds[0] ?? null;
|
|
334
|
+
const iwmtIdFromMetadata = metadataString(metadata, ["iwmt_id", "iwmtId"]);
|
|
335
|
+
const iwmtIds = mergeScopedIds(projection.iwmtIds, projection.iwmtId, [
|
|
336
|
+
...metadataStringArray(metadata, ["iwmt_ids", "iwmtIds"]),
|
|
337
|
+
...(iwmtIdFromMetadata ? [iwmtIdFromMetadata] : []),
|
|
338
|
+
].filter((entry) => typeof entry === "string" && entry.trim().length > 0));
|
|
339
|
+
projection.iwmtIds = iwmtIds;
|
|
340
|
+
projection.iwmtId = projection.iwmtId ?? iwmtIds[0] ?? null;
|
|
341
|
+
projection.workstreamTitle =
|
|
342
|
+
projection.workstreamTitle ??
|
|
343
|
+
metadataString(metadata, ["workstream_title", "workstreamTitle"]);
|
|
344
|
+
const taskIds = metadataStringArray(metadata, ["task_ids", "taskIds", "active_task_ids", "activeTaskIds"]);
|
|
345
|
+
if (taskIds.length > 0) {
|
|
346
|
+
projection.taskIds = dedupeStrings([...projection.taskIds, ...taskIds]);
|
|
347
|
+
}
|
|
348
|
+
const milestoneIds = metadataStringArray(metadata, ["milestone_ids", "milestoneIds"]);
|
|
349
|
+
if (milestoneIds.length > 0) {
|
|
350
|
+
projection.milestoneIds = dedupeStrings([...projection.milestoneIds, ...milestoneIds]);
|
|
351
|
+
}
|
|
352
|
+
projection.correlationId = projection.correlationId ?? resolveCorrelationId(item, metadata);
|
|
353
|
+
projection.sourceClient =
|
|
354
|
+
projection.sourceClient ??
|
|
355
|
+
metadataString(metadata, ["source_client", "sourceClient", "runtime_source"]);
|
|
356
|
+
const eventAt = toIso(item.timestamp);
|
|
357
|
+
const eventEpoch = toEpoch(eventAt);
|
|
358
|
+
if (eventEpoch >= projection._updatedEpoch) {
|
|
359
|
+
projection._updatedEpoch = eventEpoch;
|
|
360
|
+
projection.updatedAt = eventAt;
|
|
361
|
+
projection.lastEventAt = eventAt;
|
|
362
|
+
projection.lastEventSummary =
|
|
363
|
+
(item.summary && item.summary.trim().length > 0
|
|
364
|
+
? item.summary.trim()
|
|
365
|
+
: item.description && item.description.trim().length > 0
|
|
366
|
+
? item.description.trim()
|
|
367
|
+
: item.title) ?? projection.lastEventSummary;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function maybeAddArtifact(projection, item, metadata) {
|
|
371
|
+
const artifactId = metadataString(metadata, ["artifact_id", "artifactId"]);
|
|
372
|
+
const dedupeKey = artifactId ?? `${item.id}:artifact`;
|
|
373
|
+
if (projection._artifactIds.has(dedupeKey))
|
|
374
|
+
return;
|
|
375
|
+
projection._artifactIds.add(dedupeKey);
|
|
376
|
+
const artifact = {
|
|
377
|
+
id: artifactId,
|
|
378
|
+
type: metadataString(metadata, ["artifact_type", "artifactType"]),
|
|
379
|
+
title: (item.title ?? "Artifact").trim() || "Artifact",
|
|
380
|
+
url: metadataString(metadata, ["url", "artifact_url", "artifactUrl"]),
|
|
381
|
+
createdAt: toIso(item.timestamp),
|
|
382
|
+
};
|
|
383
|
+
projection.artifacts = projection.artifacts.concat(artifact).slice(-20);
|
|
384
|
+
projection.artifactCount = projection._artifactIds.size;
|
|
385
|
+
projection.hasArtifact = projection.artifactCount > 0;
|
|
386
|
+
if (projection.status === "needs_review") {
|
|
387
|
+
setStatus({
|
|
388
|
+
projection,
|
|
389
|
+
status: "completed",
|
|
390
|
+
atIso: artifact.createdAt,
|
|
391
|
+
explainer: "Artifact evidence was recorded after completion.",
|
|
392
|
+
force: true,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function mergeDecisionOptions(projection, optionsRaw) {
|
|
397
|
+
if (!Array.isArray(optionsRaw))
|
|
398
|
+
return;
|
|
399
|
+
const next = [];
|
|
400
|
+
for (const option of optionsRaw) {
|
|
401
|
+
const record = asRecord(option);
|
|
402
|
+
if (!record)
|
|
403
|
+
continue;
|
|
404
|
+
const id = normalizeText(record.id) ?? normalizeText(record.option_id) ?? null;
|
|
405
|
+
const label = normalizeText(record.label);
|
|
406
|
+
if (!id || !label)
|
|
407
|
+
continue;
|
|
408
|
+
next.push({
|
|
409
|
+
id,
|
|
410
|
+
label,
|
|
411
|
+
description: normalizeText(record.description),
|
|
412
|
+
impliedStatus: normalizeText(record.impliedStatus ?? record.implied_status),
|
|
413
|
+
requiresNote: metadataBoolean(record, ["requiresNote", "requires_note"]) ?? false,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
if (next.length === 0)
|
|
417
|
+
return;
|
|
418
|
+
const merged = new Map();
|
|
419
|
+
for (const option of projection.decisionOptions)
|
|
420
|
+
merged.set(option.id, option);
|
|
421
|
+
for (const option of next)
|
|
422
|
+
merged.set(option.id, option);
|
|
423
|
+
projection.decisionOptions = Array.from(merged.values()).slice(0, 8);
|
|
424
|
+
}
|
|
425
|
+
function applySessionFallback(projection, session) {
|
|
426
|
+
const status = (session.status ?? "").trim().toLowerCase();
|
|
427
|
+
const updatedAt = toIso(session.updatedAt ?? session.lastEventAt ?? session.startedAt);
|
|
428
|
+
projection.initiativeIds = mergeScopedIds(projection.initiativeIds, projection.initiativeId, session.initiativeId ? [session.initiativeId] : []);
|
|
429
|
+
projection.workstreamIds = mergeScopedIds(projection.workstreamIds, projection.workstreamId, session.workstreamId ? [session.workstreamId] : []);
|
|
430
|
+
projection.initiativeId = projection.initiativeId ?? projection.initiativeIds[0] ?? null;
|
|
431
|
+
projection.workstreamId = projection.workstreamId ?? projection.workstreamIds[0] ?? null;
|
|
432
|
+
if (RUN_LIKE_STATUS.has(projection.status)) {
|
|
433
|
+
if (!projection.workstreamId && session.workstreamId)
|
|
434
|
+
projection.workstreamId = session.workstreamId;
|
|
435
|
+
if (!projection.workstreamTitle && session.title)
|
|
436
|
+
projection.workstreamTitle = session.title;
|
|
437
|
+
if (!projection.initiativeId && session.initiativeId)
|
|
438
|
+
projection.initiativeId = session.initiativeId;
|
|
439
|
+
}
|
|
440
|
+
if (status === "failed") {
|
|
441
|
+
setStatus({
|
|
442
|
+
projection,
|
|
443
|
+
status: "failed",
|
|
444
|
+
atIso: updatedAt,
|
|
445
|
+
explainer: session.lastEventSummary ?? null,
|
|
446
|
+
force: true,
|
|
447
|
+
});
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (status === "blocked") {
|
|
451
|
+
setStatus({
|
|
452
|
+
projection,
|
|
453
|
+
status: "awaiting_input",
|
|
454
|
+
atIso: updatedAt,
|
|
455
|
+
explainer: session.blockerReason ?? session.lastEventSummary ?? null,
|
|
456
|
+
force: projection.status !== "completed",
|
|
457
|
+
});
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (status === "completed" && projection.status !== "completed" && projection.hasArtifact) {
|
|
461
|
+
setStatus({
|
|
462
|
+
projection,
|
|
463
|
+
status: "completed",
|
|
464
|
+
atIso: updatedAt,
|
|
465
|
+
explainer: session.lastEventSummary ?? null,
|
|
466
|
+
force: true,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
export function buildSliceRunProjections(input) {
|
|
471
|
+
const projections = new Map();
|
|
472
|
+
const knownSliceIds = new Set();
|
|
473
|
+
for (const item of input.activity) {
|
|
474
|
+
const metadata = asRecord(item.metadata);
|
|
475
|
+
const event = resolveEventName(metadata);
|
|
476
|
+
const sliceRunId = resolveSliceRunId(item, metadata);
|
|
477
|
+
if (!sliceRunId)
|
|
478
|
+
continue;
|
|
479
|
+
if (event.startsWith("autopilot_slice_") || event.startsWith("auto_continue_spawn_guard_") || event === "next_up_manual_dispatch_started") {
|
|
480
|
+
knownSliceIds.add(sliceRunId);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const ordered = [...input.activity].sort((a, b) => toEpoch(a.timestamp) - toEpoch(b.timestamp));
|
|
484
|
+
for (const item of ordered) {
|
|
485
|
+
const metadata = asRecord(item.metadata);
|
|
486
|
+
const event = resolveEventName(metadata);
|
|
487
|
+
const sliceRunId = resolveSliceRunId(item, metadata);
|
|
488
|
+
if (!sliceRunId)
|
|
489
|
+
continue;
|
|
490
|
+
if (!resolveRelevantActivity(item, event, metadata, knownSliceIds)) {
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
const projection = upsertProjection(projections, sliceRunId);
|
|
494
|
+
updateProjectionContext(projection, item, metadata);
|
|
495
|
+
const atIso = toIso(item.timestamp);
|
|
496
|
+
if (item.type === "artifact_created") {
|
|
497
|
+
maybeAddArtifact(projection, item, metadata);
|
|
498
|
+
if (projection.status !== "failed" && projection.status !== "archived") {
|
|
499
|
+
setStatus({
|
|
500
|
+
projection,
|
|
501
|
+
status: "completed",
|
|
502
|
+
atIso,
|
|
503
|
+
explainer: "Artifact generated and attached.",
|
|
504
|
+
force: projection.status !== "completed",
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (event === "autopilot_slice_dispatched" || event === "next_up_manual_dispatch_started") {
|
|
510
|
+
setStatus({ projection, status: "dispatching", atIso, explainer: item.summary ?? null });
|
|
511
|
+
projection.startedAt = projection.startedAt ?? atIso;
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (event === "autopilot_slice_started" || event === "autopilot_slice_heartbeat") {
|
|
515
|
+
setStatus({ projection, status: "running", atIso, explainer: item.summary ?? null });
|
|
516
|
+
projection.startedAt = projection.startedAt ?? atIso;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (event === "autopilot_slice_mcp_handshake_failed" ||
|
|
520
|
+
event === "autopilot_slice_timeout" ||
|
|
521
|
+
event === "autopilot_slice_log_stall" ||
|
|
522
|
+
event === "auto_continue_spawn_guard_blocked") {
|
|
523
|
+
setStatus({
|
|
524
|
+
projection,
|
|
525
|
+
status: "awaiting_input",
|
|
526
|
+
atIso,
|
|
527
|
+
explainer: item.summary ?? item.description ?? "Needs intervention to continue.",
|
|
528
|
+
force: true,
|
|
529
|
+
});
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
if (event === "autopilot_slice_finished" || event === "autopilot_slice_result") {
|
|
533
|
+
const parsedStatus = (metadataString(metadata, ["parsed_status", "status"]) ?? "").toLowerCase();
|
|
534
|
+
const reportedArtifacts = Math.max(0, Math.floor(metadataNumber(metadata, ["artifacts"]) ?? 0));
|
|
535
|
+
projection.decisionCount = Math.max(projection.decisionCount, Math.max(0, Math.floor(metadataNumber(metadata, ["decisions"]) ?? 0)));
|
|
536
|
+
projection.blockingDecisionCount = Math.max(projection.blockingDecisionCount, Math.max(0, Math.floor(metadataNumber(metadata, ["blocking_decisions"]) ?? 0)));
|
|
537
|
+
if (parsedStatus === "error") {
|
|
538
|
+
setStatus({
|
|
539
|
+
projection,
|
|
540
|
+
status: "failed",
|
|
541
|
+
atIso,
|
|
542
|
+
explainer: item.summary ?? item.description ?? item.title,
|
|
543
|
+
force: true,
|
|
544
|
+
});
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
if (parsedStatus === "blocked" || parsedStatus === "needs_decision") {
|
|
548
|
+
setStatus({
|
|
549
|
+
projection,
|
|
550
|
+
status: "awaiting_input",
|
|
551
|
+
atIso,
|
|
552
|
+
explainer: item.summary ?? item.description ?? "Needs decision before it can continue.",
|
|
553
|
+
force: true,
|
|
554
|
+
});
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const effectiveArtifactCount = Math.max(projection.artifactCount, reportedArtifacts);
|
|
558
|
+
if (effectiveArtifactCount > 0) {
|
|
559
|
+
projection.artifactCount = effectiveArtifactCount;
|
|
560
|
+
projection.hasArtifact = true;
|
|
561
|
+
setStatus({
|
|
562
|
+
projection,
|
|
563
|
+
status: "completed",
|
|
564
|
+
atIso,
|
|
565
|
+
explainer: item.summary ?? defaultExplainer("completed"),
|
|
566
|
+
force: true,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
setStatus({
|
|
571
|
+
projection,
|
|
572
|
+
status: "needs_review",
|
|
573
|
+
atIso,
|
|
574
|
+
explainer: item.summary ??
|
|
575
|
+
"Reported completion without artifact evidence. Review output before closing.",
|
|
576
|
+
force: true,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (event === "auto_continue_stopped") {
|
|
582
|
+
const stopReason = (metadataString(metadata, ["stop_reason", "stopReason"]) ?? "").toLowerCase();
|
|
583
|
+
if (stopReason === "error") {
|
|
584
|
+
setStatus({
|
|
585
|
+
projection,
|
|
586
|
+
status: "failed",
|
|
587
|
+
atIso,
|
|
588
|
+
explainer: item.summary ?? item.description ?? defaultExplainer("failed"),
|
|
589
|
+
force: true,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
else if (stopReason === "blocked" || stopReason === "budget_exhausted") {
|
|
593
|
+
setStatus({
|
|
594
|
+
projection,
|
|
595
|
+
status: "awaiting_input",
|
|
596
|
+
atIso,
|
|
597
|
+
explainer: item.summary ?? item.description ?? defaultExplainer("awaiting_input"),
|
|
598
|
+
force: true,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
else if (stopReason === "completed") {
|
|
602
|
+
if (projection.hasArtifact) {
|
|
603
|
+
setStatus({
|
|
604
|
+
projection,
|
|
605
|
+
status: "completed",
|
|
606
|
+
atIso,
|
|
607
|
+
explainer: item.summary ?? defaultExplainer("completed"),
|
|
608
|
+
force: true,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
setStatus({
|
|
613
|
+
projection,
|
|
614
|
+
status: "needs_review",
|
|
615
|
+
atIso,
|
|
616
|
+
explainer: item.summary ?? defaultExplainer("needs_review"),
|
|
617
|
+
force: true,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
if (item.type === "run_completed") {
|
|
624
|
+
setStatus({
|
|
625
|
+
projection,
|
|
626
|
+
status: "completed",
|
|
627
|
+
atIso,
|
|
628
|
+
explainer: item.summary ?? item.description ?? "Accepted and marked complete.",
|
|
629
|
+
force: true,
|
|
630
|
+
});
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (item.type === "run_failed") {
|
|
634
|
+
setStatus({
|
|
635
|
+
projection,
|
|
636
|
+
status: "failed",
|
|
637
|
+
atIso,
|
|
638
|
+
explainer: item.summary ?? item.description ?? item.title,
|
|
639
|
+
force: true,
|
|
640
|
+
});
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
if (item.type === "decision_requested") {
|
|
644
|
+
setStatus({
|
|
645
|
+
projection,
|
|
646
|
+
status: "awaiting_input",
|
|
647
|
+
atIso,
|
|
648
|
+
explainer: item.summary ?? item.description ?? "Awaiting decision.",
|
|
649
|
+
});
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
if (item.type === "decision_resolved") {
|
|
653
|
+
// Decision resolved may unblock a slice — revert to running if currently awaiting
|
|
654
|
+
if (projection.status === "awaiting_input") {
|
|
655
|
+
setStatus({
|
|
656
|
+
projection,
|
|
657
|
+
status: "running",
|
|
658
|
+
atIso,
|
|
659
|
+
explainer: item.summary ?? "Decision resolved. Resuming.",
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
if (item.type === "milestone_completed") {
|
|
665
|
+
// Milestone completions update context but don't change slice status
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
if (item.type === "blocker_created") {
|
|
669
|
+
setStatus({
|
|
670
|
+
projection,
|
|
671
|
+
status: "awaiting_input",
|
|
672
|
+
atIso,
|
|
673
|
+
explainer: item.summary ?? item.description ?? "Blocker raised.",
|
|
674
|
+
});
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
if (item.type === "run_started" || item.type === "delegation") {
|
|
678
|
+
setStatus({
|
|
679
|
+
projection,
|
|
680
|
+
status: "running",
|
|
681
|
+
atIso,
|
|
682
|
+
explainer: item.summary ?? item.description ?? defaultExplainer("running"),
|
|
683
|
+
});
|
|
684
|
+
projection.startedAt = projection.startedAt ?? atIso;
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
// Handoff types — no slice status change
|
|
688
|
+
if (item.type === "handoff_requested" ||
|
|
689
|
+
item.type === "handoff_claimed" ||
|
|
690
|
+
item.type === "handoff_fulfilled") {
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
// Exhaustiveness guard: compile error if a new LiveActivityType is
|
|
694
|
+
// added without being handled in this loop.
|
|
695
|
+
const _exhaustiveType = item.type;
|
|
696
|
+
void _exhaustiveType;
|
|
697
|
+
}
|
|
698
|
+
for (const decision of input.decisions) {
|
|
699
|
+
const decisionRecord = asRecord(decision);
|
|
700
|
+
const metadata = asRecord(decisionRecord?.metadata);
|
|
701
|
+
const sliceRunId = metadataString(metadata, ["slice_run_id", "sliceRunId", "run_id", "runId", "correlation_id", "correlationId"]);
|
|
702
|
+
if (!sliceRunId)
|
|
703
|
+
continue;
|
|
704
|
+
const projection = projections.get(sliceRunId);
|
|
705
|
+
if (!projection)
|
|
706
|
+
continue;
|
|
707
|
+
const status = (normalizeText(decisionRecord?.status) ?? "pending").toLowerCase();
|
|
708
|
+
if (status === "approved" || status === "declined" || status === "cancelled" || status === "resolved") {
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
projection.decisionCount += 1;
|
|
712
|
+
const isBlocking = metadataBoolean(metadata, ["blocking"]) !== false;
|
|
713
|
+
if (isBlocking)
|
|
714
|
+
projection.blockingDecisionCount += 1;
|
|
715
|
+
mergeDecisionOptions(projection, decisionRecord?.options ?? null);
|
|
716
|
+
const updatedAt = toIso(normalizeText(decisionRecord?.updatedAt)) ??
|
|
717
|
+
toIso(normalizeText(decisionRecord?.requestedAt)) ??
|
|
718
|
+
projection.updatedAt;
|
|
719
|
+
setStatus({
|
|
720
|
+
projection,
|
|
721
|
+
status: "awaiting_input",
|
|
722
|
+
atIso: updatedAt,
|
|
723
|
+
explainer: normalizeText(decisionRecord?.title) ?? "Pending decision requires input.",
|
|
724
|
+
force: projection.status !== "failed",
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
for (const session of input.sessions) {
|
|
728
|
+
const runId = normalizeText(session.runId);
|
|
729
|
+
if (!runId)
|
|
730
|
+
continue;
|
|
731
|
+
const projection = projections.get(runId);
|
|
732
|
+
if (!projection)
|
|
733
|
+
continue;
|
|
734
|
+
applySessionFallback(projection, session);
|
|
735
|
+
}
|
|
736
|
+
for (const runtime of input.runtimeInstances) {
|
|
737
|
+
const runId = normalizeText(runtime.runId ?? runtime.correlationId);
|
|
738
|
+
if (!runId)
|
|
739
|
+
continue;
|
|
740
|
+
const projection = projections.get(runId);
|
|
741
|
+
if (!projection)
|
|
742
|
+
continue;
|
|
743
|
+
projection.initiativeIds = mergeScopedIds(projection.initiativeIds, projection.initiativeId, runtime.initiativeId ? [runtime.initiativeId] : []);
|
|
744
|
+
projection.workstreamIds = mergeScopedIds(projection.workstreamIds, projection.workstreamId, runtime.workstreamId ? [runtime.workstreamId] : []);
|
|
745
|
+
projection.initiativeId = projection.initiativeId ?? projection.initiativeIds[0] ?? null;
|
|
746
|
+
projection.workstreamId = projection.workstreamId ?? projection.workstreamIds[0] ?? null;
|
|
747
|
+
projection.runtimeState = runtime.state;
|
|
748
|
+
if (runtime.state === "error" && projection.status !== "failed") {
|
|
749
|
+
setStatus({
|
|
750
|
+
projection,
|
|
751
|
+
status: "failed",
|
|
752
|
+
atIso: runtime.lastEventAt,
|
|
753
|
+
explainer: runtime.lastMessage ?? defaultExplainer("failed"),
|
|
754
|
+
force: true,
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
if (runtime.state === "active" && (projection.status === "dispatching" || projection.status === "queued")) {
|
|
758
|
+
setStatus({
|
|
759
|
+
projection,
|
|
760
|
+
status: "running",
|
|
761
|
+
atIso: runtime.lastHeartbeatAt ?? runtime.lastEventAt,
|
|
762
|
+
explainer: projection.lastEventSummary ?? defaultExplainer("running"),
|
|
763
|
+
});
|
|
764
|
+
projection.startedAt = projection.startedAt ?? toIso(runtime.lastEventAt);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const nowEpoch = Date.now();
|
|
768
|
+
const output = [];
|
|
769
|
+
for (const projection of projections.values()) {
|
|
770
|
+
if (projection.status === "completed" && !projection.hasArtifact) {
|
|
771
|
+
setStatus({
|
|
772
|
+
projection,
|
|
773
|
+
status: "needs_review",
|
|
774
|
+
atIso: projection.updatedAt,
|
|
775
|
+
explainer: "Completed without artifacts; review before closing.",
|
|
776
|
+
force: true,
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
if ((projection.status === "queued" || projection.status === "dispatching" || projection.status === "running") &&
|
|
780
|
+
projection.runtimeState === "stale" &&
|
|
781
|
+
projection.artifactCount === 0) {
|
|
782
|
+
const ageMs = nowEpoch - toEpoch(projection.lastEventAt ?? projection.updatedAt);
|
|
783
|
+
if (ageMs >= 6 * 60 * 60 * 1000) {
|
|
784
|
+
setStatus({
|
|
785
|
+
projection,
|
|
786
|
+
status: "archived",
|
|
787
|
+
atIso: projection.updatedAt,
|
|
788
|
+
explainer: "No execution evidence found recently. Archived automatically.",
|
|
789
|
+
force: true,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
else if (projection.status === "running") {
|
|
793
|
+
projection.confidence = "medium";
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (!projection.statusExplainer) {
|
|
797
|
+
projection.statusExplainer = defaultExplainer(projection.status);
|
|
798
|
+
}
|
|
799
|
+
if (projection.status === "completed" && projection.hasArtifact) {
|
|
800
|
+
projection.primaryAction = "open_artifact";
|
|
801
|
+
projection.confidence = "high";
|
|
802
|
+
}
|
|
803
|
+
else if (projection.status === "awaiting_input") {
|
|
804
|
+
projection.primaryAction = "resolve_decision";
|
|
805
|
+
projection.confidence = "high";
|
|
806
|
+
}
|
|
807
|
+
else if (projection.status === "failed") {
|
|
808
|
+
projection.primaryAction = "retry_slice";
|
|
809
|
+
projection.confidence = "high";
|
|
810
|
+
}
|
|
811
|
+
else if (projection.status === "needs_review") {
|
|
812
|
+
projection.primaryAction = "review_output";
|
|
813
|
+
projection.confidence = projection.hasArtifact ? "high" : "medium";
|
|
814
|
+
}
|
|
815
|
+
else if (projection.status === "running" || projection.status === "dispatching") {
|
|
816
|
+
projection.confidence = projection.runtimeState === "active" ? "high" : "medium";
|
|
817
|
+
}
|
|
818
|
+
output.push({
|
|
819
|
+
id: projection.id,
|
|
820
|
+
sliceRunId: projection.sliceRunId,
|
|
821
|
+
runId: projection.runId,
|
|
822
|
+
initiativeId: projection.initiativeId,
|
|
823
|
+
initiativeIds: projection.initiativeIds,
|
|
824
|
+
workstreamId: projection.workstreamId,
|
|
825
|
+
workstreamIds: projection.workstreamIds,
|
|
826
|
+
iwmtId: projection.iwmtId,
|
|
827
|
+
iwmtIds: projection.iwmtIds,
|
|
828
|
+
workstreamTitle: projection.workstreamTitle,
|
|
829
|
+
taskIds: projection.taskIds,
|
|
830
|
+
milestoneIds: projection.milestoneIds,
|
|
831
|
+
status: projection.status,
|
|
832
|
+
statusExplainer: projection.statusExplainer,
|
|
833
|
+
primaryAction: projection.primaryAction,
|
|
834
|
+
hasArtifact: projection.hasArtifact,
|
|
835
|
+
artifactCount: projection.artifactCount,
|
|
836
|
+
artifacts: projection.artifacts,
|
|
837
|
+
decisionCount: projection.decisionCount,
|
|
838
|
+
blockingDecisionCount: projection.blockingDecisionCount,
|
|
839
|
+
decisionOptions: projection.decisionOptions,
|
|
840
|
+
sourceClient: projection.sourceClient,
|
|
841
|
+
runtimeState: projection.runtimeState,
|
|
842
|
+
startedAt: projection.startedAt,
|
|
843
|
+
updatedAt: projection.updatedAt,
|
|
844
|
+
completedAt: projection.completedAt,
|
|
845
|
+
failedAt: projection.failedAt,
|
|
846
|
+
archivedAt: projection.archivedAt,
|
|
847
|
+
lastEventAt: projection.lastEventAt,
|
|
848
|
+
lastEventSummary: projection.lastEventSummary,
|
|
849
|
+
correlationId: projection.correlationId,
|
|
850
|
+
confidence: projection.confidence,
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
output.sort((a, b) => {
|
|
854
|
+
const updatedDelta = toEpoch(b.updatedAt ?? b.lastEventAt) - toEpoch(a.updatedAt ?? a.lastEventAt);
|
|
855
|
+
if (updatedDelta !== 0)
|
|
856
|
+
return updatedDelta;
|
|
857
|
+
return a.sliceRunId.localeCompare(b.sliceRunId);
|
|
858
|
+
});
|
|
859
|
+
return output;
|
|
860
|
+
}
|