@useorgx/openclaw-plugin 0.7.8 → 0.7.15

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.
Files changed (203) hide show
  1. package/README.md +94 -122
  2. package/dashboard/dist/assets/0RUEVzJa.js +1 -0
  3. package/dashboard/dist/assets/0RUEVzJa.js.br +0 -0
  4. package/dashboard/dist/assets/0RUEVzJa.js.gz +0 -0
  5. package/dashboard/dist/assets/3TtV4moZ.js +1 -0
  6. package/dashboard/dist/assets/3TtV4moZ.js.br +0 -0
  7. package/dashboard/dist/assets/3TtV4moZ.js.gz +0 -0
  8. package/dashboard/dist/assets/3VwNyxUf.js +1 -0
  9. package/dashboard/dist/assets/3VwNyxUf.js.br +0 -0
  10. package/dashboard/dist/assets/3VwNyxUf.js.gz +0 -0
  11. package/dashboard/dist/assets/{DpuQm1oF.js → 7DhYqBrM.js} +2 -2
  12. package/dashboard/dist/assets/7DhYqBrM.js.br +0 -0
  13. package/dashboard/dist/assets/7DhYqBrM.js.gz +0 -0
  14. package/dashboard/dist/assets/{tcEHYcbW.js → BCudUvwg.js} +1 -1
  15. package/dashboard/dist/assets/BCudUvwg.js.br +0 -0
  16. package/dashboard/dist/assets/BCudUvwg.js.gz +0 -0
  17. package/dashboard/dist/assets/BV0BcV1u.js +53 -0
  18. package/dashboard/dist/assets/BV0BcV1u.js.br +0 -0
  19. package/dashboard/dist/assets/BV0BcV1u.js.gz +0 -0
  20. package/dashboard/dist/assets/BVvffj0x.js +1 -0
  21. package/dashboard/dist/assets/BVvffj0x.js.br +0 -0
  22. package/dashboard/dist/assets/BVvffj0x.js.gz +0 -0
  23. package/dashboard/dist/assets/BiOgVMED.js +1 -0
  24. package/dashboard/dist/assets/BiOgVMED.js.br +0 -0
  25. package/dashboard/dist/assets/BiOgVMED.js.gz +0 -0
  26. package/dashboard/dist/assets/BjK42gtU.js +1 -0
  27. package/dashboard/dist/assets/BjK42gtU.js.br +0 -0
  28. package/dashboard/dist/assets/BjK42gtU.js.gz +0 -0
  29. package/dashboard/dist/assets/C-MOJWHs.js +1 -0
  30. package/dashboard/dist/assets/C-MOJWHs.js.br +0 -0
  31. package/dashboard/dist/assets/C-MOJWHs.js.gz +0 -0
  32. package/dashboard/dist/assets/C91KLKit.js +1 -0
  33. package/dashboard/dist/assets/C91KLKit.js.br +0 -0
  34. package/dashboard/dist/assets/C91KLKit.js.gz +0 -0
  35. package/dashboard/dist/assets/{CnitK1MX.js → C9fvfXmS.js} +1 -1
  36. package/dashboard/dist/assets/C9fvfXmS.js.br +0 -0
  37. package/dashboard/dist/assets/C9fvfXmS.js.gz +0 -0
  38. package/dashboard/dist/assets/CFZ4Swr5.js +1 -0
  39. package/dashboard/dist/assets/CFZ4Swr5.js.br +0 -0
  40. package/dashboard/dist/assets/CFZ4Swr5.js.gz +0 -0
  41. package/dashboard/dist/assets/{77gGFBt6.js → CGj8kRhg.js} +1 -1
  42. package/dashboard/dist/assets/CGj8kRhg.js.br +0 -0
  43. package/dashboard/dist/assets/CGj8kRhg.js.gz +0 -0
  44. package/dashboard/dist/assets/CJjEAGFN.js +1 -0
  45. package/dashboard/dist/assets/CJjEAGFN.js.br +0 -0
  46. package/dashboard/dist/assets/CJjEAGFN.js.gz +0 -0
  47. package/dashboard/dist/assets/CKrH5fYO.js +1 -0
  48. package/dashboard/dist/assets/CKrH5fYO.js.br +0 -0
  49. package/dashboard/dist/assets/CKrH5fYO.js.gz +0 -0
  50. package/dashboard/dist/assets/CMTTPXch.js +1 -0
  51. package/dashboard/dist/assets/CMTTPXch.js.br +0 -0
  52. package/dashboard/dist/assets/CMTTPXch.js.gz +0 -0
  53. package/dashboard/dist/assets/CSlBSRyv.js +1 -0
  54. package/dashboard/dist/assets/CSlBSRyv.js.br +0 -0
  55. package/dashboard/dist/assets/CSlBSRyv.js.gz +0 -0
  56. package/dashboard/dist/assets/CnPC783_.js +1 -0
  57. package/dashboard/dist/assets/CnPC783_.js.br +0 -0
  58. package/dashboard/dist/assets/CnPC783_.js.gz +0 -0
  59. package/dashboard/dist/assets/Ctw95IkC.js +1 -0
  60. package/dashboard/dist/assets/Ctw95IkC.js.br +0 -0
  61. package/dashboard/dist/assets/Ctw95IkC.js.gz +0 -0
  62. package/dashboard/dist/assets/DHz-aQPw.js +1 -0
  63. package/dashboard/dist/assets/DHz-aQPw.js.br +0 -0
  64. package/dashboard/dist/assets/DHz-aQPw.js.gz +0 -0
  65. package/dashboard/dist/assets/DNX2foSJ.css +1 -0
  66. package/dashboard/dist/assets/DNX2foSJ.css.br +0 -0
  67. package/dashboard/dist/assets/DNX2foSJ.css.gz +0 -0
  68. package/dashboard/dist/assets/Dj2k1r16.js +8 -0
  69. package/dashboard/dist/assets/Dj2k1r16.js.br +0 -0
  70. package/dashboard/dist/assets/Dj2k1r16.js.gz +0 -0
  71. package/dashboard/dist/assets/DxUw4FMR.js +212 -0
  72. package/dashboard/dist/assets/DxUw4FMR.js.br +0 -0
  73. package/dashboard/dist/assets/DxUw4FMR.js.gz +0 -0
  74. package/dashboard/dist/assets/T2NFtzAv.js +1 -0
  75. package/dashboard/dist/assets/T2NFtzAv.js.br +0 -0
  76. package/dashboard/dist/assets/T2NFtzAv.js.gz +0 -0
  77. package/dashboard/dist/assets/cX2e-TLi.js +1 -0
  78. package/dashboard/dist/assets/cX2e-TLi.js.br +0 -0
  79. package/dashboard/dist/assets/cX2e-TLi.js.gz +0 -0
  80. package/dashboard/dist/assets/eeHXe_OQ.js +9 -0
  81. package/dashboard/dist/assets/eeHXe_OQ.js.br +0 -0
  82. package/dashboard/dist/assets/eeHXe_OQ.js.gz +0 -0
  83. package/dashboard/dist/assets/{DxKG5zy8.js → gZr_xKlA.js} +2 -2
  84. package/dashboard/dist/assets/gZr_xKlA.js.br +0 -0
  85. package/dashboard/dist/assets/gZr_xKlA.js.gz +0 -0
  86. package/dashboard/dist/brand/control-tower.png +0 -0
  87. package/dashboard/dist/brand/design-codex.png +0 -0
  88. package/dashboard/dist/brand/engineering-autopilot.png +0 -0
  89. package/dashboard/dist/brand/launch-captain.png +0 -0
  90. package/dashboard/dist/brand/orgx-logo.png +0 -0
  91. package/dashboard/dist/brand/pipeline-intelligence.png +0 -0
  92. package/dashboard/dist/brand/product-orchestrator.png +0 -0
  93. package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
  94. package/dashboard/dist/index.html +8 -6
  95. package/dashboard/dist/index.html.br +0 -0
  96. package/dashboard/dist/index.html.gz +0 -0
  97. package/dist/hash-utils.d.ts +1 -0
  98. package/dist/hash-utils.js +4 -0
  99. package/dist/http/helpers/auto-continue-engine.d.ts +36 -0
  100. package/dist/http/helpers/auto-continue-engine.js +198 -75
  101. package/dist/http/helpers/autopilot-runtime.d.ts +1 -0
  102. package/dist/http/helpers/autopilot-runtime.js +31 -3
  103. package/dist/http/helpers/autopilot-slice-utils.d.ts +10 -0
  104. package/dist/http/helpers/autopilot-slice-utils.js +158 -54
  105. package/dist/http/helpers/hash-utils.d.ts +1 -1
  106. package/dist/http/helpers/hash-utils.js +1 -1
  107. package/dist/http/helpers/humanize-slice-failure.d.ts +35 -0
  108. package/dist/http/helpers/humanize-slice-failure.js +137 -0
  109. package/dist/http/helpers/mission-control.d.ts +1 -0
  110. package/dist/http/helpers/mission-control.js +73 -7
  111. package/dist/http/helpers/queue-constants.d.ts +37 -0
  112. package/dist/http/helpers/queue-constants.js +34 -0
  113. package/dist/http/helpers/slice-experience-v2.js +2 -5
  114. package/dist/http/helpers/slice-run-projections.js +2 -5
  115. package/dist/http/helpers/workspace-scope.js +4 -3
  116. package/dist/http/index.js +166 -63
  117. package/dist/http/routes/chat.js +1 -21
  118. package/dist/http/routes/live-misc.js +9 -2
  119. package/dist/http/routes/live-snapshot.js +14 -27
  120. package/dist/http/routes/mission-control-actions.js +7 -18
  121. package/dist/http/routes/mission-control-read.d.ts +1 -0
  122. package/dist/http/routes/mission-control-read.js +14 -56
  123. package/dist/index.d.ts +8 -1
  124. package/dist/index.js +21 -1
  125. package/dist/lib/type-coercion.d.ts +10 -0
  126. package/dist/lib/type-coercion.js +82 -0
  127. package/dist/mcp-http-handler.js +14 -2
  128. package/dist/openclaw.plugin.json +1 -1
  129. package/dist/services/experiment-randomization.js +9 -2
  130. package/dist/tools/core-tools.d.ts +27 -0
  131. package/dist/tools/core-tools.js +89 -0
  132. package/openclaw.plugin.json +1 -1
  133. package/package.json +3 -2
  134. package/dashboard/dist/assets/77gGFBt6.js.br +0 -0
  135. package/dashboard/dist/assets/77gGFBt6.js.gz +0 -0
  136. package/dashboard/dist/assets/BBpTN_SR.js +0 -1
  137. package/dashboard/dist/assets/BBpTN_SR.js.br +0 -0
  138. package/dashboard/dist/assets/BBpTN_SR.js.gz +0 -0
  139. package/dashboard/dist/assets/BJgZIVUQ.js +0 -53
  140. package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
  141. package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
  142. package/dashboard/dist/assets/BTAEErUY.js +0 -1
  143. package/dashboard/dist/assets/BTAEErUY.js.br +0 -0
  144. package/dashboard/dist/assets/BTAEErUY.js.gz +0 -0
  145. package/dashboard/dist/assets/BVShoyjA.js +0 -1
  146. package/dashboard/dist/assets/BVShoyjA.js.br +0 -0
  147. package/dashboard/dist/assets/BVShoyjA.js.gz +0 -0
  148. package/dashboard/dist/assets/BgcAY5rE.js +0 -1
  149. package/dashboard/dist/assets/BgcAY5rE.js.br +0 -0
  150. package/dashboard/dist/assets/BgcAY5rE.js.gz +0 -0
  151. package/dashboard/dist/assets/C-KIc3Wc.js +0 -1
  152. package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
  153. package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
  154. package/dashboard/dist/assets/C-PAoJF-.js +0 -1
  155. package/dashboard/dist/assets/C-PAoJF-.js.br +0 -0
  156. package/dashboard/dist/assets/C-PAoJF-.js.gz +0 -0
  157. package/dashboard/dist/assets/C0nA-iUG.js +0 -1
  158. package/dashboard/dist/assets/C0nA-iUG.js.br +0 -0
  159. package/dashboard/dist/assets/C0nA-iUG.js.gz +0 -0
  160. package/dashboard/dist/assets/C6GO-FKy.js +0 -1
  161. package/dashboard/dist/assets/C6GO-FKy.js.br +0 -0
  162. package/dashboard/dist/assets/C6GO-FKy.js.gz +0 -0
  163. package/dashboard/dist/assets/CFwPph5U.js +0 -1
  164. package/dashboard/dist/assets/CFwPph5U.js.br +0 -0
  165. package/dashboard/dist/assets/CFwPph5U.js.gz +0 -0
  166. package/dashboard/dist/assets/CL_wXqR7.js +0 -1
  167. package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
  168. package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
  169. package/dashboard/dist/assets/CPjsbbgZ.js +0 -212
  170. package/dashboard/dist/assets/CPjsbbgZ.js.br +0 -0
  171. package/dashboard/dist/assets/CPjsbbgZ.js.gz +0 -0
  172. package/dashboard/dist/assets/CSr2ZnTV.js +0 -1
  173. package/dashboard/dist/assets/CSr2ZnTV.js.br +0 -0
  174. package/dashboard/dist/assets/CSr2ZnTV.js.gz +0 -0
  175. package/dashboard/dist/assets/CgQDT6yL.js +0 -1
  176. package/dashboard/dist/assets/CgQDT6yL.js.br +0 -0
  177. package/dashboard/dist/assets/CgQDT6yL.js.gz +0 -0
  178. package/dashboard/dist/assets/CnitK1MX.js.br +0 -0
  179. package/dashboard/dist/assets/CnitK1MX.js.gz +0 -0
  180. package/dashboard/dist/assets/CxQ08qFN.js +0 -9
  181. package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
  182. package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
  183. package/dashboard/dist/assets/D7DHFX0D.js +0 -1
  184. package/dashboard/dist/assets/D7DHFX0D.js.br +0 -0
  185. package/dashboard/dist/assets/D7DHFX0D.js.gz +0 -0
  186. package/dashboard/dist/assets/DEip7uko.js +0 -1
  187. package/dashboard/dist/assets/DEip7uko.js.br +0 -0
  188. package/dashboard/dist/assets/DEip7uko.js.gz +0 -0
  189. package/dashboard/dist/assets/DHUSLc01.css +0 -1
  190. package/dashboard/dist/assets/DHUSLc01.css.br +0 -0
  191. package/dashboard/dist/assets/DHUSLc01.css.gz +0 -0
  192. package/dashboard/dist/assets/DOFL9l8s.js +0 -1
  193. package/dashboard/dist/assets/DOFL9l8s.js.br +0 -0
  194. package/dashboard/dist/assets/DOFL9l8s.js.gz +0 -0
  195. package/dashboard/dist/assets/DpuQm1oF.js.br +0 -0
  196. package/dashboard/dist/assets/DpuQm1oF.js.gz +0 -0
  197. package/dashboard/dist/assets/DxKG5zy8.js.br +0 -0
  198. package/dashboard/dist/assets/DxKG5zy8.js.gz +0 -0
  199. package/dashboard/dist/assets/cNrhgGc1.js +0 -8
  200. package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
  201. package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
  202. package/dashboard/dist/assets/tcEHYcbW.js.br +0 -0
  203. package/dashboard/dist/assets/tcEHYcbW.js.gz +0 -0
