@useorgx/openclaw-plugin 0.4.9 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +35 -0
  2. package/dashboard/dist/assets/BJgZIVUQ.js +53 -0
  3. package/dashboard/dist/assets/BJgZIVUQ.js.br +0 -0
  4. package/dashboard/dist/assets/BJgZIVUQ.js.gz +0 -0
  5. package/dashboard/dist/assets/BXWDRGm-.js +1 -0
  6. package/dashboard/dist/assets/BXWDRGm-.js.br +0 -0
  7. package/dashboard/dist/assets/BXWDRGm-.js.gz +0 -0
  8. package/dashboard/dist/assets/BgOYB78t.js +4 -0
  9. package/dashboard/dist/assets/BgOYB78t.js.br +0 -0
  10. package/dashboard/dist/assets/BgOYB78t.js.gz +0 -0
  11. package/dashboard/dist/assets/C-KIc3Wc.js.br +0 -0
  12. package/dashboard/dist/assets/C-KIc3Wc.js.gz +0 -0
  13. package/dashboard/dist/assets/CE38zU4U.js +1 -0
  14. package/dashboard/dist/assets/CE38zU4U.js.br +0 -0
  15. package/dashboard/dist/assets/CE38zU4U.js.gz +0 -0
  16. package/dashboard/dist/assets/CFGKRAzG.js +1 -0
  17. package/dashboard/dist/assets/CFGKRAzG.js.br +0 -0
  18. package/dashboard/dist/assets/CFGKRAzG.js.gz +0 -0
  19. package/dashboard/dist/assets/CGGR2GZh.js +1 -0
  20. package/dashboard/dist/assets/CGGR2GZh.js.br +0 -0
  21. package/dashboard/dist/assets/CGGR2GZh.js.gz +0 -0
  22. package/dashboard/dist/assets/CL_wXqR7.js +1 -0
  23. package/dashboard/dist/assets/CL_wXqR7.js.br +0 -0
  24. package/dashboard/dist/assets/CL_wXqR7.js.gz +0 -0
  25. package/dashboard/dist/assets/CPFiTmlw.js +8 -0
  26. package/dashboard/dist/assets/CPFiTmlw.js.br +0 -0
  27. package/dashboard/dist/assets/CPFiTmlw.js.gz +0 -0
  28. package/dashboard/dist/assets/CZZTvkQZ.js +1 -0
  29. package/dashboard/dist/assets/CZZTvkQZ.js.br +0 -0
  30. package/dashboard/dist/assets/CZZTvkQZ.js.gz +0 -0
  31. package/dashboard/dist/assets/{CpJsfbXo.js → CxQ08qFN.js} +2 -2
  32. package/dashboard/dist/assets/CxQ08qFN.js.br +0 -0
  33. package/dashboard/dist/assets/CxQ08qFN.js.gz +0 -0
  34. package/dashboard/dist/assets/D-bf6hEI.js +213 -0
  35. package/dashboard/dist/assets/D-bf6hEI.js.br +0 -0
  36. package/dashboard/dist/assets/D-bf6hEI.js.gz +0 -0
  37. package/dashboard/dist/assets/DG6y9wJI.js +2 -0
  38. package/dashboard/dist/assets/DG6y9wJI.js.br +0 -0
  39. package/dashboard/dist/assets/DG6y9wJI.js.gz +0 -0
  40. package/dashboard/dist/assets/DNxKz-GV.js +1 -0
  41. package/dashboard/dist/assets/DNxKz-GV.js.br +0 -0
  42. package/dashboard/dist/assets/DNxKz-GV.js.gz +0 -0
  43. package/dashboard/dist/assets/DW_rKUic.js +11 -0
  44. package/dashboard/dist/assets/DW_rKUic.js.br +0 -0
  45. package/dashboard/dist/assets/DW_rKUic.js.gz +0 -0
  46. package/dashboard/dist/assets/DbNoijHm.js +1 -0
  47. package/dashboard/dist/assets/DbNoijHm.js.br +0 -0
  48. package/dashboard/dist/assets/DbNoijHm.js.gz +0 -0
  49. package/dashboard/dist/assets/DjcdE6jC.js +2 -0
  50. package/dashboard/dist/assets/DjcdE6jC.js.br +0 -0
  51. package/dashboard/dist/assets/DjcdE6jC.js.gz +0 -0
  52. package/dashboard/dist/assets/FZYuCDnt.js +1 -0
  53. package/dashboard/dist/assets/FZYuCDnt.js.br +0 -0
  54. package/dashboard/dist/assets/FZYuCDnt.js.gz +0 -0
  55. package/dashboard/dist/assets/PAUiij_z.js +1 -0
  56. package/dashboard/dist/assets/PAUiij_z.js.br +0 -0
  57. package/dashboard/dist/assets/PAUiij_z.js.gz +0 -0
  58. package/dashboard/dist/assets/cNrhgGc1.js +8 -0
  59. package/dashboard/dist/assets/cNrhgGc1.js.br +0 -0
  60. package/dashboard/dist/assets/cNrhgGc1.js.gz +0 -0
  61. package/dashboard/dist/assets/h5biQs2I.css +1 -0
  62. package/dashboard/dist/assets/h5biQs2I.css.br +0 -0
  63. package/dashboard/dist/assets/h5biQs2I.css.gz +0 -0
  64. package/dashboard/dist/assets/ic2FaMnh.js +1 -0
  65. package/dashboard/dist/assets/ic2FaMnh.js.br +0 -0
  66. package/dashboard/dist/assets/ic2FaMnh.js.gz +0 -0
  67. package/dashboard/dist/assets/nByHNHoW.js +1 -0
  68. package/dashboard/dist/assets/nByHNHoW.js.br +0 -0
  69. package/dashboard/dist/assets/nByHNHoW.js.gz +0 -0
  70. package/dashboard/dist/assets/qm8xLgv-.css +1 -0
  71. package/dashboard/dist/assets/qm8xLgv-.css.br +0 -0
  72. package/dashboard/dist/assets/qm8xLgv-.css.gz +0 -0
  73. package/dashboard/dist/assets/tS9mbYZi.js +1 -0
  74. package/dashboard/dist/assets/tS9mbYZi.js.br +0 -0
  75. package/dashboard/dist/assets/tS9mbYZi.js.gz +0 -0
  76. package/dashboard/dist/brand/anthropic-mark.svg.br +0 -0
  77. package/dashboard/dist/brand/anthropic-mark.svg.gz +0 -0
  78. package/dashboard/dist/brand/openai-mark.svg.br +0 -0
  79. package/dashboard/dist/brand/openai-mark.svg.gz +0 -0
  80. package/dashboard/dist/brand/openclaw-mark.svg.br +0 -0
  81. package/dashboard/dist/brand/openclaw-mark.svg.gz +0 -0
  82. package/dashboard/dist/brand/xandy-orchestrator.png +0 -0
  83. package/dashboard/dist/index.html +7 -5
  84. package/dashboard/dist/index.html.br +0 -0
  85. package/dashboard/dist/index.html.gz +0 -0
  86. package/dist/activity-actor-fields.js +26 -4
  87. package/dist/activity-store.js +34 -8
  88. package/dist/agent-context-store.js +79 -17
  89. package/dist/agent-run-store.js +44 -3
  90. package/dist/agent-suite.d.ts +9 -0
  91. package/dist/agent-suite.js +149 -9
  92. package/dist/artifacts/artifact-domain-schemas.d.ts +66 -0
  93. package/dist/artifacts/artifact-domain-schemas.js +357 -0
  94. package/dist/artifacts/register-artifact.d.ts +4 -3
  95. package/dist/artifacts/register-artifact.js +170 -57
  96. package/dist/chat-store.d.ts +157 -0
  97. package/dist/chat-store.js +586 -0
  98. package/dist/cli/orgx.js +11 -0
  99. package/dist/contracts/client.d.ts +43 -3
  100. package/dist/contracts/client.js +159 -30
  101. package/dist/contracts/retro-schema.d.ts +81 -0
  102. package/dist/contracts/retro-schema.js +80 -0
  103. package/dist/contracts/shared-types.d.ts +159 -0
  104. package/dist/contracts/shared-types.js +177 -1
  105. package/dist/contracts/skill-pack-schema.d.ts +192 -0
  106. package/dist/contracts/skill-pack-schema.js +180 -0
  107. package/dist/contracts/types.d.ts +227 -2
  108. package/dist/entities/auto-assignment.js +43 -17
  109. package/dist/event-sanitization.d.ts +11 -0
  110. package/dist/event-sanitization.js +113 -0
  111. package/dist/fs-utils.js +13 -1
  112. package/dist/gateway-watchdog.d.ts +5 -0
  113. package/dist/gateway-watchdog.js +50 -0
  114. package/dist/hooks/post-reporting-event.mjs +1 -5
  115. package/dist/http/helpers/activity-headline.js +13 -132
  116. package/dist/http/helpers/auto-continue-engine.d.ts +198 -10
  117. package/dist/http/helpers/auto-continue-engine.js +2531 -186
  118. package/dist/http/helpers/autopilot-operations.d.ts +19 -0
  119. package/dist/http/helpers/autopilot-operations.js +182 -31
  120. package/dist/http/helpers/autopilot-runtime.d.ts +1 -0
  121. package/dist/http/helpers/autopilot-runtime.js +308 -20
  122. package/dist/http/helpers/autopilot-slice-utils.d.ts +18 -0
  123. package/dist/http/helpers/autopilot-slice-utils.js +516 -93
  124. package/dist/http/helpers/decision-mapper.d.ts +40 -0
  125. package/dist/http/helpers/decision-mapper.js +223 -7
  126. package/dist/http/helpers/dispatch-lifecycle.d.ts +19 -2
  127. package/dist/http/helpers/dispatch-lifecycle.js +242 -37
  128. package/dist/http/helpers/kickoff-context.js +74 -0
  129. package/dist/http/helpers/llm-client.d.ts +47 -0
  130. package/dist/http/helpers/llm-client.js +256 -0
  131. package/dist/http/helpers/mission-control.d.ts +102 -3
  132. package/dist/http/helpers/mission-control.js +498 -9
  133. package/dist/http/helpers/sentinel-catalog.d.ts +23 -0
  134. package/dist/http/helpers/sentinel-catalog.js +193 -0
  135. package/dist/http/helpers/session-classification.d.ts +9 -0
  136. package/dist/http/helpers/session-classification.js +564 -0
  137. package/dist/http/helpers/slice-experience-v2.d.ts +137 -0
  138. package/dist/http/helpers/slice-experience-v2.js +677 -0
  139. package/dist/http/helpers/slice-run-projections.d.ts +72 -0
  140. package/dist/http/helpers/slice-run-projections.js +860 -0
  141. package/dist/http/helpers/triage-mapper.d.ts +43 -0
  142. package/dist/http/helpers/triage-mapper.js +549 -0
  143. package/dist/http/helpers/value-utils.js +7 -2
  144. package/dist/http/helpers/workspace-scope.d.ts +15 -0
  145. package/dist/http/helpers/workspace-scope.js +170 -0
  146. package/dist/http/index.js +1354 -97
  147. package/dist/http/routes/agent-suite.d.ts +9 -0
  148. package/dist/http/routes/agent-suite.js +207 -8
  149. package/dist/http/routes/agents-catalog.js +64 -19
  150. package/dist/http/routes/chat.d.ts +19 -0
  151. package/dist/http/routes/chat.js +522 -0
  152. package/dist/http/routes/decision-actions.d.ts +8 -1
  153. package/dist/http/routes/decision-actions.js +42 -5
  154. package/dist/http/routes/dispatch-gateway-envelope.d.ts +25 -0
  155. package/dist/http/routes/dispatch-gateway-envelope.js +26 -0
  156. package/dist/http/routes/entities.d.ts +16 -0
  157. package/dist/http/routes/entities.js +294 -6
  158. package/dist/http/routes/live-legacy.d.ts +5 -0
  159. package/dist/http/routes/live-legacy.js +23 -509
  160. package/dist/http/routes/live-misc.d.ts +12 -0
  161. package/dist/http/routes/live-misc.js +251 -31
  162. package/dist/http/routes/live-snapshot.d.ts +48 -2
  163. package/dist/http/routes/live-snapshot.js +638 -19
  164. package/dist/http/routes/live-terminal.d.ts +11 -0
  165. package/dist/http/routes/live-terminal.js +261 -0
  166. package/dist/http/routes/live-triage.d.ts +61 -0
  167. package/dist/http/routes/live-triage.js +248 -0
  168. package/dist/http/routes/mission-control-actions.d.ts +49 -1
  169. package/dist/http/routes/mission-control-actions.js +1334 -84
  170. package/dist/http/routes/mission-control-read.d.ts +48 -3
  171. package/dist/http/routes/mission-control-read.js +1593 -20
  172. package/dist/http/routes/realtime-orchestrator.d.ts +10 -0
  173. package/dist/http/routes/realtime-orchestrator.js +74 -0
  174. package/dist/http/routes/run-control.d.ts +5 -2
  175. package/dist/http/routes/run-control.js +10 -0
  176. package/dist/http/routes/sentinels-catalog.d.ts +7 -0
  177. package/dist/http/routes/sentinels-catalog.js +24 -0
  178. package/dist/http/routes/summary.js +10 -3
  179. package/dist/http/routes/usage.d.ts +24 -0
  180. package/dist/http/routes/usage.js +362 -0
  181. package/dist/http/routes/work-artifacts.js +28 -9
  182. package/dist/index.js +165 -27
  183. package/dist/local-openclaw.js +29 -6
  184. package/dist/mcp-client-setup.js +3 -3
  185. package/dist/mcp-http-handler.js +33 -59
  186. package/dist/next-up-queue-store.d.ts +16 -1
  187. package/dist/next-up-queue-store.js +89 -7
  188. package/dist/outbox.d.ts +5 -0
  189. package/dist/outbox.js +113 -9
  190. package/dist/paths.js +24 -5
  191. package/dist/reporting/rollups.d.ts +53 -0
  192. package/dist/reporting/rollups.js +148 -0
  193. package/dist/retro/domain-templates.d.ts +45 -0
  194. package/dist/retro/domain-templates.js +297 -0
  195. package/dist/retro/quality-rubric.d.ts +33 -0
  196. package/dist/retro/quality-rubric.js +213 -0
  197. package/dist/runtime-cleanup.d.ts +18 -0
  198. package/dist/runtime-cleanup.js +87 -0
  199. package/dist/services/background.d.ts +11 -0
  200. package/dist/services/background.js +22 -0
  201. package/dist/services/experiment-randomization.d.ts +21 -0
  202. package/dist/services/experiment-randomization.js +63 -0
  203. package/dist/skill-pack-state.d.ts +36 -5
  204. package/dist/skill-pack-state.js +273 -29
  205. package/dist/sync/local-agent-telemetry.d.ts +13 -0
  206. package/dist/sync/local-agent-telemetry.js +128 -0
  207. package/dist/sync/outbox-replay.js +131 -24
  208. package/dist/team-context-store.d.ts +23 -0
  209. package/dist/team-context-store.js +116 -0
  210. package/dist/telemetry/posthog.js +4 -2
  211. package/dist/tools/core-tools.d.ts +10 -14
  212. package/dist/tools/core-tools.js +1289 -24
  213. package/dist/types.d.ts +2 -0
  214. package/dist/types.js +2 -0
  215. package/dist/worker-supervisor.js +23 -0
  216. package/package.json +14 -4
  217. package/dashboard/dist/assets/B3ziCA02.js +0 -8
  218. package/dashboard/dist/assets/B5NEElEI.css +0 -1
  219. package/dashboard/dist/assets/BhapSNAs.js +0 -215
  220. package/dashboard/dist/assets/iFdvE7lx.js +0 -1
  221. package/dashboard/dist/assets/jRJsmpYM.js +0 -1
  222. package/dashboard/dist/assets/sAhvFnpk.js +0 -4
