@useorgx/openclaw-plugin 0.7.0 → 0.7.3
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 +42 -11
- package/dashboard/dist/assets/6mILZQ2a.js +1 -0
- package/dashboard/dist/assets/6mILZQ2a.js.br +0 -0
- package/dashboard/dist/assets/6mILZQ2a.js.gz +0 -0
- package/dashboard/dist/assets/{DG6y9wJI.js → 8dksYiq4.js} +1 -1
- package/dashboard/dist/assets/8dksYiq4.js.br +0 -0
- package/dashboard/dist/assets/8dksYiq4.js.gz +0 -0
- package/dashboard/dist/assets/{PAUiij_z.js → B5zYRHc3.js} +1 -1
- package/dashboard/dist/assets/B5zYRHc3.js.br +0 -0
- package/dashboard/dist/assets/B5zYRHc3.js.gz +0 -0
- package/dashboard/dist/assets/{CFGKRAzG.js → B6wPWJ35.js} +1 -1
- package/dashboard/dist/assets/B6wPWJ35.js.br +0 -0
- package/dashboard/dist/assets/B6wPWJ35.js.gz +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js +1 -0
- package/dashboard/dist/assets/BWEwjt1W.js.br +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js.gz +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css +1 -0
- package/dashboard/dist/assets/BzRbDCAD.css.br +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css.gz +0 -0
- package/dashboard/dist/assets/{DNxKz-GV.js → C8uM3AX8.js} +1 -1
- package/dashboard/dist/assets/C8uM3AX8.js.br +0 -0
- package/dashboard/dist/assets/C8uM3AX8.js.gz +0 -0
- package/dashboard/dist/assets/C9jy61eu.js +212 -0
- package/dashboard/dist/assets/C9jy61eu.js.br +0 -0
- package/dashboard/dist/assets/C9jy61eu.js.gz +0 -0
- package/dashboard/dist/assets/{CE38zU4U.js → CC63EwFD.js} +1 -1
- package/dashboard/dist/assets/CC63EwFD.js.br +0 -0
- package/dashboard/dist/assets/CC63EwFD.js.gz +0 -0
- package/dashboard/dist/assets/{nByHNHoW.js → CZaT3ob_.js} +1 -1
- package/dashboard/dist/assets/CZaT3ob_.js.br +0 -0
- package/dashboard/dist/assets/CZaT3ob_.js.gz +0 -0
- package/dashboard/dist/assets/{tS9mbYZi.js → CgaottFX.js} +1 -1
- package/dashboard/dist/assets/CgaottFX.js.br +0 -0
- package/dashboard/dist/assets/CgaottFX.js.gz +0 -0
- package/dashboard/dist/assets/CzCxAZlW.js +1 -0
- package/dashboard/dist/assets/CzCxAZlW.js.br +0 -0
- package/dashboard/dist/assets/CzCxAZlW.js.gz +0 -0
- package/dashboard/dist/assets/D3iMTYEj.js +1 -0
- package/dashboard/dist/assets/D3iMTYEj.js.br +0 -0
- package/dashboard/dist/assets/D3iMTYEj.js.gz +0 -0
- package/dashboard/dist/assets/{DjcdE6jC.js → D8JNX8kq.js} +1 -1
- package/dashboard/dist/assets/D8JNX8kq.js.br +0 -0
- package/dashboard/dist/assets/D8JNX8kq.js.gz +0 -0
- package/dashboard/dist/assets/DnA8dpj6.js +1 -0
- package/dashboard/dist/assets/DnA8dpj6.js.br +0 -0
- package/dashboard/dist/assets/DnA8dpj6.js.gz +0 -0
- package/dashboard/dist/assets/{DbNoijHm.js → IUexzymk.js} +1 -1
- package/dashboard/dist/assets/IUexzymk.js.br +0 -0
- package/dashboard/dist/assets/IUexzymk.js.gz +0 -0
- package/dashboard/dist/assets/{CZZTvkQZ.js → rttbDbEx.js} +1 -1
- package/dashboard/dist/assets/rttbDbEx.js.br +0 -0
- package/dashboard/dist/assets/rttbDbEx.js.gz +0 -0
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/contracts/practice-exercise-schema.d.ts +216 -0
- package/dist/contracts/practice-exercise-schema.js +314 -0
- package/dist/contracts/shared-types.d.ts +2 -2
- package/dist/contracts/shared-types.js +22 -0
- package/dist/contracts/types.d.ts +20 -0
- package/dist/fs-utils.js +1 -13
- package/dist/http/helpers/auto-continue-engine.js +638 -24
- package/dist/http/helpers/autopilot-runtime.js +22 -7
- package/dist/http/helpers/autopilot-slice-utils.js +0 -2
- package/dist/http/helpers/kickoff-context.js +30 -0
- package/dist/http/helpers/slice-run-projections.js +19 -2
- package/dist/http/index.js +151 -93
- package/dist/http/routes/agent-suite.js +87 -0
- package/dist/http/routes/entities.js +1 -63
- package/dist/http/routes/live-snapshot.d.ts +1 -0
- package/dist/http/routes/live-snapshot.js +15 -4
- package/dist/http/routes/live-terminal.js +1 -108
- package/dist/http/routes/live-triage.js +1 -57
- package/dist/http/routes/mission-control-actions.js +0 -88
- package/dist/http/routes/mission-control-read.js +73 -8
- package/dist/mcp-http-handler.d.ts +3 -0
- package/dist/mcp-http-handler.js +2 -2
- package/dist/openclaw.plugin.json +1 -1
- package/dist/paths.js +14 -2
- package/dist/reporting/rollups.d.ts +0 -12
- package/dist/reporting/rollups.js +0 -35
- package/openclaw.plugin.json +1 -1
- package/package.json +7 -3
- package/dashboard/dist/assets/BXWDRGm-.js +0 -1
- package/dashboard/dist/assets/BXWDRGm-.js.br +0 -0
- package/dashboard/dist/assets/BXWDRGm-.js.gz +0 -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.br +0 -0
- package/dashboard/dist/assets/CFGKRAzG.js.gz +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js +0 -1
- package/dashboard/dist/assets/CGGR2GZh.js.br +0 -0
- package/dashboard/dist/assets/CGGR2GZh.js.gz +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js +0 -8
- package/dashboard/dist/assets/CPFiTmlw.js.br +0 -0
- package/dashboard/dist/assets/CPFiTmlw.js.gz +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.br +0 -0
- package/dashboard/dist/assets/CZZTvkQZ.js.gz +0 -0
- package/dashboard/dist/assets/D-bf6hEI.js +0 -213
- 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.br +0 -0
- package/dashboard/dist/assets/DG6y9wJI.js.gz +0 -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 +0 -11
- 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.br +0 -0
- package/dashboard/dist/assets/DbNoijHm.js.gz +0 -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 +0 -1
- 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.br +0 -0
- package/dashboard/dist/assets/PAUiij_z.js.gz +0 -0
- package/dashboard/dist/assets/h5biQs2I.css +0 -1
- package/dashboard/dist/assets/h5biQs2I.css.br +0 -0
- package/dashboard/dist/assets/h5biQs2I.css.gz +0 -0
- package/dashboard/dist/assets/nByHNHoW.js.br +0 -0
- package/dashboard/dist/assets/nByHNHoW.js.gz +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js.br +0 -0
- package/dashboard/dist/assets/tS9mbYZi.js.gz +0 -0
|
@@ -50,10 +50,18 @@ export function createAutopilotRuntime(deps) {
|
|
|
50
50
|
}
|
|
51
51
|
if (!inTargetSection)
|
|
52
52
|
continue;
|
|
53
|
-
const urlMatch = line.match(/^url\s*=\s*"([^"]
|
|
53
|
+
const urlMatch = line.match(/^url\s*=\s*("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')\s*(?:#.*)?$/i);
|
|
54
54
|
if (!urlMatch)
|
|
55
55
|
continue;
|
|
56
|
-
const
|
|
56
|
+
const token = (urlMatch[1] ?? "").trim();
|
|
57
|
+
if (token.length < 2)
|
|
58
|
+
continue;
|
|
59
|
+
const quote = token[0];
|
|
60
|
+
const unescaped = token
|
|
61
|
+
.slice(1, -1)
|
|
62
|
+
.replace(quote === '"' ? /\\"/g : /\\'/g, quote)
|
|
63
|
+
.trim();
|
|
64
|
+
const rawUrl = unescaped;
|
|
57
65
|
if (rawUrl.length > 0)
|
|
58
66
|
return rawUrl;
|
|
59
67
|
}
|
|
@@ -147,6 +155,13 @@ export function createAutopilotRuntime(deps) {
|
|
|
147
155
|
// best effort
|
|
148
156
|
}
|
|
149
157
|
}
|
|
158
|
+
function createSafeAppendStream(pathname) {
|
|
159
|
+
const stream = createWriteStream(pathname, { flags: "a" });
|
|
160
|
+
stream.on("error", () => {
|
|
161
|
+
// Best-effort logging only.
|
|
162
|
+
});
|
|
163
|
+
return stream;
|
|
164
|
+
}
|
|
150
165
|
function hasSliceOutput(pathname) {
|
|
151
166
|
try {
|
|
152
167
|
if (!existsSync(pathname))
|
|
@@ -181,8 +196,8 @@ export function createAutopilotRuntime(deps) {
|
|
|
181
196
|
const workerKind = (process.env.ORGX_AUTOPILOT_WORKER_KIND ?? "").trim().toLowerCase();
|
|
182
197
|
if (workerKind === "mock") {
|
|
183
198
|
const scriptPath = resolve(dirname(deps.filename), "..", "..", "scripts", "mock-autopilot-slice-worker.mjs");
|
|
184
|
-
const logStream =
|
|
185
|
-
const outStream =
|
|
199
|
+
const logStream = createSafeAppendStream(input.logPath);
|
|
200
|
+
const outStream = createSafeAppendStream(input.outputPath);
|
|
186
201
|
logStream.write(`\n==== ${new Date().toISOString()} :: mock slice ${input.runId} ====\n`);
|
|
187
202
|
const child = spawn("node", [scriptPath], {
|
|
188
203
|
cwd: input.cwd,
|
|
@@ -329,8 +344,8 @@ export function createAutopilotRuntime(deps) {
|
|
|
329
344
|
}
|
|
330
345
|
if (schemaArg)
|
|
331
346
|
claudeExtraArgs.push("--json-schema", schemaArg);
|
|
332
|
-
const logStream =
|
|
333
|
-
const outStream =
|
|
347
|
+
const logStream = createSafeAppendStream(input.logPath);
|
|
348
|
+
const outStream = createSafeAppendStream(input.outputPath);
|
|
334
349
|
logStream.write(`\n==== ${new Date().toISOString()} :: claude slice ${input.runId} ====\n`);
|
|
335
350
|
logStream.write(`claude_bin: ${claudeBin}\n`);
|
|
336
351
|
if (claudeExtraArgs.length > 0) {
|
|
@@ -462,7 +477,7 @@ export function createAutopilotRuntime(deps) {
|
|
|
462
477
|
? normalizedArgs
|
|
463
478
|
: ["exec", ...normalizedArgs];
|
|
464
479
|
const extraArgs = [];
|
|
465
|
-
const logStream =
|
|
480
|
+
const logStream = createSafeAppendStream(input.logPath);
|
|
466
481
|
logStream.write(`\n==== ${new Date().toISOString()} :: slice ${input.runId} ====\n`);
|
|
467
482
|
logStream.write(`codex_bin: ${codexBin}${codexInfo.versionString ? ` (${codexInfo.versionString})` : ""}\n`);
|
|
468
483
|
const childEnv = {
|
|
@@ -316,7 +316,6 @@ export function parseSliceResult(raw) {
|
|
|
316
316
|
const parsedFinalOutput = parseEmbeddedText(finalOutput);
|
|
317
317
|
if (parsedFinalOutput)
|
|
318
318
|
return parsedFinalOutput;
|
|
319
|
-
return null;
|
|
320
319
|
}
|
|
321
320
|
if (typeof finalOutput === "string") {
|
|
322
321
|
const parsedFinalOutput = parseSliceJsonText(finalOutput);
|
|
@@ -330,7 +329,6 @@ export function parseSliceResult(raw) {
|
|
|
330
329
|
const parsedStructured = parseEmbeddedText(structured);
|
|
331
330
|
if (parsedStructured)
|
|
332
331
|
return parsedStructured;
|
|
333
|
-
return null;
|
|
334
332
|
}
|
|
335
333
|
if (typeof structured === "string") {
|
|
336
334
|
const parsedStructured = parseSliceJsonText(structured);
|
|
@@ -80,6 +80,36 @@ export function renderKickoffMessage(input) {
|
|
|
80
80
|
typeof runtimeSettings.decision_auto_resolve_guarded_enabled === "boolean"
|
|
81
81
|
? `- Guarded auto-resolve: ${runtimeSettings.decision_auto_resolve_guarded_enabled ? "enabled" : "disabled"}`
|
|
82
82
|
: null,
|
|
83
|
+
runtimeSettings &&
|
|
84
|
+
typeof runtimeSettings.question_auto_answer_enabled === "boolean"
|
|
85
|
+
? `- Question auto-answer: ${runtimeSettings.question_auto_answer_enabled ? "enabled" : "disabled"}`
|
|
86
|
+
: null,
|
|
87
|
+
runtimeSettings &&
|
|
88
|
+
typeof runtimeSettings.question_auto_answer_timeout_sec === "number"
|
|
89
|
+
? `- Question auto-answer timeout: ${Math.max(10, Math.min(3600, Math.floor(runtimeSettings.question_auto_answer_timeout_sec)))}s`
|
|
90
|
+
: runtimeSettings &&
|
|
91
|
+
typeof runtimeSettings.question_auto_answer_delay_seconds === "number"
|
|
92
|
+
? `- Question auto-answer delay: ${Math.max(1, Math.min(900, Math.floor(runtimeSettings.question_auto_answer_delay_seconds)))}s`
|
|
93
|
+
: null,
|
|
94
|
+
runtimeSettings &&
|
|
95
|
+
typeof runtimeSettings.question_auto_answer_policy === "string"
|
|
96
|
+
? `- Question auto-answer policy: ${runtimeSettings.question_auto_answer_policy}`
|
|
97
|
+
: null,
|
|
98
|
+
runtimeSettings &&
|
|
99
|
+
typeof runtimeSettings.question_blocking_behavior === "string"
|
|
100
|
+
? `- Blocking question behavior: ${runtimeSettings.question_blocking_behavior}`
|
|
101
|
+
: null,
|
|
102
|
+
runtimeSettings &&
|
|
103
|
+
typeof runtimeSettings.question_auto_answer_action === "string"
|
|
104
|
+
? `- Question auto-answer action: ${runtimeSettings.question_auto_answer_action.trim().toLowerCase() === "reject"
|
|
105
|
+
? "reject"
|
|
106
|
+
: "approve"}`
|
|
107
|
+
: null,
|
|
108
|
+
runtimeSettings &&
|
|
109
|
+
runtimeSettings.workspace_question_defaults &&
|
|
110
|
+
typeof runtimeSettings.workspace_question_defaults === "object"
|
|
111
|
+
? "- Workspace question defaults available"
|
|
112
|
+
: null,
|
|
83
113
|
].filter((line) => Boolean(line));
|
|
84
114
|
const contextHash = kickoff.context_hash?.trim() || null;
|
|
85
115
|
const schemaVersion = (kickoff.schema_version ?? "").trim();
|
|
@@ -137,6 +137,14 @@ function resolveSliceRunId(item, metadata) {
|
|
|
137
137
|
return item.runId.trim();
|
|
138
138
|
return null;
|
|
139
139
|
}
|
|
140
|
+
function resolveDecisionSliceRunId(decision, metadata) {
|
|
141
|
+
return (metadataString(metadata, ["slice_run_id", "sliceRunId", "run_id", "runId", "correlation_id", "correlationId"]) ??
|
|
142
|
+
normalizeText(decision.runId) ??
|
|
143
|
+
normalizeText(decision.run_id) ??
|
|
144
|
+
normalizeText(decision.correlationId) ??
|
|
145
|
+
normalizeText(decision.correlation_id) ??
|
|
146
|
+
null);
|
|
147
|
+
}
|
|
140
148
|
function resolveCorrelationId(item, metadata) {
|
|
141
149
|
return (metadataString(metadata, ["correlation_id", "correlationId"]) ??
|
|
142
150
|
metadataString(metadata, ["run_id", "runId"]) ??
|
|
@@ -267,6 +275,8 @@ function createProjection(sliceRunId) {
|
|
|
267
275
|
_statusUpdatedEpoch: 0,
|
|
268
276
|
_updatedEpoch: 0,
|
|
269
277
|
_artifactIds: new Set(),
|
|
278
|
+
_hasExplicitCompletion: false,
|
|
279
|
+
_peakReportedArtifacts: 0,
|
|
270
280
|
};
|
|
271
281
|
}
|
|
272
282
|
function upsertProjection(map, sliceRunId) {
|
|
@@ -532,6 +542,7 @@ export function buildSliceRunProjections(input) {
|
|
|
532
542
|
if (event === "autopilot_slice_finished" || event === "autopilot_slice_result") {
|
|
533
543
|
const parsedStatus = (metadataString(metadata, ["parsed_status", "status"]) ?? "").toLowerCase();
|
|
534
544
|
const reportedArtifacts = Math.max(0, Math.floor(metadataNumber(metadata, ["artifacts"]) ?? 0));
|
|
545
|
+
projection._peakReportedArtifacts = Math.max(projection._peakReportedArtifacts, reportedArtifacts);
|
|
535
546
|
projection.decisionCount = Math.max(projection.decisionCount, Math.max(0, Math.floor(metadataNumber(metadata, ["decisions"]) ?? 0)));
|
|
536
547
|
projection.blockingDecisionCount = Math.max(projection.blockingDecisionCount, Math.max(0, Math.floor(metadataNumber(metadata, ["blocking_decisions"]) ?? 0)));
|
|
537
548
|
if (parsedStatus === "error") {
|
|
@@ -621,6 +632,7 @@ export function buildSliceRunProjections(input) {
|
|
|
621
632
|
continue;
|
|
622
633
|
}
|
|
623
634
|
if (item.type === "run_completed") {
|
|
635
|
+
projection._hasExplicitCompletion = true;
|
|
624
636
|
setStatus({
|
|
625
637
|
projection,
|
|
626
638
|
status: "completed",
|
|
@@ -698,7 +710,9 @@ export function buildSliceRunProjections(input) {
|
|
|
698
710
|
for (const decision of input.decisions) {
|
|
699
711
|
const decisionRecord = asRecord(decision);
|
|
700
712
|
const metadata = asRecord(decisionRecord?.metadata);
|
|
701
|
-
const sliceRunId =
|
|
713
|
+
const sliceRunId = decisionRecord
|
|
714
|
+
? resolveDecisionSliceRunId(decisionRecord, metadata)
|
|
715
|
+
: null;
|
|
702
716
|
if (!sliceRunId)
|
|
703
717
|
continue;
|
|
704
718
|
const projection = projections.get(sliceRunId);
|
|
@@ -767,7 +781,10 @@ export function buildSliceRunProjections(input) {
|
|
|
767
781
|
const nowEpoch = Date.now();
|
|
768
782
|
const output = [];
|
|
769
783
|
for (const projection of projections.values()) {
|
|
770
|
-
if (projection.status === "completed" &&
|
|
784
|
+
if (projection.status === "completed" &&
|
|
785
|
+
!projection.hasArtifact &&
|
|
786
|
+
!projection._hasExplicitCompletion &&
|
|
787
|
+
projection._peakReportedArtifacts === 0) {
|
|
771
788
|
setStatus({
|
|
772
789
|
projection,
|
|
773
790
|
status: "needs_review",
|
package/dist/http/index.js
CHANGED
|
@@ -26,6 +26,7 @@ import { readNextUpQueuePins, removeNextUpQueuePin, setNextUpQueuePinOrder, supp
|
|
|
26
26
|
import { formatStatus, formatAgents, formatActivity, formatInitiatives, getOnboardingState, } from "../dashboard-api.js";
|
|
27
27
|
import { loadLocalOpenClawSnapshot, loadLocalTurnDetail, toLocalLiveActivity, toLocalLiveAgents, toLocalLiveInitiatives, toLocalSessionTree, } from "../local-openclaw.js";
|
|
28
28
|
import { defaultOutboxAdapter } from "../adapters/outbox.js";
|
|
29
|
+
import { appendToOutbox } from "../outbox.js";
|
|
29
30
|
import { readAgentContexts, upsertAgentContext, upsertRunContext } from "../agent-context-store.js";
|
|
30
31
|
import { getAgentRun, markAgentRunStopped, readAgentRuns, upsertAgentRun, } from "../agent-run-store.js";
|
|
31
32
|
import { appendEntityComment, listEntityComments, mergeEntityComments, } from "../entity-comment-store.js";
|
|
@@ -201,7 +202,7 @@ async function mapWithConcurrency(items, concurrency, mapper) {
|
|
|
201
202
|
}
|
|
202
203
|
const ACTIVITY_WARM_THROTTLE_MS = 30_000;
|
|
203
204
|
const activityWarmByKey = new Map();
|
|
204
|
-
const SNAPSHOT_RESPONSE_CACHE_TTL_MS =
|
|
205
|
+
const SNAPSHOT_RESPONSE_CACHE_TTL_MS = 800;
|
|
205
206
|
const SNAPSHOT_RESPONSE_CACHE_MAX_ENTRIES = 16;
|
|
206
207
|
const SNAPSHOT_ACTIVITY_PERSIST_MIN_INTERVAL_MS = 15_000;
|
|
207
208
|
const SNAPSHOT_ACTIVITY_FINGERPRINT_DEPTH = 8;
|
|
@@ -222,6 +223,7 @@ const LIVE_WORKSPACE_INITIATIVE_STATUSES = [
|
|
|
222
223
|
];
|
|
223
224
|
let lastSnapshotActivityPersistAt = 0;
|
|
224
225
|
let lastSnapshotActivityFingerprint = "";
|
|
226
|
+
let snapshotCacheGeneration = 0;
|
|
225
227
|
const snapshotResponseCache = new Map();
|
|
226
228
|
const ACTIVITY_DECISION_EVENT_HINTS = new Set([
|
|
227
229
|
"decision_buffered",
|
|
@@ -359,7 +361,7 @@ function readSnapshotResponseCache(key) {
|
|
|
359
361
|
const entry = snapshotResponseCache.get(key);
|
|
360
362
|
if (!entry)
|
|
361
363
|
return null;
|
|
362
|
-
if (entry.expiresAt <= Date.now()) {
|
|
364
|
+
if (entry.generation !== snapshotCacheGeneration || entry.expiresAt <= Date.now()) {
|
|
363
365
|
snapshotResponseCache.delete(key);
|
|
364
366
|
return null;
|
|
365
367
|
}
|
|
@@ -369,6 +371,7 @@ function writeSnapshotResponseCache(key, payload) {
|
|
|
369
371
|
const now = Date.now();
|
|
370
372
|
snapshotResponseCache.set(key, {
|
|
371
373
|
expiresAt: now + SNAPSHOT_RESPONSE_CACHE_TTL_MS,
|
|
374
|
+
generation: snapshotCacheGeneration,
|
|
372
375
|
payload,
|
|
373
376
|
});
|
|
374
377
|
if (snapshotResponseCache.size <= SNAPSHOT_RESPONSE_CACHE_MAX_ENTRIES)
|
|
@@ -385,6 +388,7 @@ function writeSnapshotResponseCache(key, payload) {
|
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
390
|
function clearSnapshotResponseCache() {
|
|
391
|
+
snapshotCacheGeneration += 1;
|
|
388
392
|
snapshotResponseCache.clear();
|
|
389
393
|
}
|
|
390
394
|
function isUserScopedApiKey(apiKey) {
|
|
@@ -568,19 +572,29 @@ function applyAgentContextsToActivity(input, contexts) {
|
|
|
568
572
|
return enrichActivityActorFields(nextItem);
|
|
569
573
|
});
|
|
570
574
|
}
|
|
575
|
+
function sessionNodeEpoch(node) {
|
|
576
|
+
const raw = node.updatedAt ?? node.lastEventAt ?? node.startedAt;
|
|
577
|
+
if (!raw)
|
|
578
|
+
return 0;
|
|
579
|
+
const epoch = Date.parse(raw);
|
|
580
|
+
return Number.isFinite(epoch) ? epoch : 0;
|
|
581
|
+
}
|
|
571
582
|
function mergeSessionTrees(base, extra) {
|
|
572
|
-
const
|
|
573
|
-
const nodes
|
|
574
|
-
|
|
575
|
-
seenNodes.add(node.id);
|
|
576
|
-
nodes.push(node);
|
|
577
|
-
}
|
|
583
|
+
const nodeById = new Map();
|
|
584
|
+
for (const node of base.nodes ?? [])
|
|
585
|
+
nodeById.set(node.id, node);
|
|
578
586
|
for (const node of extra.nodes ?? []) {
|
|
579
|
-
|
|
587
|
+
const existing = nodeById.get(node.id);
|
|
588
|
+
if (!existing) {
|
|
589
|
+
nodeById.set(node.id, node);
|
|
580
590
|
continue;
|
|
581
|
-
|
|
582
|
-
|
|
591
|
+
}
|
|
592
|
+
const existingEpoch = sessionNodeEpoch(existing);
|
|
593
|
+
const extraEpoch = sessionNodeEpoch(node);
|
|
594
|
+
if (extraEpoch > existingEpoch)
|
|
595
|
+
nodeById.set(node.id, node);
|
|
583
596
|
}
|
|
597
|
+
const nodes = Array.from(nodeById.values());
|
|
584
598
|
const seenEdges = new Set();
|
|
585
599
|
const edges = [];
|
|
586
600
|
for (const edge of base.edges ?? []) {
|
|
@@ -616,82 +630,82 @@ function mergeSessionTrees(base, extra) {
|
|
|
616
630
|
groups: Array.from(groupsById.values()),
|
|
617
631
|
};
|
|
618
632
|
}
|
|
619
|
-
function
|
|
620
|
-
|
|
621
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
622
|
-
return null;
|
|
623
|
-
return value;
|
|
624
|
-
};
|
|
625
|
-
const metadataString = (metadata, keys) => {
|
|
626
|
-
if (!metadata)
|
|
627
|
-
return null;
|
|
628
|
-
for (const key of keys) {
|
|
629
|
-
const value = metadata[key];
|
|
630
|
-
if (typeof value !== "string")
|
|
631
|
-
continue;
|
|
632
|
-
const normalized = value.trim();
|
|
633
|
-
if (normalized.length > 0)
|
|
634
|
-
return normalized;
|
|
635
|
-
}
|
|
633
|
+
function asActivityMetadataRecord(value) {
|
|
634
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
636
635
|
return null;
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
"
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
636
|
+
return value;
|
|
637
|
+
}
|
|
638
|
+
function activityMetadataStr(metadata, keys) {
|
|
639
|
+
if (!metadata)
|
|
640
|
+
return null;
|
|
641
|
+
for (const key of keys) {
|
|
642
|
+
const value = metadata[key];
|
|
643
|
+
if (typeof value !== "string")
|
|
644
|
+
continue;
|
|
645
|
+
const normalized = value.trim();
|
|
646
|
+
if (normalized.length > 0)
|
|
647
|
+
return normalized;
|
|
648
|
+
}
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
const SEMANTIC_ACTIVITY_EVENTS = new Set([
|
|
652
|
+
"autopilot_slice_result",
|
|
653
|
+
"auto_continue_started",
|
|
654
|
+
"auto_continue_stopped",
|
|
655
|
+
"next_up_manual_dispatch_started",
|
|
656
|
+
"autopilot_slice_mcp_handshake_failed",
|
|
657
|
+
"autopilot_slice_timeout",
|
|
658
|
+
"autopilot_slice_log_stall",
|
|
659
|
+
"auto_continue_spawn_guard_blocked",
|
|
660
|
+
"auto_continue_spawn_guard_rate_limited",
|
|
661
|
+
"autopilot_autofix_scheduled",
|
|
662
|
+
"autopilot_autofix_executed",
|
|
663
|
+
"autopilot_autofix_skipped",
|
|
664
|
+
]);
|
|
665
|
+
function semanticActivityKey(item) {
|
|
666
|
+
const metadata = asActivityMetadataRecord(item.metadata);
|
|
667
|
+
const eventRaw = metadata?.event;
|
|
668
|
+
const event = typeof eventRaw === "string" ? eventRaw.trim().toLowerCase() : "";
|
|
669
|
+
if (!event || !SEMANTIC_ACTIVITY_EVENTS.has(event))
|
|
670
|
+
return null;
|
|
671
|
+
const runLike = (typeof item.runId === "string" && item.runId.trim().length > 0
|
|
672
|
+
? item.runId.trim()
|
|
673
|
+
: null) ??
|
|
674
|
+
activityMetadataStr(metadata, [
|
|
675
|
+
"run_id",
|
|
676
|
+
"runId",
|
|
677
|
+
"slice_run_id",
|
|
678
|
+
"sliceRunId",
|
|
679
|
+
"active_run_id",
|
|
680
|
+
"activeRunId",
|
|
681
|
+
"last_run_id",
|
|
682
|
+
"lastRunId",
|
|
683
|
+
]);
|
|
684
|
+
const correlationId = activityMetadataStr(metadata, ["correlation_id", "correlationId"]);
|
|
685
|
+
const initiativeId = (typeof item.initiativeId === "string" && item.initiativeId.trim().length > 0
|
|
686
|
+
? item.initiativeId.trim()
|
|
687
|
+
: null) ??
|
|
688
|
+
activityMetadataStr(metadata, ["initiative_id", "initiativeId"]);
|
|
689
|
+
const workstreamId = activityMetadataStr(metadata, ["workstream_id", "workstreamId"]);
|
|
690
|
+
const taskId = activityMetadataStr(metadata, ["task_id", "taskId"]);
|
|
691
|
+
const stopReason = activityMetadataStr(metadata, ["stop_reason", "stopReason"]);
|
|
692
|
+
const parsedStatus = activityMetadataStr(metadata, ["parsed_status", "parsedStatus"]);
|
|
693
|
+
const title = (item.title ?? "").trim().toLowerCase();
|
|
694
|
+
if (!runLike && !correlationId && !workstreamId && !taskId)
|
|
695
|
+
return null;
|
|
696
|
+
return [
|
|
697
|
+
event,
|
|
698
|
+
initiativeId ?? "",
|
|
699
|
+
workstreamId ?? "",
|
|
700
|
+
taskId ?? "",
|
|
701
|
+
runLike ?? "",
|
|
702
|
+
correlationId ?? "",
|
|
703
|
+
stopReason ?? "",
|
|
704
|
+
parsedStatus ?? "",
|
|
705
|
+
title,
|
|
706
|
+
].join("|");
|
|
707
|
+
}
|
|
708
|
+
function mergeActivities(base, extra, limit) {
|
|
695
709
|
const merged = [...(base ?? []), ...(extra ?? [])].sort((a, b) => {
|
|
696
710
|
const timestampDelta = Date.parse(b.timestamp) - Date.parse(a.timestamp);
|
|
697
711
|
if (timestampDelta !== 0)
|
|
@@ -705,11 +719,11 @@ function mergeActivities(base, extra, limit) {
|
|
|
705
719
|
if (seenIds.has(item.id))
|
|
706
720
|
continue;
|
|
707
721
|
seenIds.add(item.id);
|
|
708
|
-
const
|
|
709
|
-
if (
|
|
722
|
+
const sk = semanticActivityKey(item);
|
|
723
|
+
if (sk && seenSemantic.has(sk))
|
|
710
724
|
continue;
|
|
711
|
-
if (
|
|
712
|
-
seenSemantic.add(
|
|
725
|
+
if (sk)
|
|
726
|
+
seenSemantic.add(sk);
|
|
713
727
|
deduped.push(item);
|
|
714
728
|
if (deduped.length >= limit)
|
|
715
729
|
break;
|
|
@@ -2058,6 +2072,9 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2058
2072
|
return 3;
|
|
2059
2073
|
};
|
|
2060
2074
|
const sortQueueItems = (a, b) => {
|
|
2075
|
+
const queueDelta = queueRank(a.queueState) - queueRank(b.queueState);
|
|
2076
|
+
if (queueDelta !== 0)
|
|
2077
|
+
return queueDelta;
|
|
2061
2078
|
const aPinnedRank = pinnedRankByKey.get(`${a.initiativeId}:${a.workstreamId}`);
|
|
2062
2079
|
const bPinnedRank = pinnedRankByKey.get(`${b.initiativeId}:${b.workstreamId}`);
|
|
2063
2080
|
if (aPinnedRank !== undefined || bPinnedRank !== undefined) {
|
|
@@ -2066,9 +2083,6 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2066
2083
|
if (aRank !== bRank)
|
|
2067
2084
|
return aRank - bRank;
|
|
2068
2085
|
}
|
|
2069
|
-
const queueDelta = queueRank(a.queueState) - queueRank(b.queueState);
|
|
2070
|
-
if (queueDelta !== 0)
|
|
2071
|
-
return queueDelta;
|
|
2072
2086
|
const priorityRank = (value) => {
|
|
2073
2087
|
const normalized = (value ?? "").trim().toLowerCase();
|
|
2074
2088
|
if (!normalized)
|
|
@@ -3147,6 +3161,49 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3147
3161
|
catch (err) {
|
|
3148
3162
|
console.error(`[markRunCompleted] appendActivity failed for ${normalizedRunId}:`, err);
|
|
3149
3163
|
}
|
|
3164
|
+
// ── Write to outbox so snapshot merge picks up the completion ───────
|
|
3165
|
+
try {
|
|
3166
|
+
const outboxSessionId = runtimeRecord?.initiativeId ?? existingRun?.initiativeId ?? normalizedRunId;
|
|
3167
|
+
const outboxActivityItem = {
|
|
3168
|
+
id: randomUUID(),
|
|
3169
|
+
type: "run_completed",
|
|
3170
|
+
title: message,
|
|
3171
|
+
description: reason,
|
|
3172
|
+
agentId: runtimeRecord?.agentId ?? existingRun?.agentId ?? null,
|
|
3173
|
+
agentName: runtimeRecord?.agentName ?? null,
|
|
3174
|
+
requesterAgentId: runtimeRecord?.agentId ?? existingRun?.agentId ?? null,
|
|
3175
|
+
requesterAgentName: runtimeRecord?.agentName ?? null,
|
|
3176
|
+
executorAgentId: runtimeRecord?.agentId ?? existingRun?.agentId ?? null,
|
|
3177
|
+
executorAgentName: runtimeRecord?.agentName ?? null,
|
|
3178
|
+
runId: normalizedRunId,
|
|
3179
|
+
initiativeId: runtimeRecord?.initiativeId ?? existingRun?.initiativeId ?? null,
|
|
3180
|
+
timestamp: nowIso,
|
|
3181
|
+
phase: "completed",
|
|
3182
|
+
state: "done",
|
|
3183
|
+
summary: message,
|
|
3184
|
+
metadata: {
|
|
3185
|
+
source: "dashboard_manual_complete",
|
|
3186
|
+
reason,
|
|
3187
|
+
remoteOk,
|
|
3188
|
+
event: "dashboard_run_mark_completed",
|
|
3189
|
+
},
|
|
3190
|
+
};
|
|
3191
|
+
await appendToOutbox(outboxSessionId, {
|
|
3192
|
+
id: outboxActivityItem.id,
|
|
3193
|
+
type: "outcome",
|
|
3194
|
+
timestamp: nowIso,
|
|
3195
|
+
payload: {
|
|
3196
|
+
runId: normalizedRunId,
|
|
3197
|
+
status: "completed",
|
|
3198
|
+
reason,
|
|
3199
|
+
remoteOk,
|
|
3200
|
+
},
|
|
3201
|
+
activityItem: outboxActivityItem,
|
|
3202
|
+
});
|
|
3203
|
+
}
|
|
3204
|
+
catch (err) {
|
|
3205
|
+
console.error(`[markRunCompleted] outbox write failed for ${normalizedRunId}:`, err);
|
|
3206
|
+
}
|
|
3150
3207
|
return {
|
|
3151
3208
|
ok: true,
|
|
3152
3209
|
data: {
|
|
@@ -3357,6 +3414,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3357
3414
|
applyAgentContextsToActivity,
|
|
3358
3415
|
mergeSessionTrees,
|
|
3359
3416
|
mergeActivities,
|
|
3417
|
+
semanticActivityKey,
|
|
3360
3418
|
listRuntimeInstances,
|
|
3361
3419
|
injectRuntimeInstancesAsSessions,
|
|
3362
3420
|
enrichSessionsWithRuntime,
|
|
@@ -40,6 +40,65 @@ function readOptionalBoolean(payload, ...keys) {
|
|
|
40
40
|
}
|
|
41
41
|
return undefined;
|
|
42
42
|
}
|
|
43
|
+
function readOptionalInteger(payload, options, ...keys) {
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
const raw = payload[key];
|
|
46
|
+
let parsed = Number.NaN;
|
|
47
|
+
if (typeof raw === "number") {
|
|
48
|
+
parsed = raw;
|
|
49
|
+
}
|
|
50
|
+
else if (typeof raw === "string") {
|
|
51
|
+
const trimmed = raw.trim();
|
|
52
|
+
if (/^[+-]?\d+$/.test(trimmed)) {
|
|
53
|
+
parsed = Number.parseInt(trimmed, 10);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!Number.isFinite(parsed))
|
|
57
|
+
continue;
|
|
58
|
+
const normalized = Math.max(options.min, Math.min(options.max, Math.floor(parsed)));
|
|
59
|
+
return normalized;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
function readOptionalAction(payload, ...keys) {
|
|
64
|
+
for (const key of keys) {
|
|
65
|
+
const raw = payload[key];
|
|
66
|
+
if (typeof raw !== "string")
|
|
67
|
+
continue;
|
|
68
|
+
const normalized = raw.trim().toLowerCase();
|
|
69
|
+
if (normalized === "approve" || normalized === "reject") {
|
|
70
|
+
return normalized;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
function readOptionalPolicy(payload, ...keys) {
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
const raw = payload[key];
|
|
78
|
+
if (typeof raw !== "string")
|
|
79
|
+
continue;
|
|
80
|
+
const normalized = raw.trim().toLowerCase();
|
|
81
|
+
if (normalized === "contextual" ||
|
|
82
|
+
normalized === "approve_non_blocking" ||
|
|
83
|
+
normalized === "defer_non_blocking") {
|
|
84
|
+
return normalized;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
function readOptionalBlockingBehavior(payload, ...keys) {
|
|
90
|
+
for (const key of keys) {
|
|
91
|
+
const raw = payload[key];
|
|
92
|
+
if (typeof raw !== "string")
|
|
93
|
+
continue;
|
|
94
|
+
const normalized = raw.trim().toLowerCase();
|
|
95
|
+
if (normalized === "require_human" ||
|
|
96
|
+
normalized === "guarded_auto_resolve_then_human") {
|
|
97
|
+
return normalized;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
43
102
|
function normalizeRuntimeSettingsPatch(payload) {
|
|
44
103
|
const runtime = toRecord(payload.runtime_settings ?? payload.runtimeSettings);
|
|
45
104
|
const patch = {};
|
|
@@ -61,6 +120,34 @@ function normalizeRuntimeSettingsPatch(payload) {
|
|
|
61
120
|
patch.decision_auto_resolve_guarded_enabled =
|
|
62
121
|
decisionAutoResolveGuardedEnabled;
|
|
63
122
|
}
|
|
123
|
+
const questionAutoAnswerEnabled = readOptionalBoolean(runtime, "question_auto_answer_enabled", "questionAutoAnswerEnabled");
|
|
124
|
+
if (typeof questionAutoAnswerEnabled === "boolean") {
|
|
125
|
+
patch.question_auto_answer_enabled = questionAutoAnswerEnabled;
|
|
126
|
+
}
|
|
127
|
+
const questionAutoAnswerDelaySeconds = readOptionalInteger(runtime, { min: 1, max: 900 }, "question_auto_answer_delay_seconds", "questionAutoAnswerDelaySeconds");
|
|
128
|
+
if (typeof questionAutoAnswerDelaySeconds === "number") {
|
|
129
|
+
patch.question_auto_answer_delay_seconds = questionAutoAnswerDelaySeconds;
|
|
130
|
+
}
|
|
131
|
+
const questionAutoAnswerAction = readOptionalAction(runtime, "question_auto_answer_action", "questionAutoAnswerAction");
|
|
132
|
+
if (questionAutoAnswerAction) {
|
|
133
|
+
patch.question_auto_answer_action = questionAutoAnswerAction;
|
|
134
|
+
}
|
|
135
|
+
const questionAutoAnswerTimeoutSec = readOptionalInteger(runtime, { min: 10, max: 3600 }, "question_auto_answer_timeout_sec", "questionAutoAnswerTimeoutSec");
|
|
136
|
+
if (typeof questionAutoAnswerTimeoutSec === "number") {
|
|
137
|
+
patch.question_auto_answer_timeout_sec = questionAutoAnswerTimeoutSec;
|
|
138
|
+
}
|
|
139
|
+
const questionAutoAnswerPolicy = readOptionalPolicy(runtime, "question_auto_answer_policy", "questionAutoAnswerPolicy");
|
|
140
|
+
if (questionAutoAnswerPolicy) {
|
|
141
|
+
patch.question_auto_answer_policy = questionAutoAnswerPolicy;
|
|
142
|
+
}
|
|
143
|
+
const questionBlockingBehavior = readOptionalBlockingBehavior(runtime, "question_blocking_behavior", "questionBlockingBehavior");
|
|
144
|
+
if (questionBlockingBehavior) {
|
|
145
|
+
patch.question_blocking_behavior = questionBlockingBehavior;
|
|
146
|
+
}
|
|
147
|
+
const questionPolicyVersion = readOptionalInteger(runtime, { min: 1, max: 10 }, "question_policy_version", "questionPolicyVersion");
|
|
148
|
+
if (typeof questionPolicyVersion === "number") {
|
|
149
|
+
patch.question_policy_version = questionPolicyVersion;
|
|
150
|
+
}
|
|
64
151
|
const customRunInstructions = toOptionalString(runtime.custom_run_instructions ?? runtime.customRunInstructions);
|
|
65
152
|
if (typeof customRunInstructions === "string") {
|
|
66
153
|
patch.custom_run_instructions = customRunInstructions.slice(0, 4000);
|