@@ -0,0 +1,34 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Canonical state constants for queue, lane, and related enumerations.
3
+ // Use these instead of raw string literals throughout the codebase.
4
+ // ---------------------------------------------------------------------------
5
+ /** Queue state for next-up items (workstream-level). */
6
+ export const QueueState = {
7
+ QUEUED: "queued",
8
+ RUNNING: "running",
9
+ BLOCKED: "blocked",
10
+ IDLE: "idle",
11
+ /** Used only in the API normalization layer (mission-control-read.ts). */
12
+ COMPLETED: "completed",
13
+ };
14
+ /** Auto-continue lane state (per-workstream within a run). */
15
+ export const LaneState = {
16
+ IDLE: "idle",
17
+ RUNNING: "running",
18
+ BLOCKED: "blocked",
19
+ WAITING_DEPENDENCY: "waiting_dependency",
20
+ RATE_LIMITED: "rate_limited",
21
+ COMPLETED: "completed",
22
+ };
23
+ /** Auto-continue run status. */
24
+ export const RunStatus = {
25
+ RUNNING: "running",
26
+ STOPPING: "stopping",
27
+ STOPPED: "stopped",
28
+ };
29
+ /** Runner source — how the agent was assigned. */
30
+ export const RunnerSource = {
31
+ ASSIGNED: "assigned",
32
+ INFERRED: "inferred",
33
+ FALLBACK: "fallback",
34
+ };
@@ -1,10 +1,7 @@
1
+ import { asRecord } from "../../lib/type-coercion.js";
1
2
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2
3
  const GENERIC_ACTOR_IDS = new Set(["main", "unknown", "system", "openclaw", "orgx"]);
