@useorgx/openclaw-plugin 0.4.8 → 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 (284) 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 +38 -26
  88. package/dist/agent-context-store.js +84 -42
  89. package/dist/agent-run-store.js +49 -28
  90. package/dist/agent-suite.d.ts +9 -0
  91. package/dist/agent-suite.js +150 -17
  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/auth/flows.d.ts +47 -0
  97. package/dist/auth/flows.js +169 -0
  98. package/dist/auth-store.js +6 -26
  99. package/dist/byok-store.js +5 -19
  100. package/dist/chat-store.d.ts +157 -0
  101. package/dist/chat-store.js +586 -0
  102. package/dist/cli/orgx.d.ts +66 -0
  103. package/dist/cli/orgx.js +102 -0
  104. package/dist/config/refresh.d.ts +32 -0
  105. package/dist/config/refresh.js +55 -0
  106. package/dist/config/resolution.d.ts +37 -0
  107. package/dist/config/resolution.js +178 -0
  108. package/dist/contracts/client.d.ts +43 -3
  109. package/dist/contracts/client.js +159 -30
  110. package/dist/contracts/retro-schema.d.ts +81 -0
  111. package/dist/contracts/retro-schema.js +80 -0
  112. package/dist/contracts/shared-types.d.ts +306 -0
  113. package/dist/contracts/shared-types.js +179 -0
  114. package/dist/contracts/skill-pack-schema.d.ts +192 -0
  115. package/dist/contracts/skill-pack-schema.js +180 -0
  116. package/dist/contracts/types.d.ts +224 -132
  117. package/dist/contracts/types.js +5 -0
  118. package/dist/entities/auto-assignment.d.ts +36 -0
  119. package/dist/entities/auto-assignment.js +141 -0
  120. package/dist/entity-comment-store.js +5 -25
  121. package/dist/event-sanitization.d.ts +11 -0
  122. package/dist/event-sanitization.js +113 -0
  123. package/dist/fs-utils.js +13 -1
  124. package/dist/gateway-watchdog.d.ts +5 -0
  125. package/dist/gateway-watchdog.js +50 -0
  126. package/dist/hash-utils.d.ts +2 -0
  127. package/dist/hash-utils.js +12 -0
  128. package/dist/hooks/post-reporting-event.mjs +1 -5
  129. package/dist/http/helpers/activity-headline.d.ts +10 -0
  130. package/dist/http/helpers/activity-headline.js +73 -0
  131. package/dist/http/helpers/artifact-fallback.d.ts +13 -0
  132. package/dist/http/helpers/artifact-fallback.js +148 -0
  133. package/dist/http/helpers/auto-continue-engine.d.ts +486 -0
  134. package/dist/http/helpers/auto-continue-engine.js +3563 -0
  135. package/dist/http/helpers/autopilot-operations.d.ts +176 -0
  136. package/dist/http/helpers/autopilot-operations.js +554 -0
  137. package/dist/http/helpers/autopilot-runtime.d.ts +43 -0
  138. package/dist/http/helpers/autopilot-runtime.js +607 -0
  139. package/dist/http/helpers/autopilot-slice-utils.d.ts +56 -0
  140. package/dist/http/helpers/autopilot-slice-utils.js +899 -0
  141. package/dist/http/helpers/decision-mapper.d.ts +52 -0
  142. package/dist/http/helpers/decision-mapper.js +260 -0
  143. package/dist/http/helpers/dispatch-lifecycle.d.ts +119 -0
  144. package/dist/http/helpers/dispatch-lifecycle.js +809 -0
  145. package/dist/http/helpers/hash-utils.d.ts +1 -0
  146. package/dist/http/helpers/hash-utils.js +1 -0
  147. package/dist/http/helpers/kickoff-context.d.ts +12 -0
  148. package/dist/http/helpers/kickoff-context.js +228 -0
  149. package/dist/http/helpers/llm-client.d.ts +47 -0
  150. package/dist/http/helpers/llm-client.js +256 -0
  151. package/dist/http/helpers/mission-control.d.ts +193 -0
  152. package/dist/http/helpers/mission-control.js +1383 -0
  153. package/dist/http/helpers/openclaw-cli.d.ts +37 -0
  154. package/dist/http/helpers/openclaw-cli.js +283 -0
  155. package/dist/http/helpers/runtime-sse.d.ts +20 -0
  156. package/dist/http/helpers/runtime-sse.js +110 -0
  157. package/dist/http/helpers/sentinel-catalog.d.ts +23 -0
  158. package/dist/http/helpers/sentinel-catalog.js +193 -0
  159. package/dist/http/helpers/session-classification.d.ts +9 -0
  160. package/dist/http/helpers/session-classification.js +564 -0
  161. package/dist/http/helpers/slice-experience-v2.d.ts +137 -0
  162. package/dist/http/helpers/slice-experience-v2.js +677 -0
  163. package/dist/http/helpers/slice-run-projections.d.ts +72 -0
  164. package/dist/http/helpers/slice-run-projections.js +860 -0
  165. package/dist/http/helpers/triage-mapper.d.ts +43 -0
  166. package/dist/http/helpers/triage-mapper.js +549 -0
  167. package/dist/http/helpers/value-utils.d.ts +6 -0
  168. package/dist/http/helpers/value-utils.js +72 -0
  169. package/dist/http/helpers/workspace-scope.d.ts +15 -0
  170. package/dist/http/helpers/workspace-scope.js +170 -0
  171. package/dist/http/index.d.ts +88 -0
  172. package/dist/http/index.js +3610 -0
  173. package/dist/http/router.d.ts +23 -0
  174. package/dist/http/router.js +23 -0
  175. package/dist/http/routes/agent-control.d.ts +79 -0
  176. package/dist/http/routes/agent-control.js +684 -0
  177. package/dist/http/routes/agent-suite.d.ts +38 -0
  178. package/dist/http/routes/agent-suite.js +397 -0
  179. package/dist/http/routes/agents-catalog.d.ts +40 -0
  180. package/dist/http/routes/agents-catalog.js +128 -0
  181. package/dist/http/routes/billing.d.ts +23 -0
  182. package/dist/http/routes/billing.js +55 -0
  183. package/dist/http/routes/chat.d.ts +19 -0
  184. package/dist/http/routes/chat.js +522 -0
  185. package/dist/http/routes/debug.d.ts +14 -0
  186. package/dist/http/routes/debug.js +21 -0
  187. package/dist/http/routes/decision-actions.d.ts +20 -0
  188. package/dist/http/routes/decision-actions.js +103 -0
  189. package/dist/http/routes/delegation.d.ts +19 -0
  190. package/dist/http/routes/delegation.js +32 -0
  191. package/dist/http/routes/dispatch-gateway-envelope.d.ts +25 -0
  192. package/dist/http/routes/dispatch-gateway-envelope.js +26 -0
  193. package/dist/http/routes/entities.d.ts +63 -0
  194. package/dist/http/routes/entities.js +440 -0
  195. package/dist/http/routes/entity-dynamic.d.ts +25 -0
  196. package/dist/http/routes/entity-dynamic.js +191 -0
  197. package/dist/http/routes/health.d.ts +22 -0
  198. package/dist/http/routes/health.js +49 -0
  199. package/dist/http/routes/live-legacy.d.ts +115 -0
  200. package/dist/http/routes/live-legacy.js +112 -0
  201. package/dist/http/routes/live-misc.d.ts +81 -0
  202. package/dist/http/routes/live-misc.js +426 -0
  203. package/dist/http/routes/live-snapshot.d.ts +136 -0
  204. package/dist/http/routes/live-snapshot.js +916 -0
  205. package/dist/http/routes/live-terminal.d.ts +11 -0
  206. package/dist/http/routes/live-terminal.js +261 -0
  207. package/dist/http/routes/live-triage.d.ts +61 -0
  208. package/dist/http/routes/live-triage.js +248 -0
  209. package/dist/http/routes/mission-control-actions.d.ts +131 -0
  210. package/dist/http/routes/mission-control-actions.js +1791 -0
  211. package/dist/http/routes/mission-control-read.d.ts +73 -0
  212. package/dist/http/routes/mission-control-read.js +1640 -0
  213. package/dist/http/routes/onboarding.d.ts +34 -0
  214. package/dist/http/routes/onboarding.js +101 -0
  215. package/dist/http/routes/realtime-orchestrator.d.ts +10 -0
  216. package/dist/http/routes/realtime-orchestrator.js +74 -0
  217. package/dist/http/routes/run-control.d.ts +27 -0
  218. package/dist/http/routes/run-control.js +96 -0
  219. package/dist/http/routes/runtime-hooks.d.ts +69 -0
  220. package/dist/http/routes/runtime-hooks.js +437 -0
  221. package/dist/http/routes/sentinels-catalog.d.ts +7 -0
  222. package/dist/http/routes/sentinels-catalog.js +24 -0
  223. package/dist/http/routes/settings-byok.d.ts +23 -0
  224. package/dist/http/routes/settings-byok.js +163 -0
  225. package/dist/http/routes/summary.d.ts +18 -0
  226. package/dist/http/routes/summary.js +49 -0
  227. package/dist/http/routes/usage.d.ts +24 -0
  228. package/dist/http/routes/usage.js +362 -0
  229. package/dist/http/routes/work-artifacts.d.ts +9 -0
  230. package/dist/http/routes/work-artifacts.js +55 -0
  231. package/dist/http/shared-state.d.ts +16 -0
  232. package/dist/http/shared-state.js +1 -0
  233. package/dist/http-handler.d.ts +1 -88
  234. package/dist/http-handler.js +1 -10605
  235. package/dist/index.js +287 -2284
  236. package/dist/json-utils.d.ts +1 -0
  237. package/dist/json-utils.js +8 -0
  238. package/dist/local-openclaw.js +29 -6
  239. package/dist/mcp-client-setup.js +3 -3
  240. package/dist/mcp-http-handler.js +33 -59
  241. package/dist/next-up-queue-store.d.ts +16 -1
  242. package/dist/next-up-queue-store.js +93 -25
  243. package/dist/outbox.d.ts +5 -0
  244. package/dist/outbox.js +113 -9
  245. package/dist/paths.js +24 -5
  246. package/dist/reporting/rollups.d.ts +53 -0
  247. package/dist/reporting/rollups.js +148 -0
  248. package/dist/retro/domain-templates.d.ts +45 -0
  249. package/dist/retro/domain-templates.js +297 -0
  250. package/dist/retro/quality-rubric.d.ts +33 -0
  251. package/dist/retro/quality-rubric.js +213 -0
  252. package/dist/runtime-cleanup.d.ts +18 -0
  253. package/dist/runtime-cleanup.js +87 -0
  254. package/dist/runtime-instance-store.js +5 -31
  255. package/dist/services/background.d.ts +34 -0
  256. package/dist/services/background.js +45 -0
  257. package/dist/services/experiment-randomization.d.ts +21 -0
  258. package/dist/services/experiment-randomization.js +63 -0
  259. package/dist/services/instrumentation.d.ts +29 -0
  260. package/dist/services/instrumentation.js +136 -0
  261. package/dist/skill-pack-state.d.ts +36 -5
  262. package/dist/skill-pack-state.js +273 -29
  263. package/dist/snapshot-store.js +5 -25
  264. package/dist/stores/json-store.d.ts +11 -0
  265. package/dist/stores/json-store.js +42 -0
  266. package/dist/sync/local-agent-telemetry.d.ts +13 -0
  267. package/dist/sync/local-agent-telemetry.js +128 -0
  268. package/dist/sync/outbox-replay.d.ts +55 -0
  269. package/dist/sync/outbox-replay.js +621 -0
  270. package/dist/team-context-store.d.ts +23 -0
  271. package/dist/team-context-store.js +116 -0
  272. package/dist/telemetry/posthog.js +4 -2
  273. package/dist/tools/core-tools.d.ts +72 -0
  274. package/dist/tools/core-tools.js +2270 -0
  275. package/dist/types.d.ts +2 -0
  276. package/dist/types.js +2 -0
  277. package/dist/worker-supervisor.js +23 -0
  278. package/package.json +14 -4
  279. package/dashboard/dist/assets/B3ziCA02.js +0 -8
  280. package/dashboard/dist/assets/BNeJ0kpF.js +0 -1
  281. package/dashboard/dist/assets/BzkiMPmM.js +0 -215
  282. package/dashboard/dist/assets/CUV9IHHi.js +0 -1
  283. package/dashboard/dist/assets/Ie7d9Iq2.css +0 -1
  284. package/dashboard/dist/assets/sAhvFnpk.js +0 -4