@@ -0,0 +1,677 @@
1
+ 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
+ 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
+ }
8
+ function normalizeText(value) {
9
+ if (typeof value !== "string")
10
+ return null;
11
+ const trimmed = value.trim();
12
+ return trimmed.length > 0 ? trimmed : null;
13
+ }
14
+ function normalizeTextArray(values, fallback) {
15
+ const out = [];
16
+ const push = (value) => {
17
+ const normalized = normalizeText(value);
18
+ if (!normalized)
19
+ return;
20
+ if (out.some((entry) => entry.toLowerCase() === normalized.toLowerCase()))
21
+ return;
22
+ out.push(normalized);
23
+ };
24
+ if (Array.isArray(values)) {
25
+ for (const value of values)
26
+ push(value);
27
+ }
28
+ else if (typeof values === "string") {
29
+ for (const value of values.split(/[\n,;]+/g))
30
+ push(value);
31
+ }
32
+ if (fallback)
33
+ push(fallback);
34
+ return out;
35
+ }
36
+ function toEpoch(value) {
37
+ if (!value)
38
+ return 0;
39
+ const parsed = Date.parse(value);
40
+ return Number.isFinite(parsed) ? parsed : 0;
41
+ }
42
+ function isGenericActorValue(value) {
43
+ const normalized = normalizeText(value);
44
+ if (!normalized)
45
+ return true;
46
+ const lower = normalized.toLowerCase();
47
+ if (GENERIC_ACTOR_IDS.has(lower))
48
+ return true;
49
+ return UUID_RE.test(normalized);
50
+ }
51
+ function isReportingLike(text) {
52
+ const normalized = (text ?? "").trim().toLowerCase();
53
+ if (!normalized)
54
+ return false;
55
+ return normalized.startsWith("reporting") || normalized.includes("telemetry");
56
+ }
57
+ function metadataString(metadata, keys) {
58
+ if (!metadata)
59
+ return null;
60
+ for (const key of keys) {
61
+ const value = normalizeText(metadata[key]);
62
+ if (value)
63
+ return value;
64
+ }
65
+ return null;
66
+ }
67
+ function resolveActivitySliceRunId(item) {
68
+ if (normalizeText(item.runId))
69
+ return normalizeText(item.runId);
70
+ const metadata = asRecord(item.metadata);
71
+ return metadataString(metadata, [
72
+ "slice_run_id",
73
+ "sliceRunId",
74
+ "active_run_id",
75
+ "activeRunId",
76
+ "run_id",
77
+ "runId",
78
+ "correlation_id",
79
+ "correlationId",
80
+ ]);
81
+ }
82
+ function canonicalLifecycle(state) {
83
+ const normalized = state.trim().toLowerCase();
84
+ if (normalized === "needs_review")
85
+ return "completed";
86
+ if (normalized === "queued")
87
+ return "queued";
88
+ if (normalized === "dispatching")
89
+ return "dispatching";
90
+ if (normalized === "running")
91
+ return "running";
92
+ if (normalized === "awaiting_input")
93
+ return "awaiting_input";
94
+ if (normalized === "completed")
95
+ return "completed";
96
+ if (normalized === "failed")
97
+ return "failed";
98
+ if (normalized === "archived")
99
+ return "archived";
100
+ return "queued";
101
+ }
102
+ function deriveSliceKind(slice, linkedSession) {
103
+ const initiativeIds = normalizeTextArray(slice.initiativeIds, slice.initiativeId);
104
+ const workstreamIds = normalizeTextArray(slice.workstreamIds, slice.workstreamId);
105
+ const hasStructuredScope = initiativeIds.length > 0 ||
106
+ workstreamIds.length > 0 ||
107
+ (Array.isArray(slice.taskIds) && slice.taskIds.length > 0) ||
108
+ (Array.isArray(slice.milestoneIds) && slice.milestoneIds.length > 0);
109
+ if (hasStructuredScope)
110
+ return "work_slice";
111
+ if (isReportingLike(linkedSession?.title) || isReportingLike(slice.lastEventSummary)) {
112
+ return "runtime_reporting";
113
+ }
114
+ if ((slice.sourceClient ?? "").toLowerCase() === "openclaw") {
115
+ return "runtime_reporting";
116
+ }
117
+ return "system_maintenance";
118
+ }
119
+ function deriveOutcomeState(lifecycleState, hasArtifact, decisionCount) {
120
+ if (lifecycleState === "awaiting_input")
121
+ return "needs_input";
122
+ if (lifecycleState === "failed") {
123
+ return decisionCount > 0 ? "failed_actionable" : "failed_non_actionable";
124
+ }
125
+ if (lifecycleState === "completed") {
126
+ return hasArtifact ? "succeeded_with_artifacts" : "succeeded_without_artifacts";
127
+ }
128
+ return hasArtifact ? "succeeded_with_artifacts" : "needs_input";
129
+ }
130
+ function defaultStatusExplainer(state) {
131
+ switch (state) {
132
+ case "queued":
133
+ return "Queued and waiting to dispatch.";
134
+ case "dispatching":
135
+ return "Dispatch acknowledged and waiting for execution.";
136
+ case "running":
137
+ return "Slice execution is in progress.";
138
+ case "awaiting_input":
139
+ return "Needs your input to continue.";
140
+ case "completed":
141
+ return "Execution completed successfully.";
142
+ case "failed":
143
+ return "Execution failed before completion.";
144
+ case "archived":
145
+ return "Session was archived.";
146
+ default:
147
+ return "Status unavailable.";
148
+ }
149
+ }
150
+ function deriveActionContract(slice, outcomeState) {
151
+ if (outcomeState === "succeeded_with_artifacts" && slice.artifactCount > 0) {
152
+ return {
153
+ actionType: "open_artifact",
154
+ label: "Open artifact",
155
+ payloadSchema: { requires: ["sliceRunId"], optional: ["artifactId"] },
156
+ primary: true,
157
+ };
158
+ }
159
+ if (outcomeState === "needs_input") {
160
+ const hasExplicitOptions = Array.isArray(slice.decisionOptions) && slice.decisionOptions.length > 0;
161
+ return {
162
+ actionType: hasExplicitOptions ? "approve" : "provide_context",
163
+ label: hasExplicitOptions ? "Choose decision" : "Provide context",
164
+ payloadSchema: hasExplicitOptions
165
+ ? { requires: ["decisionId"], optional: ["optionId", "note"] }
166
+ : { requires: ["note"] },
167
+ primary: true,
168
+ };
169
+ }
170
+ if (outcomeState === "failed_actionable" || outcomeState === "failed_non_actionable") {
171
+ return {
172
+ actionType: "retry",
173
+ label: "Retry slice",
174
+ payloadSchema: { requires: ["sliceRunId"], optional: ["note"] },
175
+ primary: true,
176
+ };
177
+ }
178
+ if (outcomeState === "succeeded_without_artifacts") {
179
+ return {
180
+ actionType: "provide_context",
181
+ label: "Add evidence",
182
+ payloadSchema: { requires: ["note"] },
183
+ primary: true,
184
+ };
185
+ }
186
+ return null;
187
+ }
188
+ function deriveActorProvenance(slice, linkedSession) {
189
+ const sessionAgentId = normalizeText(linkedSession?.agentId) ?? null;
190
+ const sessionAgentName = normalizeText(linkedSession?.agentName) ?? null;
191
+ if (!isGenericActorValue(sessionAgentName) || !isGenericActorValue(sessionAgentId)) {
192
+ const actorId = sessionAgentId ?? sessionAgentName ?? "unknown-agent";
193
+ const displayName = sessionAgentName ?? "Agent";
194
+ return {
195
+ actorType: "agent",
196
+ actorId,
197
+ displayName,
198
+ avatarKey: `agent:${actorId}`,
199
+ };
200
+ }
201
+ const source = (slice.sourceClient ?? "").trim().toLowerCase();
202
+ if (source === "codex" || source === "claude-code" || source === "openclaw") {
203
+ const displayName = source === "codex" ? "Codex Agent" : source === "claude-code" ? "Claude Code Agent" : "OpenClaw Agent";
204
+ return {
205
+ actorType: "system",
206
+ actorId: source,
207
+ displayName,
208
+ avatarKey: source === "openclaw" ? "openclaw-system" : `agent:${source}`,
209
+ };
210
+ }
211
+ return {
212
+ actorType: "orgx",
213
+ actorId: "orgx",
214
+ displayName: "OrgX System",
215
+ avatarKey: "orgx",
216
+ };
217
+ }
218
+ function deriveActorProvenanceFromSession(session) {
219
+ const agentId = normalizeText(session.agentId);
220
+ const agentName = normalizeText(session.agentName);
221
+ if (!isGenericActorValue(agentName) || !isGenericActorValue(agentId)) {
222
+ const actorId = agentId ?? agentName ?? "unknown-agent";
223
+ return {
224
+ actorType: "agent",
225
+ actorId,
226
+ displayName: agentName ?? "Agent",
227
+ avatarKey: `agent:${actorId}`,
228
+ };
229
+ }
230
+ const runtimeClient = (normalizeText(session.runtimeClient) ?? "").toLowerCase();
231
+ if (runtimeClient === "codex" || runtimeClient === "claude-code" || runtimeClient === "openclaw") {
232
+ const displayName = runtimeClient === "codex"
233
+ ? "Codex Agent"
234
+ : runtimeClient === "claude-code"
235
+ ? "Claude Code Agent"
236
+ : "OpenClaw Agent";
237
+ return {
238
+ actorType: "system",
239
+ actorId: runtimeClient,
240
+ displayName,
241
+ avatarKey: runtimeClient === "openclaw" ? "openclaw-system" : `agent:${runtimeClient}`,
242
+ };
243
+ }
244
+ return {
245
+ actorType: "orgx",
246
+ actorId: "orgx",
247
+ displayName: "OrgX System",
248
+ avatarKey: "orgx",
249
+ };
250
+ }
251
+ function asArtifactEnvelope(sliceRunId, item) {
252
+ return {
253
+ artifactId: normalizeText(item.id) ?? `${sliceRunId}:${normalizeText(item.title) ?? "artifact"}`,
254
+ sliceRunId,
255
+ type: normalizeText(item.type) ?? "other",
256
+ title: normalizeText(item.title) ?? "Artifact",
257
+ url: normalizeText(item.url) ?? null,
258
+ preview: null,
259
+ validation: normalizeText(item.url) ? "present" : "missing",
260
+ confidence: 1,
261
+ producedAt: item.createdAt ?? null,
262
+ producer: "agent",
263
+ };
264
+ }
265
+ function consistencyFlagsForSlice(sliceKind, slice, lifecycleState) {
266
+ const flags = [];
267
+ const hasLineage = normalizeTextArray(slice.initiativeIds, slice.initiativeId).length > 0 ||
268
+ normalizeTextArray(slice.workstreamIds, slice.workstreamId).length > 0 ||
269
+ (Array.isArray(slice.taskIds) && slice.taskIds.length > 0);
270
+ if (sliceKind === "work_slice" && !hasLineage)
271
+ flags.push("lineage_gap");
272
+ if (lifecycleState === "completed" && !slice.hasArtifact)
273
+ flags.push("artifact_mismatch");
274
+ if (!slice.completedAt && !slice.failedAt && !slice.archivedAt) {
275
+ if (lifecycleState === "completed" || lifecycleState === "failed" || lifecycleState === "archived") {
276
+ flags.push("missing_terminal");
277
+ }
278
+ }
279
+ if (isGenericActorValue(slice.sourceClient)) {
280
+ flags.push("invalid_actor");
281
+ }
282
+ return flags;
283
+ }
284
+ function selectLinkedSession(slice, sessionsByRunId) {
285
+ const runId = normalizeText(slice.runId) ?? normalizeText(slice.sliceRunId);
286
+ if (!runId)
287
+ return null;
288
+ return sessionsByRunId.get(runId) ?? null;
289
+ }
290
+ function buildTimelineNarrative(projections, activity) {
291
+ const bySliceRunId = new Map();
292
+ for (const item of activity) {
293
+ const sliceRunId = resolveActivitySliceRunId(item);
294
+ if (!sliceRunId)
295
+ continue;
296
+ const list = bySliceRunId.get(sliceRunId) ?? [];
297
+ list.push(item);
298
+ bySliceRunId.set(sliceRunId, list);
299
+ }
300
+ const out = [];
301
+ for (const projection of projections) {
302
+ const events = (bySliceRunId.get(projection.sliceRunId) ?? []).sort((a, b) => toEpoch(a.timestamp) - toEpoch(b.timestamp));
303
+ const meaningful = events.filter((event) => {
304
+ const metadata = asRecord(event.metadata);
305
+ const eventName = (metadataString(metadata, ["event"]) ?? "").toLowerCase();
306
+ return eventName !== "autopilot_slice_heartbeat";
307
+ });
308
+ const dispatch = meaningful.find((event) => {
309
+ const metadata = asRecord(event.metadata);
310
+ const eventName = (metadataString(metadata, ["event"]) ?? "").toLowerCase();
311
+ return eventName.includes("dispatch");
312
+ });
313
+ const title = projection.lineage.workstreamTitles[0] ??
314
+ projection.lineage.workstreamIds[0] ??
315
+ `Slice ${projection.sliceRunId.slice(0, 8)}`;
316
+ const intent = projection.lineage.taskIds.length > 0
317
+ ? `Execute ${projection.lineage.taskIds.length} task(s) in this slice.`
318
+ : "Execute scoped work for this slice.";
319
+ const highlights = meaningful
320
+ .map((event) => normalizeText(event.summary) ?? normalizeText(event.description) ?? normalizeText(event.title))
321
+ .filter((entry) => Boolean(entry))
322
+ .slice(-4);
323
+ const outcomeSummary = projection.outcomeState === "succeeded_with_artifacts"
324
+ ? `Completed with ${projection.artifactCount} artifact${projection.artifactCount === 1 ? "" : "s"}.`
325
+ : projection.outcomeState === "succeeded_without_artifacts"
326
+ ? "Completed without artifact evidence."
327
+ : projection.outcomeState === "needs_input"
328
+ ? "Waiting for input before continuing."
329
+ : "Execution failed before completion.";
330
+ out.push({
331
+ projectionVersion: 1,
332
+ sliceRunId: projection.sliceRunId,
333
+ title,
334
+ occurredAt: projection.updatedAt ?? projection.completedAt ?? projection.failedAt ?? null,
335
+ actorProvenance: projection.actorProvenance,
336
+ intent,
337
+ dispatch: normalizeText(dispatch?.summary) ??
338
+ normalizeText(dispatch?.description) ??
339
+ (dispatch ? dispatch.title : "Dispatch details unavailable."),
340
+ highlights,
341
+ outcome: {
342
+ state: projection.outcomeState,
343
+ summary: outcomeSummary,
344
+ artifactCount: projection.artifactCount,
345
+ },
346
+ nextAction: projection.actionContract,
347
+ technicalTrace: {
348
+ eventCount: events.length,
349
+ eventIds: events.map((event) => event.id).slice(-10),
350
+ },
351
+ });
352
+ }
353
+ out.sort((a, b) => toEpoch(b.occurredAt) - toEpoch(a.occurredAt));
354
+ return out;
355
+ }
356
+ export function buildSliceExperienceSnapshotV2(input) {
357
+ const sessionsByRunId = new Map();
358
+ for (const session of input.sessions) {
359
+ const runId = normalizeText(session.runId);
360
+ if (!runId || sessionsByRunId.has(runId))
361
+ continue;
362
+ sessionsByRunId.set(runId, session);
363
+ }
364
+ const projections = input.sliceRuns.map((slice) => {
365
+ const linkedSession = selectLinkedSession(slice, sessionsByRunId);
366
+ const lifecycleState = canonicalLifecycle(slice.status);
367
+ const sliceKind = deriveSliceKind(slice, linkedSession);
368
+ const outcomeState = deriveOutcomeState(lifecycleState, Boolean(slice.hasArtifact || slice.artifactCount > 0), slice.blockingDecisionCount ?? slice.decisionCount ?? 0);
369
+ const actionContract = deriveActionContract(slice, outcomeState);
370
+ const lineage = {
371
+ initiativeIds: normalizeTextArray(slice.initiativeIds, slice.initiativeId),
372
+ initiativeTitles: [],
373
+ workstreamIds: normalizeTextArray(slice.workstreamIds, slice.workstreamId),
374
+ workstreamTitles: normalizeTextArray(slice.workstreamTitle),
375
+ taskIds: Array.isArray(slice.taskIds) ? normalizeTextArray(slice.taskIds) : [],
376
+ milestoneIds: Array.isArray(slice.milestoneIds)
377
+ ? normalizeTextArray(slice.milestoneIds)
378
+ : [],
379
+ iwmtIds: normalizeTextArray(slice.iwmtIds, slice.iwmtId),
380
+ sliceRunId: slice.sliceRunId,
381
+ sessionId: linkedSession?.id ?? null,
382
+ };
383
+ const artifacts = (Array.isArray(slice.artifacts) ? slice.artifacts : []).map((item) => asArtifactEnvelope(slice.sliceRunId, item));
384
+ const consistencyFlags = consistencyFlagsForSlice(sliceKind, slice, lifecycleState);
385
+ return {
386
+ projectionVersion: 1,
387
+ lastEventId: null,
388
+ consistencyFlags,
389
+ sliceRunId: slice.sliceRunId,
390
+ runId: slice.runId ?? null,
391
+ sliceKind,
392
+ lifecycleState,
393
+ outcomeState,
394
+ statusExplainer: slice.statusExplainer,
395
+ actorProvenance: deriveActorProvenance(slice, linkedSession),
396
+ lineage,
397
+ artifacts,
398
+ artifactCount: slice.artifactCount,
399
+ hasArtifact: Boolean(slice.hasArtifact || slice.artifactCount > 0),
400
+ actionContract,
401
+ updatedAt: slice.updatedAt ?? slice.lastEventAt ?? null,
402
+ completedAt: slice.completedAt ?? null,
403
+ failedAt: slice.failedAt ?? null,
404
+ archivedAt: slice.archivedAt ?? null,
405
+ runtimeState: slice.runtimeState ?? null,
406
+ sourceClient: slice.sourceClient ?? null,
407
+ confidence: slice.confidence,
408
+ };
409
+ });
410
+ const projectionByRunId = new Set();
411
+ for (const projection of projections) {
412
+ const runId = normalizeText(projection.runId);
413
+ if (runId)
414
+ projectionByRunId.add(runId);
415
+ projectionByRunId.add(projection.sliceRunId);
416
+ }
417
+ for (const session of input.sessions) {
418
+ const runId = normalizeText(session.runId);
419
+ if (!runId || projectionByRunId.has(runId))
420
+ continue;
421
+ const title = normalizeText(session.title) ?? null;
422
+ const lifecycleState = canonicalLifecycle(normalizeText(session.status) ?? "queued");
423
+ const hasScope = Boolean(normalizeText(session.initiativeId)) ||
424
+ Boolean(normalizeText(session.workstreamId));
425
+ const sliceKind = hasScope
426
+ ? "work_slice"
427
+ : isReportingLike(title)
428
+ ? "runtime_reporting"
429
+ : "system_maintenance";
430
+ const outcomeState = deriveOutcomeState(lifecycleState, false, Array.isArray(session.blockers) ? session.blockers.length : 0);
431
+ const hasLineage = hasScope;
432
+ const consistencyFlags = [];
433
+ if (sliceKind === "work_slice" && !hasLineage)
434
+ consistencyFlags.push("lineage_gap");
435
+ if (isGenericActorValue(normalizeText(session.agentName) ?? normalizeText(session.agentId))) {
436
+ consistencyFlags.push("invalid_actor");
437
+ }
438
+ projections.push({
439
+ projectionVersion: 1,
440
+ lastEventId: null,
441
+ consistencyFlags,
442
+ sliceRunId: runId,
443
+ runId,
444
+ sliceKind,
445
+ lifecycleState,
446
+ outcomeState,
447
+ statusExplainer: normalizeText(session.lastEventSummary) ??
448
+ defaultStatusExplainer(lifecycleState),
449
+ actorProvenance: deriveActorProvenanceFromSession(session),
450
+ lineage: {
451
+ initiativeIds: normalizeTextArray(session.initiativeId),
452
+ initiativeTitles: normalizeTextArray(session.groupLabel),
453
+ workstreamIds: normalizeTextArray(session.workstreamId),
454
+ workstreamTitles: normalizeTextArray(title),
455
+ taskIds: [],
456
+ milestoneIds: [],
457
+ iwmtIds: [],
458
+ sliceRunId: runId,
459
+ sessionId: normalizeText(session.id) ?? null,
460
+ },
461
+ artifacts: [],
462
+ artifactCount: 0,
463
+ hasArtifact: false,
464
+ actionContract: outcomeState === "needs_input"
465
+ ? {
466
+ actionType: "provide_context",
467
+ label: "Provide context",
468
+ payloadSchema: { requires: ["note"] },
469
+ primary: true,
470
+ }
471
+ : outcomeState === "failed_actionable" || outcomeState === "failed_non_actionable"
472
+ ? {
473
+ actionType: "retry",
474
+ label: "Retry slice",
475
+ payloadSchema: { requires: ["sliceRunId"], optional: ["note"] },
476
+ primary: true,
477
+ }
478
+ : null,
479
+ updatedAt: session.updatedAt ?? session.lastEventAt ?? session.startedAt ?? null,
480
+ completedAt: lifecycleState === "completed" ? session.updatedAt ?? session.lastEventAt ?? null : null,
481
+ failedAt: lifecycleState === "failed" ? session.updatedAt ?? session.lastEventAt ?? null : null,
482
+ archivedAt: lifecycleState === "archived" ? session.updatedAt ?? session.lastEventAt ?? null : null,
483
+ runtimeState: normalizeText(session.state),
484
+ sourceClient: normalizeText(session.runtimeClient),
485
+ confidence: "low",
486
+ });
487
+ projectionByRunId.add(runId);
488
+ }
489
+ for (const instance of input.runtimeInstances) {
490
+ const runId = normalizeText(instance.runId) ?? normalizeText(instance.correlationId);
491
+ if (!runId || projectionByRunId.has(runId))
492
+ continue;
493
+ const hasScope = Boolean(normalizeText(instance.initiativeId)) ||
494
+ Boolean(normalizeText(instance.workstreamId)) ||
495
+ Boolean(normalizeText(instance.taskId));
496
+ const lifecycleState = instance.state === "stopped" || (instance.event ?? "").toLowerCase() === "session_stop"
497
+ ? "completed"
498
+ : instance.state === "error" || (instance.event ?? "").toLowerCase() === "error"
499
+ ? "failed"
500
+ : instance.state === "stale"
501
+ ? "archived"
502
+ : "running";
503
+ const outcomeState = deriveOutcomeState(lifecycleState, false, 0);
504
+ const sliceKind = hasScope ? "work_slice" : "runtime_reporting";
505
+ const sourceClient = normalizeText(instance.sourceClient) ?? "unknown";
506
+ const displayName = sourceClient === "codex"
507
+ ? "Codex Agent"
508
+ : sourceClient === "claude-code"
509
+ ? "Claude Code Agent"
510
+ : sourceClient === "openclaw"
511
+ ? "OpenClaw Agent"
512
+ : sourceClient === "api"
513
+ ? "OrgX API"
514
+ : "OrgX System";
515
+ const actorType = sourceClient === "codex" ||
516
+ sourceClient === "claude-code" ||
517
+ sourceClient === "openclaw"
518
+ ? "system"
519
+ : sourceClient === "api"
520
+ ? "orgx"
521
+ : "orgx";
522
+ projections.push({
523
+ projectionVersion: 1,
524
+ lastEventId: null,
525
+ consistencyFlags: [],
526
+ sliceRunId: runId,
527
+ runId,
528
+ sliceKind,
529
+ lifecycleState,
530
+ outcomeState,
531
+ statusExplainer: normalizeText(instance.lastMessage) ??
532
+ defaultStatusExplainer(lifecycleState),
533
+ actorProvenance: {
534
+ actorType,
535
+ actorId: sourceClient,
536
+ displayName,
537
+ avatarKey: sourceClient === "openclaw" ? "openclaw-system" : `agent:${sourceClient}`,
538
+ },
539
+ lineage: {
540
+ initiativeIds: normalizeTextArray(instance.initiativeId),
541
+ initiativeTitles: [],
542
+ workstreamIds: normalizeTextArray(instance.workstreamId),
543
+ workstreamTitles: [],
544
+ taskIds: normalizeTextArray(instance.taskId),
545
+ milestoneIds: [],
546
+ iwmtIds: [],
547
+ sliceRunId: runId,
548
+ sessionId: null,
549
+ },
550
+ artifacts: [],
551
+ artifactCount: 0,
552
+ hasArtifact: false,
553
+ actionContract: null,
554
+ updatedAt: instance.updatedAt ?? instance.lastEventAt ?? null,
555
+ completedAt: lifecycleState === "completed" ? instance.updatedAt ?? instance.lastEventAt ?? null : null,
556
+ failedAt: lifecycleState === "failed" ? instance.updatedAt ?? instance.lastEventAt ?? null : null,
557
+ archivedAt: lifecycleState === "archived" ? instance.updatedAt ?? instance.lastEventAt ?? null : null,
558
+ runtimeState: normalizeText(instance.state),
559
+ sourceClient,
560
+ confidence: "low",
561
+ });
562
+ projectionByRunId.add(runId);
563
+ }
564
+ projections.sort((a, b) => toEpoch(b.updatedAt) - toEpoch(a.updatedAt));
565
+ const inProgress = projections.filter((slice) => slice.sliceKind === "work_slice" && slice.lifecycleState === "running");
566
+ const needsInputItems = projections.filter((slice) => slice.outcomeState === "needs_input");
567
+ const failedItems = projections.filter((slice) => slice.outcomeState === "failed_actionable" ||
568
+ slice.outcomeState === "failed_non_actionable");
569
+ const completedToday = projections.filter((slice) => {
570
+ if (slice.lifecycleState !== "completed")
571
+ return false;
572
+ const epoch = toEpoch(slice.completedAt ?? slice.updatedAt);
573
+ if (!epoch)
574
+ return false;
575
+ const now = new Date();
576
+ const then = new Date(epoch);
577
+ return (now.getUTCFullYear() === then.getUTCFullYear() &&
578
+ now.getUTCMonth() === then.getUTCMonth() &&
579
+ now.getUTCDate() === then.getUTCDate());
580
+ }).length;
581
+ const nextUpByInitiativeMap = new Map();
582
+ for (const rawItem of input.nextUpItems) {
583
+ const initiativeId = normalizeText(rawItem.initiativeId) ?? null;
584
+ const initiativeTitle = normalizeText(rawItem.initiativeTitle) ??
585
+ normalizeText(rawItem.groupLabel) ??
586
+ (initiativeId ?? "Unscoped initiative");
587
+ const key = initiativeId ?? "unscoped";
588
+ const existing = nextUpByInitiativeMap.get(key) ?? {
589
+ initiativeId,
590
+ initiativeTitle,
591
+ queue: [],
592
+ };
593
+ existing.queue.push({
594
+ workstreamId: normalizeText(rawItem.workstreamId) ?? null,
595
+ workstreamTitle: normalizeText(rawItem.workstreamTitle) ??
596
+ normalizeText(rawItem.title) ??
597
+ "Work slice",
598
+ queueState: normalizeText(rawItem.queueState) ?? "queued",
599
+ priorityNum: typeof rawItem.priorityNum === "number" && Number.isFinite(rawItem.priorityNum)
600
+ ? rawItem.priorityNum
601
+ : null,
602
+ dependencySummary: normalizeText(rawItem.dependencySummary) ?? normalizeText(rawItem.waitingOn) ?? null,
603
+ tasksRemaining: typeof rawItem.tasksRemaining === "number" && Number.isFinite(rawItem.tasksRemaining)
604
+ ? rawItem.tasksRemaining
605
+ : null,
606
+ });
607
+ nextUpByInitiativeMap.set(key, existing);
608
+ }
609
+ const nextUpByInitiative = Array.from(nextUpByInitiativeMap.values())
610
+ .map((entry) => ({
611
+ initiativeId: entry.initiativeId,
612
+ initiativeTitle: entry.initiativeTitle,
613
+ pendingCount: entry.queue.length,
614
+ queue: entry.queue.sort((a, b) => {
615
+ const aPriority = typeof a.priorityNum === "number" ? a.priorityNum : Number.MAX_SAFE_INTEGER;
616
+ const bPriority = typeof b.priorityNum === "number" ? b.priorityNum : Number.MAX_SAFE_INTEGER;
617
+ return aPriority - bPriority;
618
+ }),
619
+ }))
620
+ .sort((a, b) => b.pendingCount - a.pendingCount);
621
+ const timelineNarrative = buildTimelineNarrative(projections, input.activity);
622
+ const allConsistencyFlags = new Set();
623
+ let missingTerminal = 0;
624
+ let lineageGap = 0;
625
+ let artifactMismatch = 0;
626
+ let invalidActor = 0;
627
+ for (const projection of projections) {
628
+ for (const flag of projection.consistencyFlags)
629
+ allConsistencyFlags.add(flag);
630
+ if (projection.consistencyFlags.includes("missing_terminal"))
631
+ missingTerminal += 1;
632
+ if (projection.consistencyFlags.includes("lineage_gap"))
633
+ lineageGap += 1;
634
+ if (projection.consistencyFlags.includes("artifact_mismatch"))
635
+ artifactMismatch += 1;
636
+ if (projection.consistencyFlags.includes("invalid_actor"))
637
+ invalidActor += 1;
638
+ }
639
+ const consistencyFlags = Array.from(allConsistencyFlags.values());
640
+ const degraded = consistencyFlags.length > 0;
641
+ return {
642
+ generatedAt: input.generatedAt,
643
+ runningWorkSlices: inProgress.length,
644
+ needsInput: needsInputItems.length,
645
+ failedActionable: failedItems.length,
646
+ completedToday,
647
+ inProgress,
648
+ needsInputItems,
649
+ failedItems,
650
+ nextUpByInitiative,
651
+ timelineNarrative,
652
+ consistencyFlags,
653
+ dataHealth: {
654
+ status: degraded ? "degraded" : "healthy",
655
+ totals: {
656
+ slices: projections.length,
657
+ missingTerminal,
658
+ lineageGap,
659
+ artifactMismatch,
660
+ invalidActor,
661
+ },
662
+ },
663
+ projections,
664
+ };
665
+ }
666
+ export function findSliceNarrative(timelineNarrative, sliceRunId) {
667
+ const normalized = sliceRunId.trim();
668
+ if (!normalized)
669
+ return null;
670
+ return timelineNarrative.find((entry) => entry.sliceRunId === normalized) ?? null;
671
+ }
672
+ export function findSessionDetailProjection(projections, sessionId) {
673
+ const normalized = sessionId.trim();
674
+ if (!normalized)
675
+ return null;
676
+ return projections.find((entry) => entry.lineage.sessionId === normalized) ?? null;
677
+ }