3
- function asRecord(value) {
4
- if (!value || typeof value !== "object" || Array.isArray(value))
5
- return null;
6
- return value;
7
- }
4
+ // asRecord imported from ../../lib/type-coercion.js
8
5
  function normalizeText(value) {
9
6
  if (typeof value !== "string")
10
7
  return null;
@@ -1,3 +1,4 @@
1
+ import { asRecord } from "../../lib/type-coercion.js";
1
2
  const TERMINAL_STATES = new Set([
2
3
  "completed",
3
4
  "needs_review",
@@ -5,11 +6,7 @@ const TERMINAL_STATES = new Set([
5
6
  "archived",
6
7
  ]);
7
8
  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
- }
9
+ // asRecord imported from ../../lib/type-coercion.js
13
10
  function normalizeText(value) {
14
11
  if (typeof value !== "string")
15
12
  return null;
@@ -70,20 +70,21 @@ export function workspaceScopeFromHeaders(headers) {
70
70
  "x-orgx-command-center-id",
71
71
  "x_orgx_command_center_id",
72
72
  ]);
73
+ const centerId = readScopeValue(headers, ["center"]);
73
74
  if (workspaceId && commandCenterId && workspaceId !== commandCenterId) {
74
75
  return {
75
76
  workspace_id: workspaceId,
76
77
  command_center_id: commandCenterId,
77
- center: workspaceId,
78
+ center: centerId ?? workspaceId,
78
79
  };
79
80
  }
80
- const canonical = workspaceId ?? commandCenterId;
81
+ const canonical = workspaceId ?? commandCenterId ?? centerId;
81
82
  if (!canonical)
82
83
  return null;
83
84
  return {
84
85
  workspace_id: canonical,
85
86
  command_center_id: canonical,
86
- center: canonical,
87
+ center: centerId ?? canonical,
87
88
  };
88
89
  }
89
90
  function mergeCandidates(payloadCandidates, queryCandidates) {
@@ -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";
@@ -106,8 +107,64 @@ async function resolveSkillPackOverrides(input) {
106
107
  }
107
108
  }
108
109
  function safeErrorMessage(err) {
109
- const raw = err instanceof Error ? err.message : typeof err === "string" ? err : "";
110
- const normalized = raw.trim().toLowerCase();
110
+ const raw = err instanceof Error
111
+ ? err.message
112
+ : typeof err === "string"
113
+ ? err
114
+ : err && typeof err === "object" && "message" in err && typeof err.message === "string"
115
+ ? (err.message ?? "")
116
+ : "";
117
+ const parseStructuredMessage = (value) => {
118
+ const trimmed = value.trim();
119
+ if (!trimmed)
120
+ return null;
121
+ const parseObjectMessage = (parsed) => {
122
+ if (!parsed || typeof parsed !== "object")
123
+ return null;
124
+ const root = parsed;
125
+ const nested = root.error && typeof root.error === "object" ? root.error : null;
126
+ const envelope = nested ?? root;
127
+ return ((typeof envelope.message === "string" && envelope.message.trim()) ||
128
+ (typeof envelope.detail === "string" && envelope.detail.trim()) ||
129
+ (!nested && typeof root.error === "string" && root.error.trim()) ||
130
+ null);
131
+ };
132
+ try {
133
+ const parsed = JSON.parse(trimmed);
134
+ const direct = parseObjectMessage(parsed);
135
+ if (direct)
136
+ return direct;
137
+ }
138
+ catch {
139
+ // Continue and try extracting embedded JSON payloads.
140
+ }
141
+ const firstBrace = trimmed.indexOf("{");
142
+ const lastBrace = trimmed.lastIndexOf("}");
143
+ if (firstBrace >= 0 && lastBrace > firstBrace) {
144
+ const candidate = trimmed.slice(firstBrace, lastBrace + 1);
145
+ try {
146
+ const parsed = JSON.parse(candidate);
147
+ return parseObjectMessage(parsed);
148
+ }
149
+ catch {
150
+ return null;
151
+ }
152
+ }
153
+ return null;
154
+ };
155
+ const stripStructuredNoise = (value) => value
156
+ .replace(/"requestId"\s*:\s*"[^"]*"/gi, "")
157
+ .replace(/"timestamp"\s*:\s*"[^"]*"/gi, "")
158
+ .replace(/"docsUrl"\s*:\s*"[^"]*"/gi, "")
159
+ .replace(/\brequest[_\s-]?id[:=]\s*[\w-]+/gi, "")
160
+ .replace(/\btimestamp[:=]\s*\S+/gi, "")
161
+ .replace(/\bdocsUrl[:=]\s*\S+/gi, "")
162
+ .replace(/[{}]/g, " ")
163
+ .replace(/\s{2,}/g, " ")
164
+ .trim();
165
+ const normalizedMessage = parseStructuredMessage(raw) ?? raw;
166
+ const sanitized = stripStructuredNoise(normalizedMessage);
167
+ const normalized = sanitized.toLowerCase();
111
168
  if (normalized.length > 0) {
112
169
  if (normalized.includes("signal is aborted") ||
113
170
  normalized.includes("aborterror") ||
@@ -121,7 +178,16 @@ function safeErrorMessage(err) {
121
178
  if (normalized.includes("failed to fetch") || normalized.includes("network")) {
122
179
  return "network request failed";
123
180
  }
124
- return raw;
181
+ if (normalized.includes("relation does not exist")) {
182
+ return sanitized;
183
+ }
184
+ if (normalized.includes("internal_error") || normalized.includes("internal server error")) {
185
+ return "temporary server issue";
186
+ }
187
+ if (normalized.includes("failed to list decision") || normalized.includes("failed to load decision")) {
188
+ return "decision data temporarily unavailable";
189
+ }
190
+ return sanitized;
125
191
  }
126
192
  return "Unexpected error";
127
193
  }
@@ -318,6 +384,16 @@ function deriveStructuredActivityBucket(input) {
318
384
  "nonBlockingDecisionCount",
319
385
  ]) ?? 0;
320
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
+ }
321
397
  // Any blocked slice result needs decision-first surfacing in the Activity UX.
322
398
  if (input.phase === "blocked")
323
399
  return "decision";
@@ -650,26 +726,10 @@ function activityMetadataStr(metadata, keys) {
650
726
  }
651
727
  return null;
652
728
  }