@@ -0,0 +1,440 @@
1
+ import { resolveWorkspaceScope, workspaceScopeFromHeaders, } from "../helpers/workspace-scope.js";
2
+ import { cascadeProgressFromChildren, deriveLifecycleState, } from "../../reporting/rollups.js";
3
+ const WORKSTREAM_REASSIGNMENT_FIELDS = [
4
+ "domain",
5
+ "role",
6
+ "assigned_agents",
7
+ "assignedAgents",
8
+ "assigned_agent_ids",
9
+ "assignedAgentIds",
10
+ "assigned_agent_names",
11
+ "assignedAgentNames",
12
+ ];
13
+ function hasOwn(input, key) {
14
+ return Object.prototype.hasOwnProperty.call(input, key);
15
+ }
16
+ function hasMeaningfulReassignmentValue(value) {
17
+ if (typeof value === "string")
18
+ return value.trim().length > 0;
19
+ if (Array.isArray(value))
20
+ return value.length > 0;
21
+ return value !== null && value !== undefined;
22
+ }
23
+ function includesWorkstreamReassignmentMutation(payload) {
24
+ return WORKSTREAM_REASSIGNMENT_FIELDS.some((field) => hasOwn(payload, field) && hasMeaningfulReassignmentValue(payload[field]));
25
+ }
26
+ function isActiveOrReadyStatus(status) {
27
+ const normalized = (status ?? "").trim().toLowerCase();
28
+ if (!normalized)
29
+ return false;
30
+ return (normalized === "active" ||
31
+ normalized === "ready" ||
32
+ normalized === "in_progress" ||
33
+ normalized === "running" ||
34
+ normalized === "queued" ||
35
+ normalized === "pending");
36
+ }
37
+ function toObjectArray(input) {
38
+ if (!Array.isArray(input))
39
+ return [];
40
+ return input.filter((item) => Boolean(item) && typeof item === "object");
41
+ }
42
+ export function registerEntitiesRoutes(router, deps) {
43
+ router.add("POST", "entities", async ({ req, res }) => {
44
+ try {
45
+ const payload = await deps.parseJsonRequest(req);
46
+ const type = deps.pickString(payload, ["type"]);
47
+ const title = deps.pickString(payload, ["title", "name"]);
48
+ if (!type || !title) {
49
+ deps.sendJson(res, 400, {
50
+ error: "Both 'type' and 'title' are required.",
51
+ });
52
+ return;
53
+ }
54
+ const data = deps.normalizeEntityMutationPayload({ ...payload, title });
55
+ delete data.type;
56
+ let entity = await deps.client.createEntity(type, data);
57
+ let autoAssignment = null;
58
+ if (type === "initiative" || type === "workstream") {
59
+ const entityRecord = entity;
60
+ autoAssignment = await deps.resolveAutoAssignments({
61
+ entityId: String(entityRecord.id ?? ""),
62
+ entityType: type,
63
+ initiativeId: type === "initiative"
64
+ ? String(entityRecord.id ?? "")
65
+ : deps.pickString(data, ["initiative_id", "initiativeId"]),
66
+ title: deps.pickString(entityRecord, ["title", "name"]) ?? title ?? "Untitled",
67
+ summary: deps.pickString(entityRecord, ["summary", "description", "context"]) ?? null,
68
+ });
69
+ if (autoAssignment.updated_entity) {
70
+ entity = autoAssignment.updated_entity;
71
+ }
72
+ }
73
+ deps.sendJson(res, 201, { ok: true, entity, auto_assignment: autoAssignment });
74
+ }
75
+ catch (err) {
76
+ deps.sendJson(res, 500, {
77
+ error: deps.safeErrorMessage(err),
78
+ });
79
+ }
80
+ }, "Create entity");
81
+ router.add("PATCH", "entities", async ({ req, res }) => {
82
+ let payload = {};
83
+ let type = null;
84
+ let id = null;
85
+ let requestedStatus = null;
86
+ try {
87
+ payload = await deps.parseJsonRequest(req);
88
+ type = deps.pickString(payload, ["type"]);
89
+ id = deps.pickString(payload, ["id"]);
90
+ requestedStatus = deps.pickString(payload, ["status"]);
91
+ if (!type || !id) {
92
+ deps.sendJson(res, 400, {
93
+ error: "Both 'type' and 'id' are required for PATCH.",
94
+ });
95
+ return;
96
+ }
97
+ const updates = { ...payload };
98
+ delete updates.type;
99
+ delete updates.id;
100
+ const normalizedType = type.trim().toLowerCase();
101
+ const normalizedUpdates = deps.normalizeEntityMutationPayload(updates);
102
+ const entity = await deps.client.updateEntity(type, id, normalizedUpdates);
103
+ let reassignment = null;
104
+ let initiativeReassignment = null;
105
+ if (normalizedType === "workstream" &&
106
+ deps.scheduleWorkstreamReassignment &&
107
+ includesWorkstreamReassignmentMutation(normalizedUpdates)) {
108
+ const entityRecord = entity && typeof entity === "object"
109
+ ? entity
110
+ : {};
111
+ const initiativeId = deps.pickString(entityRecord, ["initiative_id", "initiativeId"]) ??
112
+ deps.pickString(normalizedUpdates, ["initiative_id", "initiativeId"]) ??
113
+ null;
114
+ const workstreamStatus = deps.pickString(entityRecord, ["status"]) ??
115
+ deps.pickString(normalizedUpdates, ["status"]) ??
116
+ null;
117
+ if (!initiativeId) {
118
+ reassignment = { scheduled: false, reason: "missing_initiative" };
119
+ }
120
+ else if (!isActiveOrReadyStatus(workstreamStatus)) {
121
+ reassignment = { scheduled: false, reason: "workstream_not_active_or_ready" };
122
+ }
123
+ else {
124
+ try {
125
+ const scheduled = await deps.scheduleWorkstreamReassignment({
126
+ initiativeId,
127
+ workstreamId: id,
128
+ status: workstreamStatus,
129
+ event: "workstream_reassigned",
130
+ });
131
+ reassignment = {
132
+ scheduled: true,
133
+ requestId: scheduled && typeof scheduled === "object"
134
+ ? (deps.pickString(scheduled, ["requestId"]) ?? null)
135
+ : null,
136
+ dueAt: scheduled && typeof scheduled === "object"
137
+ ? (deps.pickString(scheduled, ["dueAt"]) ?? null)
138
+ : null,
139
+ };
140
+ }
141
+ catch (reassignmentErr) {
142
+ reassignment = {
143
+ scheduled: false,
144
+ reason: `schedule_failed:${deps.safeErrorMessage(reassignmentErr)}`,
145
+ };
146
+ }
147
+ }
148
+ }
149
+ if (normalizedType === "initiative" &&
150
+ deps.scheduleWorkstreamReassignment &&
151
+ includesWorkstreamReassignmentMutation(normalizedUpdates)) {
152
+ const workstreams = await deps.client.listEntities("workstream", {
153
+ initiative_id: id,
154
+ limit: 200,
155
+ });
156
+ const workstreamRows = toObjectArray(workstreams && typeof workstreams === "object"
157
+ ? workstreams.data
158
+ : []);
159
+ const failures = [];
160
+ let requested = 0;
161
+ let scheduled = 0;
162
+ let skipped = 0;
163
+ for (const row of workstreamRows) {
164
+ const workstreamId = deps.pickString(row, ["id"]);
165
+ if (!workstreamId) {
166
+ skipped += 1;
167
+ continue;
168
+ }
169
+ const workstreamStatus = deps.pickString(row, ["status"]);
170
+ requested += 1;
171
+ if (!isActiveOrReadyStatus(workstreamStatus)) {
172
+ skipped += 1;
173
+ continue;
174
+ }
175
+ try {
176
+ await deps.scheduleWorkstreamReassignment({
177
+ initiativeId: id,
178
+ workstreamId,
179
+ status: workstreamStatus,
180
+ event: "initiative_reassigned",
181
+ });
182
+ scheduled += 1;
183
+ }
184
+ catch (reassignmentErr) {
185
+ failures.push(`${workstreamId}:${deps.safeErrorMessage(reassignmentErr)}`);
186
+ }
187
+ }
188
+ initiativeReassignment = {
189
+ triggered: true,
190
+ requested,
191
+ scheduled,
192
+ skipped,
193
+ failures,
194
+ };
195
+ }
196
+ if (normalizedType === "initiative") {
197
+ deps.clearLocalInitiativeStatusOverride(id);
198
+ }
199
+ // Post-status-transition hook: when a task status changes to
200
+ // completed/done, recompute cascaded progress for parent entities.
201
+ let cascadedProgress = null;
202
+ if (normalizedType === "task" && requestedStatus) {
203
+ const normalizedRequestedStatus = requestedStatus.trim().toLowerCase();
204
+ const isTerminal = normalizedRequestedStatus === "done" ||
205
+ normalizedRequestedStatus === "completed" ||
206
+ normalizedRequestedStatus === "cancelled" ||
207
+ normalizedRequestedStatus === "archived";
208
+ if (isTerminal) {
209
+ // Look up sibling tasks for the parent workstream/milestone/initiative
210
+ const entityRecord = entity && typeof entity === "object"
211
+ ? entity
212
+ : {};
213
+ const parentWorkstreamId = deps.pickString(entityRecord, ["workstream_id", "workstreamId"]) ??
214
+ deps.pickString(normalizedUpdates, ["workstream_id", "workstreamId"]);
215
+ const parentInitiativeId = deps.pickString(entityRecord, ["initiative_id", "initiativeId"]) ??
216
+ deps.pickString(normalizedUpdates, ["initiative_id", "initiativeId"]);
217
+ // Fetch sibling tasks to compute cascaded progress
218
+ const parentScope = parentWorkstreamId
219
+ ? { initiative_id: parentInitiativeId ?? undefined }
220
+ : parentInitiativeId
221
+ ? { initiative_id: parentInitiativeId }
222
+ : null;
223
+ if (parentScope) {
224
+ try {
225
+ const siblingResult = await deps.client.listEntities("task", {
226
+ ...parentScope,
227
+ limit: 500,
228
+ });
229
+ const siblingRows = toObjectArray(siblingResult && typeof siblingResult === "object"
230
+ ? siblingResult.data
231
+ : []);
232
+ const siblingStatuses = siblingRows
233
+ .filter((row) => {
234
+ if (!parentWorkstreamId)
235
+ return true;
236
+ const rowWsId = deps.pickString(row, ["workstream_id", "workstreamId"]);
237
+ return rowWsId === parentWorkstreamId;
238
+ })
239
+ .map((row) => deps.pickString(row, ["status"]) ?? "unknown");
240
+ const parentProgress = cascadeProgressFromChildren(siblingStatuses);
241
+ const parentLifecycleState = deriveLifecycleState(parentWorkstreamId ? "active" : (parentInitiativeId ? "active" : "active"), siblingStatuses);
242
+ cascadedProgress = {
243
+ parentProgress,
244
+ parentLifecycleState,
245
+ taskStatuses: siblingStatuses,
246
+ };
247
+ }
248
+ catch {
249
+ // Non-critical: cascade progress is best-effort
250
+ }
251
+ }
252
+ }
253
+ }
254
+ deps.sendJson(res, 200, {
255
+ ok: true,
256
+ entity,
257
+ reassignment,
258
+ initiative_reassignment: initiativeReassignment,
259
+ ...(cascadedProgress ? { cascaded_progress: cascadedProgress } : {}),
260
+ });
261
+ }
262
+ catch (err) {
263
+ if (type?.trim().toLowerCase() === "initiative" &&
264
+ id &&
265
+ requestedStatus &&
266
+ deps.isUnauthorizedOrgxError(err)) {
267
+ deps.setLocalInitiativeStatusOverride(id, requestedStatus);
268
+ deps.sendJson(res, 200, {
269
+ ok: true,
270
+ localFallback: true,
271
+ warning: deps.safeErrorMessage(err),
272
+ entity: {
273
+ id,
274
+ type,
275
+ status: requestedStatus,
276
+ },
277
+ });
278
+ return;
279
+ }
280
+ deps.sendJson(res, 500, {
281
+ error: deps.safeErrorMessage(err),
282
+ });
283
+ }
284
+ }, "Update entity");
285
+ async function renderEntityList(query, res, headerScope) {
286
+ const type = query.get("type");
287
+ if (!type) {
288
+ deps.sendJson(res, 400, {
289
+ error: "Query parameter 'type' is required for GET /entities.",
290
+ });
291
+ return;
292
+ }
293
+ const workspaceScope = resolveWorkspaceScope(query, headerScope, {
294
+ allowProjectScope: false,
295
+ });
296
+ if (workspaceScope.error) {
297
+ deps.sendJson(res, 400, { error: workspaceScope.error });
298
+ return;
299
+ }
300
+ const status = query.get("status") ?? undefined;
301
+ const id = query.get("id") ?? undefined;
302
+ const ids = query
303
+ .get("ids")
304
+ ?.split(",")
305
+ .map((value) => value.trim())
306
+ .filter((value) => value.length > 0);
307
+ const search = query.get("search")?.trim() || undefined;
308
+ const initiativeId = query.get("initiative_id") ?? undefined;
309
+ const workspaceId = workspaceScope.workspaceId ?? undefined;
310
+ const limit = query.get("limit") ? Number(query.get("limit")) : undefined;
311
+ const offset = query.get("offset") ? Number(query.get("offset")) : undefined;
312
+ try {
313
+ const data = await deps.client.listEntities(type, {
314
+ id,
315
+ ids,
316
+ search,
317
+ status,
318
+ initiative_id: initiativeId,
319
+ workspace_id: workspaceId,
320
+ command_center_id: workspaceId,
321
+ limit: Number.isFinite(limit) ? limit : undefined,
322
+ offset: Number.isFinite(offset) ? offset : undefined,
323
+ });
324
+ if (type.trim().toLowerCase() === "initiative") {
325
+ const payload = data;
326
+ const rawRows = Array.isArray(payload.data)
327
+ ? payload.data.filter((row) => Boolean(row && typeof row === "object"))
328
+ : [];
329
+ const filteredById = rawRows.filter((row) => {
330
+ const rowId = deps.pickString(row, ["id"])?.trim();
331
+ if (!rowId)
332
+ return false;
333
+ if (id && rowId !== id.trim())
334
+ return false;
335
+ if (ids && ids.length > 0 && !ids.includes(rowId))
336
+ return false;
337
+ return true;
338
+ });
339
+ const searchTokens = typeof search === "string" && search.length > 0
340
+ ? Array.from(new Set(search
341
+ .toLowerCase()
342
+ .split(/\s+/)
343
+ .map((token) => token.trim())
344
+ .filter((token) => token.length > 0)))
345
+ : [];
346
+ const searchedRows = searchTokens.length > 0
347
+ ? filteredById.filter((row) => {
348
+ const haystack = [
349
+ deps.pickString(row, ["title", "name"]) ?? "",
350
+ deps.pickString(row, ["summary", "description"]) ?? "",
351
+ deps.pickString(row, ["status"]) ?? "",
352
+ ]
353
+ .join(" ")
354
+ .toLowerCase();
355
+ return searchTokens.every((token) => haystack.includes(token));
356
+ })
357
+ : filteredById;
358
+ const workspaceScopeId = (workspaceId ?? "").trim();
359
+ const rows = workspaceScopeId.length > 0
360
+ ? searchedRows.filter((row) => {
361
+ const rowScope = deps.pickString(row, [
362
+ "workspace_id",
363
+ "workspaceId",
364
+ "command_center_id",
365
+ "commandCenterId",
366
+ ]) ?? "";
367
+ return rowScope.trim() === workspaceScopeId;
368
+ })
369
+ : searchedRows;
370
+ deps.sendJson(res, 200, {
371
+ ...payload,
372
+ data: deps.applyLocalInitiativeOverrides(rows),
373
+ pagination: payload.pagination && typeof payload.pagination === "object"
374
+ ? {
375
+ ...payload.pagination,
376
+ total: rows.length,
377
+ }
378
+ : payload.pagination,
379
+ });
380
+ return;
381
+ }
382
+ deps.sendJson(res, 200, data);
383
+ }
384
+ catch (err) {
385
+ if (type.trim().toLowerCase() === "initiative" && deps.isUnauthorizedOrgxError(err)) {
386
+ const snapshotInitiatives = deps
387
+ .formatInitiatives(deps.getSnapshot())
388
+ .map((item) => ({
389
+ id: item.id,
390
+ title: item.title,
391
+ name: item.title,
392
+ summary: null,
393
+ status: item.status,
394
+ progress_pct: item.progress ?? null,
395
+ created_at: null,
396
+ updated_at: null,
397
+ }))
398
+ .filter((item) => (initiativeId ? item.id === initiativeId : true))
399
+ .filter((item) => (id ? item.id === id : true))
400
+ .filter((item) => ids && ids.length > 0 ? ids.includes(item.id) : true)
401
+ .filter((item) => {
402
+ if (!search)
403
+ return true;
404
+ const searchTokens = search
405
+ .toLowerCase()
406
+ .split(/\s+/)
407
+ .map((token) => token.trim())
408
+ .filter((token) => token.length > 0);
409
+ if (searchTokens.length === 0)
410
+ return true;
411
+ const haystack = `${item.title} ${item.summary ?? ""} ${item.status ?? ""}`.toLowerCase();
412
+ return searchTokens.every((token) => haystack.includes(token));
413
+ });
414
+ deps.sendJson(res, 200, {
415
+ data: deps.applyLocalInitiativeOverrides(snapshotInitiatives),
416
+ localFallback: true,
417
+ warning: deps.safeErrorMessage(err),
418
+ });
419
+ return;
420
+ }
421
+ if (type.trim().toLowerCase() === "command_center") {
422
+ deps.sendJson(res, 200, {
423
+ data: [],
424
+ pagination: { total: 0, has_more: false },
425
+ localFallback: true,
426
+ warning: deps.safeErrorMessage(err),
427
+ });
428
+ return;
429
+ }
430
+ deps.sendJson(res, 500, {
431
+ error: deps.safeErrorMessage(err),
432
+ });
433
+ }
434
+ }
435
+ router.add("GET", "entities", async ({ query, res, req }) => renderEntityList(query, res, workspaceScopeFromHeaders(req?.headers)), "List entities");
436
+ router.add("HEAD", "entities", async ({ query, res, req }) => renderEntityList(query, res, workspaceScopeFromHeaders(req?.headers)), "List entities (HEAD)");
437
+ router.add("*", "entities", ({ res }) => {
438
+ deps.sendJson(res, 405, { error: "Method not allowed" });
439
+ }, "Reject unsupported methods for entities");
440
+ }
@@ -0,0 +1,25 @@
1
+ import type { Router } from "../router.js";
2
+ type JsonRecord = Record<string, unknown>;
3
+ type RegisterEntityDynamicRoutesDeps<TReq, TRes> = {
4
+ parseJsonRequest: (req: TReq) => Promise<JsonRecord>;
5
+ pickString: (input: Record<string, unknown>, keys: string[]) => string | null;
6
+ rawRequest: (method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE", path: string, body?: unknown) => Promise<unknown>;
7
+ listEntityComments: (entityType: string, entityId: string) => unknown[];
8
+ mergeEntityComments: (remote: unknown, local: unknown[]) => unknown[];
9
+ appendEntityComment: (input: {
10
+ entityType: string;
11
+ entityId: string;
12
+ body: string;
13
+ commentType: string;
14
+ severity: string;
15
+ tags: unknown;
16
+ }) => unknown;
17
+ updateEntity: (type: string, id: string, updates: Record<string, unknown>) => Promise<unknown>;
18
+ setLocalInitiativeStatusOverride: (initiativeId: string, status: string) => void;
19
+ clearLocalInitiativeStatusOverride: (initiativeId: string) => void;
20
+ isUnauthorizedOrgxError: (err: unknown) => boolean;
21
+ sendJson: (res: TRes, status: number, payload: unknown) => void;
22
+ safeErrorMessage: (err: unknown) => string;
23
+ };
24
+ export declare function registerEntityDynamicRoutes<TReq, TRes>(router: Router<Record<string, never>, TReq, TRes>, deps: RegisterEntityDynamicRoutesDeps<TReq, TRes>): void;
25
+ export {};
@@ -0,0 +1,191 @@
1
+ export function registerEntityDynamicRoutes(router, deps) {
2
+ router.add("*", "entities/*", async ({ req, res, path }) => {
3
+ const method = (req.method ?? "GET").toUpperCase();
4
+ const entityCommentsMatch = path.match(/^entities\/([^/]+)\/([^/]+)\/comments$/);
5
+ if (entityCommentsMatch) {
6
+ if (method !== "GET" && method !== "POST") {
7
+ deps.sendJson(res, 405, { error: "Method not allowed" });
8
+ return;
9
+ }
10
+ try {
11
+ const entityType = decodeURIComponent(entityCommentsMatch[1]);
12
+ const entityId = decodeURIComponent(entityCommentsMatch[2]);
13
+ if (!entityType || !entityId) {
14
+ deps.sendJson(res, 400, {
15
+ ok: false,
16
+ error: "entity type and id are required",
17
+ });
18
+ return;
19
+ }
20
+ const commentsPath = `/api/entities/${encodeURIComponent(entityType)}/${encodeURIComponent(entityId)}/comments`;
21
+ if (method === "GET") {
22
+ const local = deps.listEntityComments(entityType, entityId);
23
+ try {
24
+ const data = (await deps.rawRequest("GET", commentsPath));
25
+ const comments = deps.mergeEntityComments(data?.comments, local);
26
+ if (data && typeof data === "object") {
27
+ deps.sendJson(res, 200, { ...data, comments });
28
+ }
29
+ else {
30
+ deps.sendJson(res, 200, { status: "success", comments, nextCursor: null });
31
+ }
32
+ }
33
+ catch (err) {
34
+ deps.sendJson(res, 200, {
35
+ status: "success",
36
+ comments: local,
37
+ nextCursor: null,
38
+ localFallback: true,
39
+ warning: deps.safeErrorMessage(err),
40
+ });
41
+ }
42
+ return;
43
+ }
44
+ const payload = await deps.parseJsonRequest(req);
45
+ const body = deps.pickString(payload, ["body", "comment", "text", "message"]) ?? "";
46
+ if (!body.trim()) {
47
+ deps.sendJson(res, 400, { ok: false, error: "comment body is required" });
48
+ return;
49
+ }
50
+ const commentType = deps.pickString(payload, ["comment_type", "commentType", "type"]) ?? "note";
51
+ const severity = deps.pickString(payload, ["severity", "level"]) ?? "info";
52
+ const tags = payload.tags;
53
+ const normalizedPayload = {
54
+ body,
55
+ comment_type: commentType,
56
+ severity,
57
+ tags,
58
+ parent_comment_id: null,
59
+ };
60
+ try {
61
+ const data = await deps.rawRequest("POST", commentsPath, normalizedPayload);
62
+ deps.sendJson(res, 200, data);
63
+ }
64
+ catch (err) {
65
+ const warning = deps.safeErrorMessage(err);
66
+ try {
67
+ const comment = deps.appendEntityComment({
68
+ entityType,
69
+ entityId,
70
+ body,
71
+ commentType,
72
+ severity,
73
+ tags,
74
+ });
75
+ deps.sendJson(res, 200, {
76
+ status: "success",
77
+ comment,
78
+ localFallback: true,
79
+ warning,
80
+ });
81
+ }
82
+ catch (localErr) {
83
+ deps.sendJson(res, 500, {
84
+ ok: false,
85
+ error: warning || "Unable to save comment",
86
+ localError: deps.safeErrorMessage(localErr),
87
+ });
88
+ }
89
+ }
90
+ }
91
+ catch (err) {
92
+ deps.sendJson(res, 500, { ok: false, error: deps.safeErrorMessage(err) });
93
+ }
94
+ return;
95
+ }
96
+ const entityActionMatch = path.match(/^entities\/([^/]+)\/([^/]+)\/([^/]+)$/);
97
+ if (entityActionMatch) {
98
+ if (method !== "POST") {
99
+ deps.sendJson(res, 405, { error: "Method not allowed" });
100
+ return;
101
+ }
102
+ try {
103
+ const entityType = decodeURIComponent(entityActionMatch[1]);
104
+ const entityId = decodeURIComponent(entityActionMatch[2]);
105
+ const entityAction = decodeURIComponent(entityActionMatch[3]);
106
+ const payload = await deps.parseJsonRequest(req);
107
+ const normalizedEntityType = entityType.trim().toLowerCase();
108
+ if (entityAction === "delete") {
109
+ const deleteStatus = normalizedEntityType === "initiative" ? "archived" : "deleted";
110
+ try {
111
+ const entity = await deps.updateEntity(entityType, entityId, {
112
+ status: deleteStatus,
113
+ });
114
+ if (normalizedEntityType === "initiative") {
115
+ deps.clearLocalInitiativeStatusOverride(entityId);
116
+ }
117
+ deps.sendJson(res, 200, { ok: true, entity, deletedAsStatus: deleteStatus });
118
+ }
119
+ catch (err) {
120
+ if (normalizedEntityType === "initiative" &&
121
+ deps.isUnauthorizedOrgxError(err)) {
122
+ deps.setLocalInitiativeStatusOverride(entityId, deleteStatus);
123
+ deps.sendJson(res, 200, {
124
+ ok: true,
125
+ localFallback: true,
126
+ warning: deps.safeErrorMessage(err),
127
+ entity: {
128
+ id: entityId,
129
+ type: entityType,
130
+ status: deleteStatus,
131
+ },
132
+ deletedAsStatus: deleteStatus,
133
+ });
134
+ return;
135
+ }
136
+ throw err;
137
+ }
138
+ return;
139
+ }
140
+ const statusMap = {
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];
149
+ if (!newStatus) {
150
+ deps.sendJson(res, 400, {
151
+ error: `Unknown entity action: ${entityAction}`,
152
+ });
153
+ return;
154
+ }
155
+ try {
156
+ const entity = await deps.updateEntity(entityType, entityId, {
157
+ status: newStatus,
158
+ ...(payload.force ? { force: true } : {}),
159
+ });
160
+ if (normalizedEntityType === "initiative") {
161
+ deps.clearLocalInitiativeStatusOverride(entityId);
162
+ }
163
+ deps.sendJson(res, 200, { ok: true, entity });
164
+ }
165
+ catch (err) {
166
+ if (normalizedEntityType === "initiative" &&
167
+ deps.isUnauthorizedOrgxError(err)) {
168
+ deps.setLocalInitiativeStatusOverride(entityId, newStatus);
169
+ deps.sendJson(res, 200, {
170
+ ok: true,
171
+ localFallback: true,
172
+ warning: deps.safeErrorMessage(err),
173
+ entity: {
174
+ id: entityId,
175
+ type: entityType,
176
+ status: newStatus,
177
+ },
178
+ });
179
+ return;
180
+ }
181
+ throw err;
182
+ }
183
+ }
184
+ catch (err) {
185
+ deps.sendJson(res, 500, { error: deps.safeErrorMessage(err) });
186
+ }
187
+ return;
188
+ }
189
+ deps.sendJson(res, 404, { error: "Unknown API endpoint" });
190
+ }, "Dynamic entity comments/actions");
191
+ }
@@ -0,0 +1,22 @@
1
+ import type { Router } from "../router.js";
2
+ type HealthDiagnostics = {
3
+ getHealth?: (input: {
4
+ probeRemote: boolean;
5
+ }) => Promise<unknown>;
6
+ };
7
+ type HealthRouteDeps<TRes> = {
8
+ diagnostics?: HealthDiagnostics;
9
+ readOutboxSummary: () => Promise<{
10
+ pendingTotal: number;
11
+ pendingByQueue: Record<string, number>;
12
+ oldestEventAt: string | null;
13
+ newestEventAt: string | null;
14
+ }>;
15
+ parseBooleanQuery: (value: string | null) => boolean;
16
+ baseUrl: string;
17
+ hasApiKey: boolean;
18
+ sendJson: (res: TRes, status: number, payload: unknown) => void;
19
+ safeErrorMessage: (err: unknown) => string;
20
+ };
21
+ export declare function registerHealthRoutes<TReq, TRes>(router: Router<Record<string, never>, TReq, TRes>, deps: HealthRouteDeps<TRes>): void;
22
+ export {};