@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
package/dist/http/index.js
CHANGED
|
@@ -46,10 +46,11 @@ import { summarizeActivityHeadline } from "./helpers/activity-headline.js";
|
|
|
46
46
|
import { createAutoContinueEngine, } from "./helpers/auto-continue-engine.js";
|
|
47
47
|
import { createAutopilotOperations, } from "./helpers/autopilot-operations.js";
|
|
48
48
|
import { mapDecisionEntity } from "./helpers/decision-mapper.js";
|
|
49
|
-
import { idempotencyKey, stableHash } from "./helpers/hash-utils.js";
|
|
49
|
+
import { deterministicActivityId, idempotencyKey, stableHash } from "./helpers/hash-utils.js";
|
|
50
50
|
import { createCodexBinResolver, } from "./helpers/autopilot-slice-utils.js";
|
|
51
51
|
import { createLocalArtifactDetailFallbackBuilder } from "./helpers/artifact-fallback.js";
|
|
52
52
|
import { buildMissionControlGraph, deriveExecutionPolicy, dedupeStrings, isDispatchableWorkstreamStatus, isDoneStatus, isInProgressStatus, isTodoStatus, listEntitiesSafe, normalizeEntityMutationPayload, pickStringArray, resolveAutoAssignments, selectSliceTasksByScope, } from "./helpers/mission-control.js";
|
|
53
|
+
import { QueueState, LaneState, RunStatus, RunnerSource, } from "./helpers/queue-constants.js";
|
|
53
54
|
import { configureOpenClawProviderRouting, fetchBillingStatusSafe, isPidAlive, listOpenClawAgents, listOpenClawProviderModels, modelImpliesByok, normalizeOpenClawProvider, resolveAutoOpenClawProvider, resolveByokEnvOverrides, spawnOpenClawAgentTurn, stopDetachedProcess, } from "./helpers/openclaw-cli.js";
|
|
54
55
|
import { fetchKickoffContextSafe, renderKickoffMessage } from "./helpers/kickoff-context.js";
|
|
55
56
|
import { createDispatchLifecycle } from "./helpers/dispatch-lifecycle.js";
|
|
@@ -177,6 +178,9 @@ function safeErrorMessage(err) {
|
|
|
177
178
|
if (normalized.includes("failed to fetch") || normalized.includes("network")) {
|
|
178
179
|
return "network request failed";
|
|
179
180
|
}
|
|
181
|
+
if (normalized.includes("relation does not exist")) {
|
|
182
|
+
return sanitized;
|
|
183
|
+
}
|
|
180
184
|
if (normalized.includes("internal_error") || normalized.includes("internal server error")) {
|
|
181
185
|
return "temporary server issue";
|
|
182
186
|
}
|
|
@@ -380,6 +384,16 @@ function deriveStructuredActivityBucket(input) {
|
|
|
380
384
|
"nonBlockingDecisionCount",
|
|
381
385
|
]) ?? 0;
|
|
382
386
|
if (event === "autopilot_slice_result") {
|
|
387
|
+
const parsedStatus = typeof metadata?.parsed_status === "string"
|
|
388
|
+
? metadata.parsed_status.trim().toLowerCase()
|
|
389
|
+
: typeof metadata?.parsedStatus === "string"
|
|
390
|
+
? metadata.parsedStatus.trim().toLowerCase()
|
|
391
|
+
: "";
|
|
392
|
+
if (parsedStatus === "completed" && decisionRequired === false) {
|
|
393
|
+
if (artifacts > 0)
|
|
394
|
+
return "artifact";
|
|
395
|
+
return "message";
|
|
396
|
+
}
|
|
383
397
|
// Any blocked slice result needs decision-first surfacing in the Activity UX.
|
|
384
398
|
if (input.phase === "blocked")
|
|
385
399
|
return "decision";
|
|
@@ -712,26 +726,10 @@ function activityMetadataStr(metadata, keys) {
|
|
|
712
726
|
}
|
|
713
727
|
return null;
|
|
714
728
|
}
|
|
715
|
-
const SEMANTIC_ACTIVITY_EVENTS = new Set([
|
|
716
|
-
"autopilot_slice_result",
|
|
717
|
-
"auto_continue_started",
|
|
718
|
-
"auto_continue_stopped",
|
|
719
|
-
"next_up_manual_dispatch_started",
|
|
720
|
-
"autopilot_slice_mcp_handshake_failed",
|
|
721
|
-
"autopilot_slice_timeout",
|
|
722
|
-
"autopilot_slice_log_stall",
|
|
723
|
-
"auto_continue_spawn_guard_blocked",
|
|
724
|
-
"auto_continue_spawn_guard_rate_limited",
|
|
725
|
-
"autopilot_autofix_scheduled",
|
|
726
|
-
"autopilot_autofix_executed",
|
|
727
|
-
"autopilot_autofix_skipped",
|
|
728
|
-
]);
|
|
729
729
|
function semanticActivityKey(item) {
|
|
730
730
|
const metadata = asActivityMetadataRecord(item.metadata);
|
|
731
731
|
const eventRaw = metadata?.event;
|
|
732
732
|
const event = typeof eventRaw === "string" ? eventRaw.trim().toLowerCase() : "";
|
|
733
|
-
if (!event || !SEMANTIC_ACTIVITY_EVENTS.has(event))
|
|
734
|
-
return null;
|
|
735
733
|
const runLike = (typeof item.runId === "string" && item.runId.trim().length > 0
|
|
736
734
|
? item.runId.trim()
|
|
737
735
|
: null) ??
|
|
@@ -755,6 +753,8 @@ function semanticActivityKey(item) {
|
|
|
755
753
|
const stopReason = activityMetadataStr(metadata, ["stop_reason", "stopReason"]);
|
|
756
754
|
const parsedStatus = activityMetadataStr(metadata, ["parsed_status", "parsedStatus"]);
|
|
757
755
|
const title = (item.title ?? "").trim().toLowerCase();
|
|
756
|
+
if (!event && !runLike && !correlationId)
|
|
757
|
+
return null;
|
|
758
758
|
if (!runLike && !correlationId && !workstreamId && !taskId)
|
|
759
759
|
return null;
|
|
760
760
|
return [
|
|
@@ -769,6 +769,27 @@ function semanticActivityKey(item) {
|
|
|
769
769
|
title,
|
|
770
770
|
].join("|");
|
|
771
771
|
}
|
|
772
|
+
function activityRichness(item) {
|
|
773
|
+
let score = 0;
|
|
774
|
+
const meta = item.metadata;
|
|
775
|
+
if (meta)
|
|
776
|
+
score += Object.keys(meta).length;
|
|
777
|
+
if (item.summary)
|
|
778
|
+
score += 2;
|
|
779
|
+
if (item.description)
|
|
780
|
+
score += 2;
|
|
781
|
+
if (item.agentName)
|
|
782
|
+
score += 1;
|
|
783
|
+
if (item.runtimeLabel)
|
|
784
|
+
score += 1;
|
|
785
|
+
if (item.runtimeProvider)
|
|
786
|
+
score += 1;
|
|
787
|
+
if (item.phase)
|
|
788
|
+
score += 1;
|
|
789
|
+
if (item.state)
|
|
790
|
+
score += 1;
|
|
791
|
+
return score;
|
|
792
|
+
}
|
|
772
793
|
function mergeActivities(base, extra, limit) {
|
|
773
794
|
const merged = [...(base ?? []), ...(extra ?? [])].sort((a, b) => {
|
|
774
795
|
const timestampDelta = Date.parse(b.timestamp) - Date.parse(a.timestamp);
|
|
@@ -779,15 +800,28 @@ function mergeActivities(base, extra, limit) {
|
|
|
779
800
|
const deduped = [];
|
|
780
801
|
const seenIds = new Set();
|
|
781
802
|
const seenSemantic = new Set();
|
|
803
|
+
const semanticIdx = new Map();
|
|
782
804
|
for (const item of merged) {
|
|
783
|
-
if (seenIds.has(item.id))
|
|
805
|
+
if (seenIds.has(item.id)) {
|
|
806
|
+
const existingIdx = deduped.findIndex(d => d.id === item.id);
|
|
807
|
+
if (existingIdx >= 0 && activityRichness(item) > activityRichness(deduped[existingIdx])) {
|
|
808
|
+
deduped[existingIdx] = item;
|
|
809
|
+
}
|
|
784
810
|
continue;
|
|
811
|
+
}
|
|
785
812
|
seenIds.add(item.id);
|
|
786
813
|
const sk = semanticActivityKey(item);
|
|
787
|
-
if (sk && seenSemantic.has(sk))
|
|
814
|
+
if (sk && seenSemantic.has(sk)) {
|
|
815
|
+
const existingIdx = semanticIdx.get(sk);
|
|
816
|
+
if (activityRichness(item) > activityRichness(deduped[existingIdx])) {
|
|
817
|
+
deduped[existingIdx] = item;
|
|
818
|
+
}
|
|
788
819
|
continue;
|
|
789
|
-
|
|
820
|
+
}
|
|
821
|
+
if (sk) {
|
|
790
822
|
seenSemantic.add(sk);
|
|
823
|
+
semanticIdx.set(sk, deduped.length);
|
|
824
|
+
}
|
|
791
825
|
deduped.push(item);
|
|
792
826
|
if (deduped.length >= limit)
|
|
793
827
|
break;
|
|
@@ -1812,7 +1846,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
1812
1846
|
};
|
|
1813
1847
|
const codexBinResolver = createCodexBinResolver();
|
|
1814
1848
|
const resolveCodexBinInfo = () => codexBinResolver.resolveCodexBinInfo();
|
|
1815
|
-
const { autoContinueRuns, autoContinueSliceRuns, localInitiativeStatusOverrides, writeRuntimeEvent, autoContinueTickMs: AUTO_CONTINUE_TICK_MS, defaultAutoContinueTokenBudget, defaultAutoContinueMaxParallelSlices, setLocalInitiativeStatusOverride, clearLocalInitiativeStatusOverride, applyLocalInitiativeOverrides, applyLocalInitiativeOverrideToGraph, updateInitiativeAutoContinueState, stopAutoContinueRun, tickAutoContinueRun, tickAllAutoContinue, isInitiativeActiveStatus, runningAutoContinueForWorkstream, getAutoContinueLaneForWorkstream, scheduleAutoFixForWorkstream, startAutoContinueRun, } = createAutoContinueEngine({
|
|
1849
|
+
const { autoContinueRuns, autoContinueSliceRuns, localInitiativeStatusOverrides, writeRuntimeEvent, autoContinueTickMs: AUTO_CONTINUE_TICK_MS, defaultAutoContinueTokenBudget, defaultAutoContinueMaxParallelSlices, setLocalInitiativeStatusOverride, clearLocalInitiativeStatusOverride, applyLocalInitiativeOverrides, applyLocalInitiativeOverrideToGraph, updateInitiativeAutoContinueState, stopAutoContinueRun, tickAutoContinueRun, tickAllAutoContinue, isInitiativeActiveStatus, runningAutoContinueForWorkstream, getAutoContinueLaneForWorkstream, scheduleAutoFixForWorkstream, startAutoContinueRun, skipCurrentWorkstream, getCanonicalAutopilotState, } = createAutoContinueEngine({
|
|
1816
1850
|
client,
|
|
1817
1851
|
filename: __filename,
|
|
1818
1852
|
safeErrorMessage,
|
|
@@ -2173,11 +2207,11 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2173
2207
|
initiativeStatusById.set(initiativeId, override.status);
|
|
2174
2208
|
}
|
|
2175
2209
|
const queueRank = (state) => {
|
|
2176
|
-
if (state ===
|
|
2210
|
+
if (state === QueueState.RUNNING)
|
|
2177
2211
|
return 0;
|
|
2178
|
-
if (state ===
|
|
2212
|
+
if (state === QueueState.QUEUED)
|
|
2179
2213
|
return 1;
|
|
2180
|
-
if (state ===
|
|
2214
|
+
if (state === QueueState.BLOCKED)
|
|
2181
2215
|
return 2;
|
|
2182
2216
|
return 3;
|
|
2183
2217
|
};
|
|
@@ -2322,12 +2356,12 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2322
2356
|
const hasRunning = statusValues.some((status) => isInProgressStatus(status));
|
|
2323
2357
|
const hasQueued = statusValues.some((status) => status === "queued" || status === "pending");
|
|
2324
2358
|
const queueState = hasRunning
|
|
2325
|
-
?
|
|
2359
|
+
? QueueState.RUNNING
|
|
2326
2360
|
: hasBlocked
|
|
2327
|
-
?
|
|
2361
|
+
? QueueState.BLOCKED
|
|
2328
2362
|
: hasQueued
|
|
2329
|
-
?
|
|
2330
|
-
:
|
|
2363
|
+
? QueueState.QUEUED
|
|
2364
|
+
: QueueState.IDLE;
|
|
2331
2365
|
const latestRunner = [];
|
|
2332
2366
|
const latestRunnerSeen = new Set();
|
|
2333
2367
|
pushRunnerAgent(latestRunner, latestRunnerSeen, {
|
|
@@ -2341,7 +2375,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2341
2375
|
const runnerAgentId = primaryRunner?.id ?? "unassigned";
|
|
2342
2376
|
const runnerAgentName = primaryRunner?.name ?? "Unassigned";
|
|
2343
2377
|
const pinKey = `${entry.initiativeId}:${entry.workstreamId}`;
|
|
2344
|
-
if (isSuppressed(entry.initiativeId, entry.workstreamId) && queueState !==
|
|
2378
|
+
if (isSuppressed(entry.initiativeId, entry.workstreamId) && queueState !== QueueState.RUNNING) {
|
|
2345
2379
|
continue;
|
|
2346
2380
|
}
|
|
2347
2381
|
fallbackItems.push({
|
|
@@ -2360,7 +2394,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2360
2394
|
runnerAgentId,
|
|
2361
2395
|
runnerAgentName,
|
|
2362
2396
|
runnerAgents,
|
|
2363
|
-
runnerSource:
|
|
2397
|
+
runnerSource: RunnerSource.FALLBACK,
|
|
2364
2398
|
queueState,
|
|
2365
2399
|
blockReason: hasBlocked
|
|
2366
2400
|
? entry.blockers[0] ??
|
|
@@ -2545,7 +2579,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2545
2579
|
: [];
|
|
2546
2580
|
const runScopedToCurrentWorkstream = scopedAllowedWorkstreams.length === 1 &&
|
|
2547
2581
|
scopedAllowedWorkstreams[0] === workstream.id &&
|
|
2548
|
-
autoContinueRun?.status ===
|
|
2582
|
+
autoContinueRun?.status === RunStatus.RUNNING;
|
|
2549
2583
|
const activeRunIds = Array.isArray(autoContinueRun?.activeSliceRunIds)
|
|
2550
2584
|
? autoContinueRun.activeSliceRunIds
|
|
2551
2585
|
.filter((id) => typeof id === "string" && id.trim().length > 0)
|
|
@@ -2595,20 +2629,26 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2595
2629
|
const sliceMilestoneId = defaultScope === "milestone"
|
|
2596
2630
|
? scopeSelection.milestoneIds[0] ?? pin?.preferredMilestoneId ?? null
|
|
2597
2631
|
: null;
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2632
|
+
// Only mark as "running" when the lane is actively executing a slice.
|
|
2633
|
+
// runScopedToCurrentWorkstream means the initiative run targets this
|
|
2634
|
+
// workstream, but the lane may be idle between slices — don't conflate
|
|
2635
|
+
// "scoped to a running initiative" with "actively executing".
|
|
2636
|
+
const laneIsActivelyRunning = laneState === LaneState.RUNNING ||
|
|
2637
|
+
(runScopedToCurrentWorkstream && activeRunIds.length > 0);
|
|
2638
|
+
let queueState = laneIsActivelyRunning
|
|
2639
|
+
? QueueState.RUNNING
|
|
2640
|
+
: candidateTask
|
|
2641
|
+
? QueueState.QUEUED
|
|
2642
|
+
: runScopedToCurrentWorkstream
|
|
2643
|
+
? QueueState.QUEUED
|
|
2644
|
+
: QueueState.IDLE;
|
|
2605
2645
|
let blockReason = null;
|
|
2606
|
-
if (laneState ===
|
|
2607
|
-
queueState =
|
|
2646
|
+
if (laneState === LaneState.BLOCKED) {
|
|
2647
|
+
queueState = QueueState.BLOCKED;
|
|
2608
2648
|
blockReason = autoContinueLane?.blockedReason ?? "Blocked";
|
|
2609
2649
|
}
|
|
2610
|
-
else if (laneState ===
|
|
2611
|
-
queueState =
|
|
2650
|
+
else if (laneState === LaneState.WAITING_DEPENDENCY) {
|
|
2651
|
+
queueState = QueueState.BLOCKED;
|
|
2612
2652
|
if (Array.isArray(autoContinueLane?.waitingOnWorkstreamIds) &&
|
|
2613
2653
|
autoContinueLane.waitingOnWorkstreamIds.length > 0) {
|
|
2614
2654
|
const waitingTitles = autoContinueLane.waitingOnWorkstreamIds
|
|
@@ -2626,12 +2666,12 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2626
2666
|
blockReason = "Waiting on dependency workstreams";
|
|
2627
2667
|
}
|
|
2628
2668
|
}
|
|
2629
|
-
else if (laneState ===
|
|
2630
|
-
queueState =
|
|
2669
|
+
else if (laneState === LaneState.RATE_LIMITED) {
|
|
2670
|
+
queueState = QueueState.BLOCKED;
|
|
2631
2671
|
blockReason = autoContinueLane?.blockedReason ?? "Rate-limited";
|
|
2632
2672
|
}
|
|
2633
2673
|
if (!autoContinueRun && !readyTask && candidateTask) {
|
|
2634
|
-
queueState =
|
|
2674
|
+
queueState = QueueState.BLOCKED;
|
|
2635
2675
|
const blockedDeps = candidateTask.dependencyIds
|
|
2636
2676
|
.map((depId) => nodeById.get(depId))
|
|
2637
2677
|
.filter((dependency) => Boolean(dependency && !isDoneStatus(dependency.status)))
|
|
@@ -2649,7 +2689,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2649
2689
|
if (!candidateTask && !autoContinueRun && !pin) {
|
|
2650
2690
|
continue;
|
|
2651
2691
|
}
|
|
2652
|
-
if (isSuppressed(initiativeId, workstream.id) && queueState !==
|
|
2692
|
+
if (isSuppressed(initiativeId, workstream.id) && queueState !== QueueState.RUNNING) {
|
|
2653
2693
|
continue;
|
|
2654
2694
|
}
|
|
2655
2695
|
runningWorkstreams.add(workstream.id);
|
|
@@ -2684,10 +2724,10 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2684
2724
|
}
|
|
2685
2725
|
const runnerAgents = assignedRunnerAgents.length > 0 ? assignedRunnerAgents : inferredRunnerAgents;
|
|
2686
2726
|
const runnerSource = assignedRunnerAgents.length > 0
|
|
2687
|
-
?
|
|
2727
|
+
? RunnerSource.ASSIGNED
|
|
2688
2728
|
: runnerAgents.length > 0
|
|
2689
|
-
?
|
|
2690
|
-
:
|
|
2729
|
+
? RunnerSource.INFERRED
|
|
2730
|
+
: RunnerSource.FALLBACK;
|
|
2691
2731
|
const primaryRunner = runnerAgents[0] ?? null;
|
|
2692
2732
|
const runnerAgentId = primaryRunner?.id ?? "unassigned";
|
|
2693
2733
|
const runnerAgentName = primaryRunner?.name ?? "Unassigned";
|
|
@@ -2781,14 +2821,14 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2781
2821
|
: activeTaskId
|
|
2782
2822
|
? [activeTaskId]
|
|
2783
2823
|
: [];
|
|
2784
|
-
const queueState = laneState ===
|
|
2785
|
-
?
|
|
2786
|
-
: laneState ===
|
|
2787
|
-
laneState ===
|
|
2788
|
-
laneState ===
|
|
2789
|
-
?
|
|
2790
|
-
:
|
|
2791
|
-
if (isSuppressed(initiativeId, workstream.id) && queueState !==
|
|
2824
|
+
const queueState = laneState === LaneState.RUNNING
|
|
2825
|
+
? QueueState.RUNNING
|
|
2826
|
+
: laneState === LaneState.BLOCKED ||
|
|
2827
|
+
laneState === LaneState.WAITING_DEPENDENCY ||
|
|
2828
|
+
laneState === LaneState.RATE_LIMITED
|
|
2829
|
+
? QueueState.BLOCKED
|
|
2830
|
+
: QueueState.QUEUED;
|
|
2831
|
+
if (isSuppressed(initiativeId, workstream.id) && queueState !== QueueState.RUNNING) {
|
|
2792
2832
|
continue;
|
|
2793
2833
|
}
|
|
2794
2834
|
const runRunnerAgents = [];
|
|
@@ -2814,9 +2854,9 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
2814
2854
|
runnerAgentId: runPrimaryRunner?.id ?? "unassigned",
|
|
2815
2855
|
runnerAgentName: runPrimaryRunner?.name ?? "Unassigned",
|
|
2816
2856
|
runnerAgents: runRunnerAgents,
|
|
2817
|
-
runnerSource: runPrimaryRunner ?
|
|
2857
|
+
runnerSource: runPrimaryRunner ? RunnerSource.INFERRED : RunnerSource.FALLBACK,
|
|
2818
2858
|
queueState,
|
|
2819
|
-
blockReason: queueState ===
|
|
2859
|
+
blockReason: queueState === QueueState.BLOCKED
|
|
2820
2860
|
? lane?.blockedReason ?? "Blocked"
|
|
2821
2861
|
: null,
|
|
2822
2862
|
isPinned: Boolean(pinnedByKey.get(`${initiativeId}:${workstream.id}`)),
|
|
@@ -3240,10 +3280,11 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3240
3280
|
broadcastRuntimeSse("runtime.updated", runtimeRecord);
|
|
3241
3281
|
}
|
|
3242
3282
|
clearSnapshotResponseCache();
|
|
3283
|
+
const activityId = deterministicActivityId("run_completed", normalizedRunId, nowIso, runtimeRecord?.agentId ?? existingRun?.agentId ?? null, "dashboard_run_mark_completed");
|
|
3243
3284
|
try {
|
|
3244
3285
|
appendActivityItems([
|
|
3245
3286
|
{
|
|
3246
|
-
id:
|
|
3287
|
+
id: activityId,
|
|
3247
3288
|
type: "run_completed",
|
|
3248
3289
|
title: message,
|
|
3249
3290
|
description: reason,
|
|
@@ -3275,7 +3316,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3275
3316
|
try {
|
|
3276
3317
|
const outboxSessionId = runtimeRecord?.initiativeId ?? existingRun?.initiativeId ?? normalizedRunId;
|
|
3277
3318
|
const outboxActivityItem = {
|
|
3278
|
-
id:
|
|
3319
|
+
id: activityId,
|
|
3279
3320
|
type: "run_completed",
|
|
3280
3321
|
title: message,
|
|
3281
3322
|
description: reason,
|
|
@@ -3376,6 +3417,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3376
3417
|
dispatchFallbackWorkstreamTurn,
|
|
3377
3418
|
tickAutoContinueRun,
|
|
3378
3419
|
stopAutoContinueRun,
|
|
3420
|
+
skipCurrentWorkstream,
|
|
3379
3421
|
updateInitiativeAutoContinueState,
|
|
3380
3422
|
tickAllAutoContinue,
|
|
3381
3423
|
scheduleAutoFixForWorkstream,
|
|
@@ -3551,6 +3593,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3551
3593
|
},
|
|
3552
3594
|
runAction: (runId, action, input) => client.runAction(runId, action, input),
|
|
3553
3595
|
listChatThreads: ({ commandCenterId, initiativeId, limit, offset }) => listChatThreads({ commandCenterId, initiativeId, limit, offset }),
|
|
3596
|
+
getCanonicalAutopilotState,
|
|
3554
3597
|
sendJson,
|
|
3555
3598
|
});
|
|
3556
3599
|
registerRuntimeHookRoutes(apiRouter, {
|
|
@@ -3,7 +3,7 @@ type JsonRecord = Record<string, unknown>;
|
|
|
3
3
|
type RegisterChatRoutesDeps<TReq, TRes> = {
|
|
4
4
|
parseJsonRequest: (req: TReq) => Promise<JsonRecord>;
|
|
5
5
|
pickString: (input: Record<string, unknown>, keys: string[]) => string | null;
|
|
6
|
-
parsePositiveInt: (raw: string | null, fallback: number) => number;
|
|
6
|
+
parsePositiveInt: (raw: string | null, fallback: number, max?: number) => number;
|
|
7
7
|
emitActivitySafe?: (input: {
|
|
8
8
|
initiativeId: string | null;
|
|
9
9
|
sourceClient?: string;
|
package/dist/http/routes/chat.js
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
1
|
import { getChatThread, linkChatThreadScope, listChatThreads, recordChatLaunch, sendChatMessage, updateChatLaunchStatus, } from "../../chat-store.js";
|
|
2
|
-
|
|
3
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
4
|
-
return null;
|
|
5
|
-
return value;
|
|
6
|
-
}
|
|
7
|
-
function asStringArray(value) {
|
|
8
|
-
if (!Array.isArray(value))
|
|
9
|
-
return [];
|
|
10
|
-
const out = [];
|
|
11
|
-
const seen = new Set();
|
|
12
|
-
for (const entry of value) {
|
|
13
|
-
if (typeof entry !== "string")
|
|
14
|
-
continue;
|
|
15
|
-
const trimmed = entry.trim();
|
|
16
|
-
if (!trimmed || seen.has(trimmed))
|
|
17
|
-
continue;
|
|
18
|
-
seen.add(trimmed);
|
|
19
|
-
out.push(trimmed);
|
|
20
|
-
}
|
|
21
|
-
return out;
|
|
22
|
-
}
|
|
2
|
+
import { asRecord, asStringArray } from "../../lib/type-coercion.js";
|
|
23
3
|
function normalizeStatus(value) {
|
|
24
4
|
const normalized = (value ?? "").trim().toLowerCase();
|
|
25
5
|
if (normalized === "requested" ||
|
|
@@ -181,8 +161,8 @@ export function registerChatRoutes(router, deps) {
|
|
|
181
161
|
const initiativeId = query.get("initiative_id") ?? query.get("initiative");
|
|
182
162
|
const searchQuery = query.get("query") ?? query.get("q");
|
|
183
163
|
const status = query.get("status");
|
|
184
|
-
const limit = deps.parsePositiveInt(query.get("limit"), 60);
|
|
185
|
-
const offset = deps.parsePositiveInt(query.get("offset"), 0);
|
|
164
|
+
const limit = deps.parsePositiveInt(query.get("limit"), 60, 200);
|
|
165
|
+
const offset = deps.parsePositiveInt(query.get("offset"), 0, 10000);
|
|
186
166
|
sendThreadList(deps, res, {
|
|
187
167
|
commandCenterId: commandCenterId?.trim() ?? null,
|
|
188
168
|
initiativeId: initiativeId?.trim() ?? null,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveWorkspaceScope, workspaceScopeFromHeaders, } from "../helpers/workspace-scope.js";
|
|
2
|
+
import { deriveInitiativeLifecycleStatus } from "../helpers/mission-control.js";
|
|
2
3
|
const WORKSTREAM_REASSIGNMENT_FIELDS = [
|
|
3
4
|
"domain",
|
|
4
5
|
"role",
|
|
@@ -38,6 +39,59 @@ function toObjectArray(input) {
|
|
|
38
39
|
return [];
|
|
39
40
|
return input.filter((item) => Boolean(item) && typeof item === "object");
|
|
40
41
|
}
|
|
42
|
+
async function reconcileInitiativeStatusesFromWorkstreams(deps, rows, input) {
|
|
43
|
+
if (rows.length === 0)
|
|
44
|
+
return rows;
|
|
45
|
+
const initiativeIds = new Set(rows
|
|
46
|
+
.map((row) => deps.pickString(row, ["id"]))
|
|
47
|
+
.filter((value) => Boolean(value)));
|
|
48
|
+
if (initiativeIds.size === 0)
|
|
49
|
+
return rows;
|
|
50
|
+
const scopedInitiativeId = input.initiativeId?.trim() ?? "";
|
|
51
|
+
const scopedWorkspaceId = input.workspaceId?.trim() ?? "";
|
|
52
|
+
if (!scopedInitiativeId && !scopedWorkspaceId) {
|
|
53
|
+
// Avoid unscoped global workstream scans.
|
|
54
|
+
return rows;
|
|
55
|
+
}
|
|
56
|
+
const filters = { limit: 2000 };
|
|
57
|
+
if (scopedInitiativeId) {
|
|
58
|
+
filters.initiative_id = scopedInitiativeId;
|
|
59
|
+
}
|
|
60
|
+
if (scopedWorkspaceId) {
|
|
61
|
+
filters.workspace_id = scopedWorkspaceId;
|
|
62
|
+
filters.command_center_id = scopedWorkspaceId;
|
|
63
|
+
}
|
|
64
|
+
const response = await deps.client.listEntities("workstream", filters);
|
|
65
|
+
const workstreamRows = toObjectArray(response && typeof response === "object"
|
|
66
|
+
? response.data
|
|
67
|
+
: []);
|
|
68
|
+
const childStatusesByInitiative = new Map();
|
|
69
|
+
for (const workstreamRow of workstreamRows) {
|
|
70
|
+
const initiativeId = deps.pickString(workstreamRow, ["initiative_id", "initiativeId"]);
|
|
71
|
+
if (!initiativeId || !initiativeIds.has(initiativeId))
|
|
72
|
+
continue;
|
|
73
|
+
const status = deps.pickString(workstreamRow, ["status"]) ?? "todo";
|
|
74
|
+
const statuses = childStatusesByInitiative.get(initiativeId) ?? [];
|
|
75
|
+
statuses.push(status);
|
|
76
|
+
childStatusesByInitiative.set(initiativeId, statuses);
|
|
77
|
+
}
|
|
78
|
+
return rows.map((row) => {
|
|
79
|
+
const initiativeId = deps.pickString(row, ["id"]);
|
|
80
|
+
const status = deps.pickString(row, ["status"]);
|
|
81
|
+
if (!initiativeId || !status)
|
|
82
|
+
return row;
|
|
83
|
+
const childStatuses = childStatusesByInitiative.get(initiativeId) ?? [];
|
|
84
|
+
if (childStatuses.length === 0)
|
|
85
|
+
return row;
|
|
86
|
+
const normalized = deriveInitiativeLifecycleStatus(status, childStatuses);
|
|
87
|
+
if (normalized === status)
|
|
88
|
+
return row;
|
|
89
|
+
return {
|
|
90
|
+
...row,
|
|
91
|
+
status: normalized,
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
41
95
|
export function registerEntitiesRoutes(router, deps) {
|
|
42
96
|
router.add("POST", "entities", async ({ req, res }) => {
|
|
43
97
|
try {
|
|
@@ -305,13 +359,17 @@ export function registerEntitiesRoutes(router, deps) {
|
|
|
305
359
|
return rowScope.trim() === workspaceScopeId;
|
|
306
360
|
})
|
|
307
361
|
: searchedRows;
|
|
362
|
+
const reconciledRows = await reconcileInitiativeStatusesFromWorkstreams(deps, rows, {
|
|
363
|
+
initiativeId: id,
|
|
364
|
+
workspaceId: workspaceScopeId,
|
|
365
|
+
}).catch(() => rows);
|
|
308
366
|
deps.sendJson(res, 200, {
|
|
309
367
|
...payload,
|
|
310
|
-
data: deps.applyLocalInitiativeOverrides(
|
|
368
|
+
data: deps.applyLocalInitiativeOverrides(reconciledRows),
|
|
311
369
|
pagination: payload.pagination && typeof payload.pagination === "object"
|
|
312
370
|
? {
|
|
313
371
|
...payload.pagination,
|
|
314
|
-
total:
|
|
372
|
+
total: reconciledRows.length,
|
|
315
373
|
}
|
|
316
374
|
: payload.pagination,
|
|
317
375
|
});
|
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
function resolveEntityActionStatus(entityType, entityAction) {
|
|
2
|
+
const normalizedType = entityType.trim().toLowerCase();
|
|
3
|
+
const normalizedAction = entityAction.trim().toLowerCase();
|
|
4
|
+
const defaultStatusMap = {
|
|
5
|
+
start: "in_progress",
|
|
6
|
+
complete: "done",
|
|
7
|
+
block: "blocked",
|
|
8
|
+
unblock: "in_progress",
|
|
9
|
+
pause: "paused",
|
|
10
|
+
resume: "active",
|
|
11
|
+
};
|
|
12
|
+
const statusMapByEntityType = {
|
|
13
|
+
initiative: {
|
|
14
|
+
start: "active",
|
|
15
|
+
complete: "completed",
|
|
16
|
+
block: "blocked",
|
|
17
|
+
unblock: "active",
|
|
18
|
+
pause: "paused",
|
|
19
|
+
resume: "active",
|
|
20
|
+
},
|
|
21
|
+
workstream: {
|
|
22
|
+
start: "active",
|
|
23
|
+
complete: "completed",
|
|
24
|
+
block: "blocked",
|
|
25
|
+
unblock: "active",
|
|
26
|
+
pause: "paused",
|
|
27
|
+
resume: "active",
|
|
28
|
+
},
|
|
29
|
+
milestone: {
|
|
30
|
+
start: "in_progress",
|
|
31
|
+
complete: "completed",
|
|
32
|
+
block: "at_risk",
|
|
33
|
+
unblock: "in_progress",
|
|
34
|
+
pause: "planned",
|
|
35
|
+
resume: "in_progress",
|
|
36
|
+
},
|
|
37
|
+
task: {
|
|
38
|
+
start: "in_progress",
|
|
39
|
+
complete: "done",
|
|
40
|
+
block: "blocked",
|
|
41
|
+
unblock: "in_progress",
|
|
42
|
+
pause: "todo",
|
|
43
|
+
resume: "in_progress",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
const map = statusMapByEntityType[normalizedType] ?? defaultStatusMap;
|
|
47
|
+
return map[normalizedAction] ?? null;
|
|
48
|
+
}
|
|
1
49
|
export function registerEntityDynamicRoutes(router, deps) {
|
|
2
50
|
router.add("*", "entities/*", async ({ req, res, path }) => {
|
|
3
51
|
const method = (req.method ?? "GET").toUpperCase();
|
|
@@ -137,15 +185,7 @@ export function registerEntityDynamicRoutes(router, deps) {
|
|
|
137
185
|
}
|
|
138
186
|
return;
|
|
139
187
|
}
|
|
140
|
-
const
|
|
141
|
-
start: "in_progress",
|
|
142
|
-
complete: "done",
|
|
143
|
-
block: "blocked",
|
|
144
|
-
unblock: "in_progress",
|
|
145
|
-
pause: "paused",
|
|
146
|
-
resume: "active",
|
|
147
|
-
};
|
|
148
|
-
const newStatus = statusMap[entityAction];
|
|
188
|
+
const newStatus = resolveEntityActionStatus(entityType, entityAction);
|
|
149
189
|
if (!newStatus) {
|
|
150
190
|
deps.sendJson(res, 400, {
|
|
151
191
|
error: `Unknown entity action: ${entityAction}`,
|
|
@@ -14,7 +14,7 @@ type SnapshotPersistState = {
|
|
|
14
14
|
lastPersistAt: number;
|
|
15
15
|
};
|
|
16
16
|
type LiveSnapshotRoutesDeps<TReq, TRes> = {
|
|
17
|
-
parsePositiveInt: (raw: string | null, fallback: number) => number;
|
|
17
|
+
parsePositiveInt: (raw: string | null, fallback: number, max?: number) => number;
|
|
18
18
|
readSnapshotResponseCache: (key: string) => Record<string, unknown> | null;
|
|
19
19
|
writeSnapshotResponseCache: (key: string, payload: Record<string, unknown>) => void;
|
|
20
20
|
safeErrorMessage: (err: unknown) => string;
|
|
@@ -131,6 +131,15 @@ type LiveSnapshotRoutesDeps<TReq, TRes> = {
|
|
|
131
131
|
total: number;
|
|
132
132
|
updatedAt: string;
|
|
133
133
|
};
|
|
134
|
+
getCanonicalAutopilotState?: (initiativeId: string) => {
|
|
135
|
+
state: "idle" | "running" | "blocked" | "stopping";
|
|
136
|
+
reason: string | null;
|
|
137
|
+
activeRunId: string | null;
|
|
138
|
+
activeWorkstreamId: string | null;
|
|
139
|
+
activeWorkstreamTitle: string | null;
|
|
140
|
+
queueHeadTitle: string | null;
|
|
141
|
+
lastTransitionAt: string;
|
|
142
|
+
} | null;
|
|
134
143
|
sendJson: (res: TRes, status: number, payload: unknown) => void;
|
|
135
144
|
};
|
|
136
145
|
export declare function registerLiveSnapshotRoutes<TReq, TRes>(router: Router<Record<string, never>, TReq, TRes>, deps: LiveSnapshotRoutesDeps<TReq, TRes>): void;
|
|
@@ -231,9 +231,9 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
231
231
|
};
|
|
232
232
|
const headerScopeFromRequest = (req) => workspaceScopeFromHeaders(req?.headers);
|
|
233
233
|
function parseSnapshotQuery(query, headerScope) {
|
|
234
|
-
const sessionsLimit = deps.parsePositiveInt(query.get("sessionsLimit") ?? query.get("sessions_limit"), 320);
|
|
235
|
-
const activityLimit = deps.parsePositiveInt(query.get("activityLimit") ?? query.get("activity_limit"), 600);
|
|
236
|
-
const decisionsLimit = deps.parsePositiveInt(query.get("decisionsLimit") ?? query.get("decisions_limit"), 120);
|
|
234
|
+
const sessionsLimit = deps.parsePositiveInt(query.get("sessionsLimit") ?? query.get("sessions_limit"), 320, 1000);
|
|
235
|
+
const activityLimit = deps.parsePositiveInt(query.get("activityLimit") ?? query.get("activity_limit"), 600, 2000);
|
|
236
|
+
const decisionsLimit = deps.parsePositiveInt(query.get("decisionsLimit") ?? query.get("decisions_limit"), 120, 500);
|
|
237
237
|
const initiative = query.get("initiative");
|
|
238
238
|
const scope = resolveWorkspaceScope(query, headerScope, {
|
|
239
239
|
allowProjectScope: false,
|
|
@@ -450,29 +450,7 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
450
450
|
try {
|
|
451
451
|
const buffered = await deps.readOutboxItems();
|
|
452
452
|
if (buffered.length > 0) {
|
|
453
|
-
|
|
454
|
-
.sort((a, b) => {
|
|
455
|
-
const d = Date.parse(b.timestamp) - Date.parse(a.timestamp);
|
|
456
|
-
if (d !== 0)
|
|
457
|
-
return d;
|
|
458
|
-
return b.id.localeCompare(a.id);
|
|
459
|
-
})
|
|
460
|
-
.slice(0, activityLimit);
|
|
461
|
-
const deduped = [];
|
|
462
|
-
const seenIds = new Set();
|
|
463
|
-
const seenSemantic = new Set();
|
|
464
|
-
for (const item of merged) {
|
|
465
|
-
if (seenIds.has(item.id))
|
|
466
|
-
continue;
|
|
467
|
-
seenIds.add(item.id);
|
|
468
|
-
const sk = deps.semanticActivityKey(item);
|
|
469
|
-
if (sk && seenSemantic.has(sk))
|
|
470
|
-
continue;
|
|
471
|
-
if (sk)
|
|
472
|
-
seenSemantic.add(sk);
|
|
473
|
-
deduped.push(item);
|
|
474
|
-
}
|
|
475
|
-
activity = deduped;
|
|
453
|
+
activity = deps.mergeActivities(activity, buffered, activityLimit);
|
|
476
454
|
}
|
|
477
455
|
}
|
|
478
456
|
catch (err) {
|
|
@@ -500,6 +478,9 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
500
478
|
agents: agentContexts,
|
|
501
479
|
runs: runContexts,
|
|
502
480
|
});
|
|
481
|
+
// Final dedup pass after enrichment — catches items that became
|
|
482
|
+
// semantically identical after runtime/agent context decoration.
|
|
483
|
+
activity = deps.mergeActivities(activity, [], activityLimit);
|
|
503
484
|
activity = activity.filter((item) => !shouldHideActivityItem(item));
|
|
504
485
|
sessions = normalizeReportingBlockedSessions({
|
|
505
486
|
sessions,
|
|
@@ -542,6 +523,14 @@ export function registerLiveSnapshotRoutes(router, deps) {
|
|
|
542
523
|
projectId,
|
|
543
524
|
degraded: degraded.length > 0 ? degraded : undefined,
|
|
544
525
|
};
|
|
526
|
+
if (typeof deps.getCanonicalAutopilotState === "function" && initiative) {
|
|
527
|
+
try {
|
|
528
|
+
payload.autopilot = deps.getCanonicalAutopilotState(initiative) ?? null;
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
// best effort
|
|
532
|
+
}
|
|
533
|
+
}
|
|
545
534
|
if (typeof deps.listChatThreads === "function") {
|
|
546
535
|
try {
|
|
547
536
|
const listed = deps.listChatThreads({
|
|
@@ -66,6 +66,12 @@ type RegisterMissionControlActionsRoutesDeps<TReq, TRes> = {
|
|
|
66
66
|
}>;
|
|
67
67
|
tickAutoContinueRun: (run: any) => Promise<void>;
|
|
68
68
|
stopAutoContinueRun: (input: any) => Promise<void>;
|
|
69
|
+
skipCurrentWorkstream: (initiativeId: string, workstreamId: string, reason?: string) => Promise<{
|
|
70
|
+
ok: boolean;
|
|
71
|
+
skippedWorkstreamId: string;
|
|
72
|
+
nextWorkstreamId?: string;
|
|
73
|
+
nextWorkstreamTitle?: string;
|
|
74
|
+
}>;
|
|
69
75
|
updateInitiativeAutoContinueState: (input: any) => Promise<void>;
|
|
70
76
|
tickAllAutoContinue: () => Promise<void>;
|
|
71
77
|
scheduleAutoFixForWorkstream: (input: {
|