653
- const SEMANTIC_ACTIVITY_EVENTS = new Set([
654
- "autopilot_slice_result",
655
- "auto_continue_started",
656
- "auto_continue_stopped",
657
- "next_up_manual_dispatch_started",
658
- "autopilot_slice_mcp_handshake_failed",
659
- "autopilot_slice_timeout",
660
- "autopilot_slice_log_stall",
661
- "auto_continue_spawn_guard_blocked",
662
- "auto_continue_spawn_guard_rate_limited",
663
- "autopilot_autofix_scheduled",
664
- "autopilot_autofix_executed",
665
- "autopilot_autofix_skipped",
666
- ]);
667
729
  function semanticActivityKey(item) {
668
730
  const metadata = asActivityMetadataRecord(item.metadata);
669
731
  const eventRaw = metadata?.event;
670
732
  const event = typeof eventRaw === "string" ? eventRaw.trim().toLowerCase() : "";
671
- if (!event || !SEMANTIC_ACTIVITY_EVENTS.has(event))
672
- return null;
673
733
  const runLike = (typeof item.runId === "string" && item.runId.trim().length > 0
674
734
  ? item.runId.trim()
675
735
  : null) ??
@@ -693,6 +753,8 @@ function semanticActivityKey(item) {
693
753
  const stopReason = activityMetadataStr(metadata, ["stop_reason", "stopReason"]);
694
754
  const parsedStatus = activityMetadataStr(metadata, ["parsed_status", "parsedStatus"]);
695
755
  const title = (item.title ?? "").trim().toLowerCase();
756
+ if (!event && !runLike && !correlationId)
757
+ return null;
696
758
  if (!runLike && !correlationId && !workstreamId && !taskId)
697
759
  return null;
698
760
  return [
@@ -707,6 +769,27 @@ function semanticActivityKey(item) {
707
769
  title,
708
770
  ].join("|");
709
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
+ }
710
793
  function mergeActivities(base, extra, limit) {
711
794
  const merged = [...(base ?? []), ...(extra ?? [])].sort((a, b) => {
712
795
  const timestampDelta = Date.parse(b.timestamp) - Date.parse(a.timestamp);
@@ -717,15 +800,28 @@ function mergeActivities(base, extra, limit) {
717
800
  const deduped = [];
718
801
  const seenIds = new Set();
719
802
  const seenSemantic = new Set();
803
+ const semanticIdx = new Map();
720
804
  for (const item of merged) {
721
- 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
+ }
722
810
  continue;
811
+ }
723
812
  seenIds.add(item.id);
724
813
  const sk = semanticActivityKey(item);
725
- 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
+ }
726
819
  continue;
727
- if (sk)
820
+ }
821
+ if (sk) {
728
822
  seenSemantic.add(sk);
823
+ semanticIdx.set(sk, deduped.length);
824
+ }
729
825
  deduped.push(item);
730
826
  if (deduped.length >= limit)
731
827
  break;
@@ -2111,11 +2207,11 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2111
2207
  initiativeStatusById.set(initiativeId, override.status);
2112
2208
  }
2113
2209
  const queueRank = (state) => {
2114
- if (state === "running")
2210
+ if (state === QueueState.RUNNING)
2115
2211
  return 0;
2116
- if (state === "queued")
2212
+ if (state === QueueState.QUEUED)
2117
2213
  return 1;
2118
- if (state === "blocked")
2214
+ if (state === QueueState.BLOCKED)
2119
2215
  return 2;
2120
2216
  return 3;
2121
2217
  };
@@ -2260,12 +2356,12 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2260
2356
  const hasRunning = statusValues.some((status) => isInProgressStatus(status));
2261
2357
  const hasQueued = statusValues.some((status) => status === "queued" || status === "pending");
2262
2358
  const queueState = hasRunning
2263
- ? "running"
2359
+ ? QueueState.RUNNING
2264
2360
  : hasBlocked
2265
- ? "blocked"
2361
+ ? QueueState.BLOCKED
2266
2362
  : hasQueued
2267
- ? "queued"
2268
- : "idle";
2363
+ ? QueueState.QUEUED
2364
+ : QueueState.IDLE;
2269
2365
  const latestRunner = [];
2270
2366
  const latestRunnerSeen = new Set();
2271
2367
  pushRunnerAgent(latestRunner, latestRunnerSeen, {
@@ -2279,7 +2375,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2279
2375
  const runnerAgentId = primaryRunner?.id ?? "unassigned";
2280
2376
  const runnerAgentName = primaryRunner?.name ?? "Unassigned";
2281
2377
  const pinKey = `${entry.initiativeId}:${entry.workstreamId}`;
2282
- if (isSuppressed(entry.initiativeId, entry.workstreamId) && queueState !== "running") {
2378
+ if (isSuppressed(entry.initiativeId, entry.workstreamId) && queueState !== QueueState.RUNNING) {
2283
2379
  continue;
2284
2380
  }
2285
2381
  fallbackItems.push({
@@ -2298,7 +2394,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2298
2394
  runnerAgentId,
2299
2395
  runnerAgentName,
2300
2396
  runnerAgents,
2301
- runnerSource: "fallback",
2397
+ runnerSource: RunnerSource.FALLBACK,
2302
2398
  queueState,
2303
2399
  blockReason: hasBlocked
2304
2400
  ? entry.blockers[0] ??
@@ -2483,7 +2579,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2483
2579
  : [];
2484
2580
  const runScopedToCurrentWorkstream = scopedAllowedWorkstreams.length === 1 &&
2485
2581
  scopedAllowedWorkstreams[0] === workstream.id &&
2486
- autoContinueRun?.status === "running";
2582
+ autoContinueRun?.status === RunStatus.RUNNING;
2487
2583
  const activeRunIds = Array.isArray(autoContinueRun?.activeSliceRunIds)
2488
2584
  ? autoContinueRun.activeSliceRunIds
2489
2585
  .filter((id) => typeof id === "string" && id.trim().length > 0)
@@ -2533,20 +2629,26 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2533
2629
  const sliceMilestoneId = defaultScope === "milestone"
2534
2630
  ? scopeSelection.milestoneIds[0] ?? pin?.preferredMilestoneId ?? null
2535
2631
  : null;
2536
- let queueState = laneState === "running"
2537
- ? "running"
2538
- : runScopedToCurrentWorkstream
2539
- ? "running"
2540
- : candidateTask
2541
- ? "queued"
2542
- : "idle";
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;
2543
2645
  let blockReason = null;
2544
- if (laneState === "blocked") {
2545
- queueState = "blocked";
2646
+ if (laneState === LaneState.BLOCKED) {
2647
+ queueState = QueueState.BLOCKED;
2546
2648
  blockReason = autoContinueLane?.blockedReason ?? "Blocked";
2547
2649
  }
2548
- else if (laneState === "waiting_dependency") {
2549
- queueState = "blocked";
2650
+ else if (laneState === LaneState.WAITING_DEPENDENCY) {
2651
+ queueState = QueueState.BLOCKED;
2550
2652
  if (Array.isArray(autoContinueLane?.waitingOnWorkstreamIds) &&
2551
2653
  autoContinueLane.waitingOnWorkstreamIds.length > 0) {
2552
2654
  const waitingTitles = autoContinueLane.waitingOnWorkstreamIds
@@ -2564,12 +2666,12 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2564
2666
  blockReason = "Waiting on dependency workstreams";
2565
2667
  }
2566
2668
  }
2567
- else if (laneState === "rate_limited") {
2568
- queueState = "blocked";
2669
+ else if (laneState === LaneState.RATE_LIMITED) {
2670
+ queueState = QueueState.BLOCKED;
2569
2671
  blockReason = autoContinueLane?.blockedReason ?? "Rate-limited";
2570
2672
  }
2571
2673
  if (!autoContinueRun && !readyTask && candidateTask) {
2572
- queueState = "blocked";
2674
+ queueState = QueueState.BLOCKED;
2573
2675
  const blockedDeps = candidateTask.dependencyIds
2574
2676
  .map((depId) => nodeById.get(depId))
2575
2677
  .filter((dependency) => Boolean(dependency && !isDoneStatus(dependency.status)))
@@ -2587,7 +2689,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2587
2689
  if (!candidateTask && !autoContinueRun && !pin) {
2588
2690
  continue;
2589
2691
  }
2590
- if (isSuppressed(initiativeId, workstream.id) && queueState !== "running") {
2692
+ if (isSuppressed(initiativeId, workstream.id) && queueState !== QueueState.RUNNING) {
2591
2693
  continue;
2592
2694
  }
2593
2695
  runningWorkstreams.add(workstream.id);
@@ -2622,10 +2724,10 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2622
2724
  }
2623
2725
  const runnerAgents = assignedRunnerAgents.length > 0 ? assignedRunnerAgents : inferredRunnerAgents;
2624
2726
  const runnerSource = assignedRunnerAgents.length > 0
2625
- ? "assigned"
2727
+ ? RunnerSource.ASSIGNED
2626
2728
  : runnerAgents.length > 0
2627
- ? "inferred"
2628
- : "fallback";
2729
+ ? RunnerSource.INFERRED
2730
+ : RunnerSource.FALLBACK;
2629
2731
  const primaryRunner = runnerAgents[0] ?? null;
2630
2732
  const runnerAgentId = primaryRunner?.id ?? "unassigned";
2631
2733
  const runnerAgentName = primaryRunner?.name ?? "Unassigned";
@@ -2719,14 +2821,14 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2719
2821
  : activeTaskId
2720
2822
  ? [activeTaskId]
2721
2823
  : [];
2722
- const queueState = laneState === "running"
2723
- ? "running"
2724
- : laneState === "blocked" ||
2725
- laneState === "waiting_dependency" ||
2726
- laneState === "rate_limited"
2727
- ? "blocked"
2728
- : "queued";
2729
- if (isSuppressed(initiativeId, workstream.id) && queueState !== "running") {
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) {
2730
2832
  continue;
2731
2833
  }
2732
2834
  const runRunnerAgents = [];
@@ -2752,9 +2854,9 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
2752
2854
  runnerAgentId: runPrimaryRunner?.id ?? "unassigned",
2753
2855
  runnerAgentName: runPrimaryRunner?.name ?? "Unassigned",
2754
2856
  runnerAgents: runRunnerAgents,
2755
- runnerSource: runPrimaryRunner ? "inferred" : "fallback",
2857
+ runnerSource: runPrimaryRunner ? RunnerSource.INFERRED : RunnerSource.FALLBACK,
2756
2858
  queueState,
2757
- blockReason: queueState === "blocked"
2859
+ blockReason: queueState === QueueState.BLOCKED
2758
2860
  ? lane?.blockedReason ?? "Blocked"
2759
2861
  : null,
2760
2862
  isPinned: Boolean(pinnedByKey.get(`${initiativeId}:${workstream.id}`)),
@@ -3178,10 +3280,11 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
3178
3280
  broadcastRuntimeSse("runtime.updated", runtimeRecord);
3179
3281
  }
3180
3282
  clearSnapshotResponseCache();
3283
+ const activityId = deterministicActivityId("run_completed", normalizedRunId, nowIso, runtimeRecord?.agentId ?? existingRun?.agentId ?? null, "dashboard_run_mark_completed");
3181
3284
  try {
3182
3285
  appendActivityItems([
3183
3286
  {
3184
- id: randomUUID(),
3287
+ id: activityId,
3185
3288
  type: "run_completed",
3186
3289
  title: message,
3187
3290
  description: reason,
@@ -3213,7 +3316,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
3213
3316
  try {
3214
3317
  const outboxSessionId = runtimeRecord?.initiativeId ?? existingRun?.initiativeId ?? normalizedRunId;
3215
3318
  const outboxActivityItem = {
3216
- id: randomUUID(),
3319
+ id: activityId,
3217
3320
  type: "run_completed",
3218
3321
  title: message,
3219
3322
  description: reason,
@@ -1,25 +1,5 @@
1
1
  import { getChatThread, linkChatThreadScope, listChatThreads, recordChatLaunch, sendChatMessage, updateChatLaunchStatus, } from "../../chat-store.js";
2
- function asRecord(value) {
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" ||
@@ -31,8 +31,15 @@ export function registerLiveMiscRoutes(router, deps) {
31
31
  const projectId = projectIdRaw?.trim() ?? "";
32
32
  if (!projectId)
33
33
  return null;
34
- const ids = await deps.listInitiativeIdsForProject({ projectId });
35
- return new Set(ids);
34
+ try {
35
+ const ids = await deps.listInitiativeIdsForProject({ projectId });
36
+ return new Set(ids);
37
+ }
38
+ catch {
39
+ // Scope discovery is best-effort. Continue with unscoped fallback data
40
+ // instead of hard-failing live surfaces.
41
+ return null;
42
+ }
36
43
  }
37
44
  router.add("POST", "live/activity/headline", async ({ req, res }) => {
38
45
  try {
@@ -13,7 +13,7 @@ const LIVE_SNAPSHOT_UPSTREAM_TIMEOUT_MS = (() => {
13
13
  const LIVE_SNAPSHOT_NEXT_UP_TIMEOUT_MS = (() => {
14
14
  const raw = Number(process.env.ORGX_LIVE_SNAPSHOT_NEXT_UP_TIMEOUT_MS ?? "");
15
15
  if (!Number.isFinite(raw))
16
- return 1_200;
16
+ return 350;
17
17
  return Math.max(250, Math.min(15_000, Math.floor(raw)));
18
18
  })();
19
19
  async function withSoftTimeout(work, timeoutMs, label) {
@@ -280,9 +280,15 @@ export function registerLiveSnapshotRoutes(router, deps) {
280
280
  const agentContexts = contextStore.agents;
281
281
  const runContexts = contextStore.runs ?? {};
282
282
  const scopedAgentIds = deps.getScopedAgentIds(agentContexts);
283
- const scopedProjectInitiativeIds = projectId && projectId.trim().length > 0
284
- ? new Set(await deps.listInitiativeIdsForProject({ projectId: projectId.trim() }))
285
- : null;
283
+ let scopedProjectInitiativeIds = null;
284
+ if (projectId && projectId.trim().length > 0) {
285
+ try {
286
+ scopedProjectInitiativeIds = new Set(await deps.listInitiativeIdsForProject({ projectId: projectId.trim() }));
287
+ }
288
+ catch (err) {
289
+ degraded.push(`workspace initiative scope unavailable (${deps.safeErrorMessage(err)})`);
290
+ }
291
+ }
286
292
  let outboxStatus;
287
293
  try {
288
294
  const diagnosticsOutbox = await deps.readDiagnosticsOutboxStatus();
@@ -444,29 +450,7 @@ export function registerLiveSnapshotRoutes(router, deps) {
444
450
  try {
445
451
  const buffered = await deps.readOutboxItems();
446
452
  if (buffered.length > 0) {
447
- const merged = [...activity, ...buffered]
448
- .sort((a, b) => {
449
- const d = Date.parse(b.timestamp) - Date.parse(a.timestamp);
450
- if (d !== 0)
451
- return d;
452
- return b.id.localeCompare(a.id);
453
- })
454
- .slice(0, activityLimit);
455
- const deduped = [];
456
- const seenIds = new Set();
457
- const seenSemantic = new Set();
458
- for (const item of merged) {
459
- if (seenIds.has(item.id))
460
- continue;
461
- seenIds.add(item.id);
462
- const sk = deps.semanticActivityKey(item);
463
- if (sk && seenSemantic.has(sk))
464
- continue;
465
- if (sk)
466
- seenSemantic.add(sk);
467
- deduped.push(item);
468
- }
469
- activity = deduped;
453
+ activity = deps.mergeActivities(activity, buffered, activityLimit);
470
454
  }
471
455
  }
472
456
  catch (err) {
@@ -494,6 +478,9 @@ export function registerLiveSnapshotRoutes(router, deps) {
494
478
  agents: agentContexts,
495
479
  runs: runContexts,
496
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);
497
484
  activity = activity.filter((item) => !shouldHideActivityItem(item));
498
485
  sessions = normalizeReportingBlockedSessions({
499
486
  sessions,