@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,809 @@
1
+ import { appendToOutbox } from "../../outbox.js";
2
+ import { computeMilestoneRollup, computeWorkstreamRollup, } from "../../reporting/rollups.js";
3
+ import { readSkillPackState } from "../../skill-pack-state.js";
4
+ import { normalizeDecisionActionType } from "../../contracts/shared-types.js";
5
+ import { buildMissionControlGraph, deriveExecutionPolicy, inferExecutionDomainFromText, normalizeExecutionDomain, ORGX_SKILL_BY_DOMAIN, spawnGuardIsRateLimited, summarizeSpawnGuardBlockReason, } from "./mission-control.js";
6
+ export function createDispatchLifecycle(deps) {
7
+ const decisionV2Enabled = String(process.env.DECISION_V2_ENABLED ?? "true").trim().toLowerCase() !== "false";
8
+ const decisionDedupeEnabled = String(process.env.DECISION_DEDUPE_ENABLED ?? "true").trim().toLowerCase() !== "false";
9
+ const decisionEvidenceRequiredForBlocking = String(process.env.DECISION_EVIDENCE_REQUIRED_FOR_BLOCKING ?? "false")
10
+ .trim()
11
+ .toLowerCase() === "true";
12
+ const asRecord = (value) => {
13
+ if (!value || typeof value !== "object" || Array.isArray(value))
14
+ return null;
15
+ return value;
16
+ };
17
+ const extractDecisionIdsFromChangesetResults = (value) => {
18
+ if (!Array.isArray(value))
19
+ return [];
20
+ const ids = [];
21
+ const seen = new Set();
22
+ const pushId = (candidate) => {
23
+ if (typeof candidate !== "string")
24
+ return;
25
+ const id = candidate.trim();
26
+ if (!id || seen.has(id))
27
+ return;
28
+ seen.add(id);
29
+ ids.push(id);
30
+ };
31
+ for (const entry of value) {
32
+ const record = asRecord(entry);
33
+ if (!record)
34
+ continue;
35
+ pushId(record.decision_id);
36
+ pushId(record.decisionId);
37
+ const directDecision = asRecord(record.decision);
38
+ if (directDecision) {
39
+ pushId(directDecision.id);
40
+ pushId(directDecision.decision_id);
41
+ pushId(directDecision.decisionId);
42
+ }
43
+ const entityType = (typeof record.entity_type === "string" ? record.entity_type : null) ??
44
+ (typeof record.entityType === "string" ? record.entityType : null);
45
+ if (entityType && entityType.trim().toLowerCase() === "decision") {
46
+ pushId(record.entity_id);
47
+ pushId(record.entityId);
48
+ pushId(record.id);
49
+ }
50
+ const created = asRecord(record.created);
51
+ if (created) {
52
+ const createdType = (typeof created.entity_type === "string" ? created.entity_type : null) ??
53
+ (typeof created.entityType === "string" ? created.entityType : null);
54
+ if (createdType && createdType.trim().toLowerCase() === "decision") {
55
+ pushId(created.entity_id);
56
+ pushId(created.entityId);
57
+ pushId(created.id);
58
+ }
59
+ }
60
+ }
61
+ return ids;
62
+ };
63
+ function withProvenanceMetadata(metadata) {
64
+ const input = metadata ?? {};
65
+ const out = { ...input };
66
+ if (out.orgx_plugin_version === undefined) {
67
+ out.orgx_plugin_version = (deps.pluginVersion ?? "").trim() || null;
68
+ }
69
+ try {
70
+ const state = readSkillPackState();
71
+ const overrides = state.overrides;
72
+ if (out.skill_pack_name === undefined) {
73
+ out.skill_pack_name = overrides?.name ?? state.pack?.name ?? null;
74
+ }
75
+ if (out.skill_pack_version === undefined) {
76
+ out.skill_pack_version = overrides?.version ?? state.pack?.version ?? null;
77
+ }
78
+ if (out.skill_pack_checksum === undefined) {
79
+ out.skill_pack_checksum = overrides?.checksum ?? state.pack?.checksum ?? null;
80
+ }
81
+ if (out.skill_pack_source === undefined) {
82
+ out.skill_pack_source = overrides?.source ?? null;
83
+ }
84
+ if (out.skill_pack_etag === undefined) {
85
+ out.skill_pack_etag = state.etag ?? null;
86
+ }
87
+ if (out.skill_pack_policy_frozen === undefined) {
88
+ out.skill_pack_policy_frozen = Boolean(state.policy?.frozen);
89
+ }
90
+ if (out.skill_pack_policy_pinned_checksum === undefined) {
91
+ out.skill_pack_policy_pinned_checksum = state.policy?.pinnedChecksum ?? null;
92
+ }
93
+ }
94
+ catch {
95
+ // best effort
96
+ }
97
+ if (out.orgx_provenance === undefined) {
98
+ out.orgx_provenance = {
99
+ plugin_version: out.orgx_plugin_version ?? null,
100
+ skill_pack: {
101
+ name: out.skill_pack_name ?? null,
102
+ version: out.skill_pack_version ?? null,
103
+ checksum: out.skill_pack_checksum ?? null,
104
+ source: out.skill_pack_source ?? null,
105
+ etag: out.skill_pack_etag ?? null,
106
+ },
107
+ policy: {
108
+ frozen: out.skill_pack_policy_frozen ?? null,
109
+ pinned_checksum: out.skill_pack_policy_pinned_checksum ?? null,
110
+ },
111
+ };
112
+ }
113
+ return Object.keys(out).length > 0 ? out : undefined;
114
+ }
115
+ async function emitActivitySafe(input) {
116
+ const initiativeId = input.initiativeId?.trim() ?? "";
117
+ if (!initiativeId)
118
+ return;
119
+ const message = input.message.trim();
120
+ if (!message)
121
+ return;
122
+ const normalizedRunId = input.runId?.trim() || null;
123
+ const normalizedCorrelationId = normalizedRunId ? null : (input.correlationId?.trim() || `openclaw-${Date.now()}`);
124
+ const canonicalMetadata = {
125
+ initiative_id: initiativeId,
126
+ run_id: normalizedRunId,
127
+ slice_run_id: normalizedRunId,
128
+ correlation_id: normalizedCorrelationId,
129
+ };
130
+ const metadataWithProvenance = withProvenanceMetadata(input.metadata);
131
+ const activityBucket = deps.deriveStructuredActivityBucket({
132
+ phase: input.phase,
133
+ metadata: metadataWithProvenance,
134
+ explicitBucket: input.activityBucket ?? null,
135
+ });
136
+ const enrichedMetadata = {
137
+ ...(metadataWithProvenance ?? {}),
138
+ ...canonicalMetadata,
139
+ activity_bucket: activityBucket,
140
+ };
141
+ try {
142
+ await deps.client.emitActivity({
143
+ initiative_id: initiativeId,
144
+ run_id: normalizedRunId ?? undefined,
145
+ correlation_id: normalizedCorrelationId ?? undefined,
146
+ source_client: "openclaw",
147
+ message,
148
+ phase: input.phase,
149
+ progress_pct: typeof input.progressPct === "number" && Number.isFinite(input.progressPct)
150
+ ? Math.max(0, Math.min(100, Math.round(input.progressPct)))
151
+ : undefined,
152
+ level: input.level,
153
+ next_step: input.nextStep,
154
+ metadata: enrichedMetadata,
155
+ });
156
+ }
157
+ catch (err) {
158
+ const msg = deps.safeErrorMessage(err);
159
+ const runId = normalizedRunId ?? "";
160
+ // If OrgX rejects an unknown run_id, retry by treating the local run_id as a
161
+ // correlation key (non-UUID) so OrgX can create/attach a run deterministically.
162
+ if (runId && /^404\b/.test(msg) && /\brun\b/i.test(msg) && /not found/i.test(msg)) {
163
+ const retryCorrelationId = `openclaw_run_${deps.stableHash(runId).slice(0, 24)}`;
164
+ try {
165
+ await deps.client.emitActivity({
166
+ initiative_id: initiativeId,
167
+ run_id: undefined,
168
+ correlation_id: retryCorrelationId,
169
+ source_client: "openclaw",
170
+ message,
171
+ phase: input.phase,
172
+ progress_pct: typeof input.progressPct === "number" && Number.isFinite(input.progressPct)
173
+ ? Math.max(0, Math.min(100, Math.round(input.progressPct)))
174
+ : undefined,
175
+ level: input.level,
176
+ next_step: input.nextStep,
177
+ metadata: {
178
+ ...(enrichedMetadata ?? {}),
179
+ replay_run_id_as_correlation: true,
180
+ },
181
+ });
182
+ return;
183
+ }
184
+ catch {
185
+ // Fall through to local outbox.
186
+ }
187
+ }
188
+ // Fall back to local outbox so activity is still visible in Mission Control/Activity.
189
+ try {
190
+ const timestamp = new Date().toISOString();
191
+ const runId = normalizedRunId || normalizedCorrelationId || null;
192
+ const activityItem = {
193
+ id: deps.randomUUID(),
194
+ type: input.phase === "completed"
195
+ ? "run_completed"
196
+ : input.phase === "blocked"
197
+ ? "run_failed"
198
+ : "run_started",
199
+ title: message,
200
+ description: input.nextStep ?? null,
201
+ agentId: (typeof metadataWithProvenance?.agent_id === "string"
202
+ ? metadataWithProvenance.agent_id
203
+ : null) ?? null,
204
+ agentName: (typeof metadataWithProvenance?.agent_name === "string"
205
+ ? metadataWithProvenance.agent_name
206
+ : null) ?? null,
207
+ requesterAgentId: null,
208
+ requesterAgentName: null,
209
+ executorAgentId: (typeof metadataWithProvenance?.agent_id === "string"
210
+ ? metadataWithProvenance.agent_id
211
+ : null) ?? null,
212
+ executorAgentName: (typeof metadataWithProvenance?.agent_name === "string"
213
+ ? metadataWithProvenance.agent_name
214
+ : null) ?? null,
215
+ runId,
216
+ initiativeId,
217
+ timestamp,
218
+ phase: input.phase,
219
+ summary: message,
220
+ kind: activityBucket,
221
+ metadata: {
222
+ ...(enrichedMetadata ?? {}),
223
+ source: "openclaw_local_fallback",
224
+ },
225
+ };
226
+ await appendToOutbox(initiativeId, {
227
+ id: deps.randomUUID(),
228
+ type: "progress",
229
+ timestamp,
230
+ payload: {
231
+ initiative_id: initiativeId,
232
+ run_id: normalizedRunId ?? undefined,
233
+ correlation_id: normalizedCorrelationId ?? undefined,
234
+ source_client: "openclaw",
235
+ message,
236
+ phase: input.phase,
237
+ progress_pct: typeof input.progressPct === "number" && Number.isFinite(input.progressPct)
238
+ ? Math.max(0, Math.min(100, Math.round(input.progressPct)))
239
+ : undefined,
240
+ level: input.level ?? "info",
241
+ next_step: input.nextStep ?? undefined,
242
+ metadata: enrichedMetadata,
243
+ },
244
+ activityItem,
245
+ });
246
+ }
247
+ catch {
248
+ // best effort
249
+ }
250
+ }
251
+ }
252
+ async function requestDecisionSafe(input) {
253
+ const initiativeId = input.initiativeId?.trim() ?? "";
254
+ const title = input.title.trim();
255
+ if (!initiativeId || !title) {
256
+ return { queued: false, decisionIds: [] };
257
+ }
258
+ const normalizedOptions = [];
259
+ for (const entry of Array.isArray(input.options) ? input.options : []) {
260
+ if (typeof entry === "string") {
261
+ const label = entry.trim();
262
+ if (label.length > 0)
263
+ normalizedOptions.push(label);
264
+ continue;
265
+ }
266
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
267
+ continue;
268
+ const record = entry;
269
+ const label = typeof record.label === "string" ? record.label.trim() : "";
270
+ if (!label)
271
+ continue;
272
+ const optionPayload = {
273
+ label,
274
+ };
275
+ if (typeof record.id === "string" && record.id.trim()) {
276
+ optionPayload.id = record.id.trim();
277
+ }
278
+ if (typeof record.description === "string" && record.description.trim()) {
279
+ optionPayload.description = record.description.trim();
280
+ }
281
+ if (typeof record.implied_status === "string" && record.implied_status.trim()) {
282
+ const implied = record.implied_status.trim().toLowerCase();
283
+ if (implied === "approved" ||
284
+ implied === "declined" ||
285
+ implied === "cancelled" ||
286
+ implied === "rejected") {
287
+ optionPayload.implied_status = implied;
288
+ }
289
+ }
290
+ const normalizedActionType = normalizeDecisionActionType(record.action_type ?? record.type ?? record.verb ?? record.action);
291
+ if (normalizedActionType) {
292
+ optionPayload.action_type = normalizedActionType;
293
+ }
294
+ if (record.requires_note === true) {
295
+ optionPayload.requires_note = true;
296
+ }
297
+ normalizedOptions.push(optionPayload);
298
+ }
299
+ const normalizedEvidenceRefs = (Array.isArray(input.evidenceRefs) ? input.evidenceRefs : [])
300
+ .map((entry) => (entry && typeof entry === "object" && !Array.isArray(entry) ? entry : null))
301
+ .filter((entry) => Boolean(entry));
302
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
303
+ const asUuid = (value) => {
304
+ const normalized = value?.trim() ?? "";
305
+ return uuidPattern.test(normalized) ? normalized : undefined;
306
+ };
307
+ const asIsoDateTime = (value) => {
308
+ const normalized = value?.trim() ?? "";
309
+ if (!normalized)
310
+ return undefined;
311
+ const epoch = Date.parse(normalized);
312
+ if (!Number.isFinite(epoch))
313
+ return undefined;
314
+ return new Date(epoch).toISOString();
315
+ };
316
+ const sourceRef = input.sourceRef && typeof input.sourceRef === "object" && !Array.isArray(input.sourceRef)
317
+ ? input.sourceRef
318
+ : null;
319
+ const metadata = input.metadata && typeof input.metadata === "object" && !Array.isArray(input.metadata)
320
+ ? input.metadata
321
+ : null;
322
+ const dedupeKey = input.dedupeKey?.trim() ||
323
+ `decision:${deps.stableHash([
324
+ initiativeId,
325
+ input.workstreamId?.trim() || "none",
326
+ input.conflictSource?.trim() || "general",
327
+ title.toLowerCase(),
328
+ ].join("|")).slice(0, 24)}`;
329
+ const blocking = input.blocking ?? true;
330
+ const fallbackEvidenceRefs = blocking && decisionEvidenceRequiredForBlocking && normalizedEvidenceRefs.length === 0
331
+ ? [
332
+ {
333
+ evidence_type: "decision_context",
334
+ title: "Decision context",
335
+ summary: input.summary?.trim() ||
336
+ "Blocking decision emitted without explicit evidence payload.",
337
+ payload: {
338
+ source_ref: sourceRef ?? null,
339
+ generated_by: "plugin_guardrail",
340
+ },
341
+ },
342
+ ]
343
+ : [];
344
+ const effectiveEvidenceRefs = [...normalizedEvidenceRefs, ...fallbackEvidenceRefs];
345
+ const operation = {
346
+ op: "decision.create",
347
+ title,
348
+ summary: input.summary ?? undefined,
349
+ urgency: input.urgency ?? "high",
350
+ options: normalizedOptions,
351
+ blocking,
352
+ ...(decisionV2Enabled
353
+ ? {
354
+ decision_type: input.decisionType?.trim() || undefined,
355
+ workstream_id: asUuid(input.workstreamId),
356
+ agent_id: asUuid(input.agentId),
357
+ due_at: asIsoDateTime(input.dueAt),
358
+ source_system: input.sourceSystem?.trim() || "openclaw",
359
+ conflict_source: input.conflictSource?.trim() || undefined,
360
+ dedupe_key: decisionDedupeEnabled ? dedupeKey : undefined,
361
+ recommended_action: input.recommendedAction?.trim() || undefined,
362
+ source_run_id: asUuid(input.sourceRunId),
363
+ source_session_id: input.sourceSessionId?.trim() || undefined,
364
+ source_stream_id: asUuid(input.sourceStreamId),
365
+ source_ref: sourceRef ?? undefined,
366
+ evidence_refs: effectiveEvidenceRefs.length > 0 ? effectiveEvidenceRefs : undefined,
367
+ metadata: metadata ?? undefined,
368
+ }
369
+ : {}),
370
+ };
371
+ try {
372
+ const response = await deps.client.applyChangeset({
373
+ initiative_id: initiativeId,
374
+ correlation_id: input.correlationId?.trim() || undefined,
375
+ source_client: "openclaw",
376
+ idempotency_key: deps.idempotencyKey([
377
+ "openclaw",
378
+ "decision",
379
+ initiativeId,
380
+ dedupeKey,
381
+ title,
382
+ input.correlationId ?? null,
383
+ ]),
384
+ operations: [operation],
385
+ });
386
+ return {
387
+ queued: true,
388
+ decisionIds: extractDecisionIdsFromChangesetResults(response?.results),
389
+ };
390
+ }
391
+ catch (err) {
392
+ const timestamp = new Date().toISOString();
393
+ const correlationId = input.correlationId?.trim() || undefined;
394
+ const operations = [operation];
395
+ const activityItem = {
396
+ id: deps.randomUUID(),
397
+ type: "decision_requested",
398
+ title,
399
+ description: input.summary ?? null,
400
+ agentId: null,
401
+ agentName: null,
402
+ requesterAgentId: null,
403
+ requesterAgentName: null,
404
+ executorAgentId: null,
405
+ executorAgentName: null,
406
+ runId: correlationId ?? null,
407
+ initiativeId,
408
+ timestamp,
409
+ phase: "review",
410
+ summary: input.summary ?? null,
411
+ metadata: {
412
+ source: "openclaw_local_fallback",
413
+ event: "decision_buffered",
414
+ urgency: input.urgency ?? "high",
415
+ blocking: input.blocking ?? true,
416
+ options: normalizedOptions,
417
+ dedupe_key: decisionDedupeEnabled ? dedupeKey : null,
418
+ decision_type: decisionV2Enabled
419
+ ? input.decisionType?.trim() || null
420
+ : null,
421
+ conflict_source: decisionV2Enabled
422
+ ? input.conflictSource?.trim() || null
423
+ : null,
424
+ source_system: decisionV2Enabled
425
+ ? input.sourceSystem?.trim() || "openclaw"
426
+ : "openclaw",
427
+ error: deps.safeErrorMessage(err),
428
+ },
429
+ };
430
+ try {
431
+ await appendToOutbox(initiativeId, {
432
+ id: deps.randomUUID(),
433
+ type: "changeset",
434
+ timestamp,
435
+ payload: {
436
+ initiative_id: initiativeId,
437
+ correlation_id: correlationId,
438
+ source_client: "openclaw",
439
+ idempotency_key: deps.idempotencyKey([
440
+ "openclaw",
441
+ "decision",
442
+ initiativeId,
443
+ dedupeKey,
444
+ title,
445
+ input.correlationId ?? null,
446
+ "outbox",
447
+ ]),
448
+ operations,
449
+ },
450
+ activityItem,
451
+ });
452
+ return { queued: true, decisionIds: [] };
453
+ }
454
+ catch {
455
+ return { queued: false, decisionIds: [] };
456
+ }
457
+ }
458
+ }
459
+ async function checkSpawnGuardSafe(input) {
460
+ const bypassRaw = (process.env.ORGX_SPAWN_GUARD_BYPASS ?? "").trim().toLowerCase();
461
+ const modeRaw = (process.env.ORGX_SPAWN_GUARD_MODE ?? "").trim().toLowerCase();
462
+ const bypass = (bypassRaw && bypassRaw !== "0" && bypassRaw !== "false" && bypassRaw !== "no") ||
463
+ modeRaw === "off" ||
464
+ modeRaw === "bypass";
465
+ if (bypass)
466
+ return null;
467
+ const scopedClient = deps.client;
468
+ if (typeof scopedClient.checkSpawnGuard !== "function") {
469
+ return null;
470
+ }
471
+ const taskId = input.taskId?.trim() ?? "";
472
+ const targetLabel = input.targetLabel?.trim() ||
473
+ (taskId ? `task ${taskId}` : "dispatch target");
474
+ try {
475
+ return await scopedClient.checkSpawnGuard(input.domain, taskId || undefined);
476
+ }
477
+ catch (err) {
478
+ await emitActivitySafe({
479
+ initiativeId: input.initiativeId,
480
+ runId: input.runId ?? null,
481
+ correlationId: input.correlationId,
482
+ phase: "blocked",
483
+ level: "warn",
484
+ message: `Spawn guard check degraded for ${targetLabel}; continuing with local policy.`,
485
+ metadata: {
486
+ event: "spawn_guard_degraded",
487
+ task_id: taskId || null,
488
+ domain: input.domain,
489
+ error: deps.safeErrorMessage(err),
490
+ },
491
+ });
492
+ return null;
493
+ }
494
+ }
495
+ function extractSpawnGuardModelTier(result) {
496
+ if (!result || typeof result !== "object")
497
+ return null;
498
+ return (deps.pickString(result, ["modelTier", "model_tier"]) ??
499
+ null);
500
+ }
501
+ function formatRequiredSkills(requiredSkills) {
502
+ const normalized = requiredSkills
503
+ .map((entry) => entry.trim())
504
+ .filter((entry) => entry.length > 0)
505
+ .map((entry) => (entry.startsWith("$") ? entry : `$${entry}`));
506
+ return normalized.length > 0
507
+ ? normalized.join(", ")
508
+ : "$orgx-engineering-agent";
509
+ }
510
+ function buildPolicyEnforcedMessage(input) {
511
+ const modelTier = extractSpawnGuardModelTier(input.spawnGuardResult ?? null);
512
+ return [
513
+ `Execution policy: ${input.executionPolicy.domain}`,
514
+ `Required skills: ${formatRequiredSkills(input.executionPolicy.requiredSkills)}`,
515
+ modelTier ? `Spawn guard model tier: ${modelTier}` : null,
516
+ "",
517
+ input.baseMessage,
518
+ ]
519
+ .filter((entry) => Boolean(entry))
520
+ .join("\n");
521
+ }
522
+ async function resolveDispatchExecutionPolicy(input) {
523
+ const initiativeId = input.initiativeId?.trim() ?? "";
524
+ const taskId = input.taskId?.trim() ?? "";
525
+ const workstreamId = input.workstreamId?.trim() ?? "";
526
+ let resolvedTaskTitle = input.taskTitle?.trim() || null;
527
+ let resolvedWorkstreamTitle = input.workstreamTitle?.trim() || null;
528
+ if (initiativeId && (taskId || workstreamId)) {
529
+ try {
530
+ const graph = await buildMissionControlGraph(deps.client, initiativeId);
531
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
532
+ const taskNode = taskId ? nodeById.get(taskId) ?? null : null;
533
+ const workstreamNode = workstreamId ? nodeById.get(workstreamId) ?? null : null;
534
+ if (taskNode && taskNode.type === "task") {
535
+ resolvedTaskTitle = resolvedTaskTitle ?? taskNode.title;
536
+ const relatedWorkstream = (taskNode.workstreamId ? nodeById.get(taskNode.workstreamId) ?? null : null) ??
537
+ workstreamNode;
538
+ const normalizedWorkstream = relatedWorkstream && relatedWorkstream.type === "workstream"
539
+ ? relatedWorkstream
540
+ : null;
541
+ resolvedWorkstreamTitle =
542
+ resolvedWorkstreamTitle ?? normalizedWorkstream?.title ?? null;
543
+ return {
544
+ executionPolicy: deriveExecutionPolicy(taskNode, normalizedWorkstream),
545
+ taskTitle: resolvedTaskTitle,
546
+ workstreamTitle: resolvedWorkstreamTitle,
547
+ };
548
+ }
549
+ if (workstreamNode && workstreamNode.type === "workstream") {
550
+ resolvedWorkstreamTitle = resolvedWorkstreamTitle ?? workstreamNode.title;
551
+ const assignedDomain = workstreamNode.assignedAgents
552
+ .map((agent) => normalizeExecutionDomain(agent.domain))
553
+ .find((entry) => Boolean(entry));
554
+ const domain = assignedDomain ??
555
+ inferExecutionDomainFromText(workstreamNode.title, input.initiativeTitle, input.message);
556
+ const normalizedDomain = normalizeExecutionDomain(domain) ?? "engineering";
557
+ return {
558
+ executionPolicy: {
559
+ domain: normalizedDomain,
560
+ requiredSkills: [
561
+ ORGX_SKILL_BY_DOMAIN[normalizedDomain] ??
562
+ ORGX_SKILL_BY_DOMAIN.engineering,
563
+ ],
564
+ },
565
+ taskTitle: resolvedTaskTitle,
566
+ workstreamTitle: resolvedWorkstreamTitle,
567
+ };
568
+ }
569
+ }
570
+ catch {
571
+ // best effort
572
+ }
573
+ }
574
+ const inferredDomain = normalizeExecutionDomain(inferExecutionDomainFromText(resolvedTaskTitle, resolvedWorkstreamTitle, input.initiativeTitle, input.message)) ?? "engineering";
575
+ return {
576
+ executionPolicy: {
577
+ domain: inferredDomain,
578
+ requiredSkills: [
579
+ ORGX_SKILL_BY_DOMAIN[inferredDomain] ?? ORGX_SKILL_BY_DOMAIN.engineering,
580
+ ],
581
+ },
582
+ taskTitle: resolvedTaskTitle,
583
+ workstreamTitle: resolvedWorkstreamTitle,
584
+ };
585
+ }
586
+ async function syncParentRollupsForTask(input) {
587
+ const initiativeId = input.initiativeId?.trim() ?? "";
588
+ const taskId = input.taskId?.trim() ?? "";
589
+ if (!initiativeId || !taskId)
590
+ return;
591
+ let tasks = [];
592
+ try {
593
+ const response = await deps.client.listEntities("task", {
594
+ initiative_id: initiativeId,
595
+ limit: 4000,
596
+ });
597
+ tasks = Array.isArray(response?.data)
598
+ ? response.data
599
+ : [];
600
+ }
601
+ catch {
602
+ return;
603
+ }
604
+ const task = tasks.find((row) => String(row.id ?? "").trim() === taskId) ?? null;
605
+ const resolvedMilestoneId = (input.milestoneId?.trim() || "") ||
606
+ (task ? deps.pickString(task, ["milestone_id", "milestoneId"]) ?? "" : "");
607
+ const resolvedWorkstreamId = (input.workstreamId?.trim() || "") ||
608
+ (task ? deps.pickString(task, ["workstream_id", "workstreamId"]) ?? "" : "");
609
+ if (resolvedMilestoneId) {
610
+ const milestoneTaskStatuses = tasks
611
+ .filter((row) => deps.pickString(row, ["milestone_id", "milestoneId"]) === resolvedMilestoneId)
612
+ .map((row) => deps.pickString(row, ["status"]) ?? "todo");
613
+ const rollup = computeMilestoneRollup(milestoneTaskStatuses);
614
+ try {
615
+ await deps.client.applyChangeset({
616
+ initiative_id: initiativeId,
617
+ correlation_id: input.correlationId?.trim() || undefined,
618
+ source_client: "openclaw",
619
+ idempotency_key: deps.idempotencyKey([
620
+ "openclaw",
621
+ "rollup",
622
+ "milestone",
623
+ resolvedMilestoneId,
624
+ rollup.status,
625
+ String(rollup.progressPct),
626
+ String(rollup.done),
627
+ String(rollup.total),
628
+ ]),
629
+ operations: [
630
+ {
631
+ op: "milestone.update",
632
+ milestone_id: resolvedMilestoneId,
633
+ status: rollup.status,
634
+ },
635
+ ],
636
+ });
637
+ }
638
+ catch {
639
+ // best effort
640
+ }
641
+ }
642
+ if (resolvedWorkstreamId) {
643
+ const workstreamTaskStatuses = tasks
644
+ .filter((row) => deps.pickString(row, ["workstream_id", "workstreamId"]) === resolvedWorkstreamId)
645
+ .map((row) => deps.pickString(row, ["status"]) ?? "todo");
646
+ const rollup = computeWorkstreamRollup(workstreamTaskStatuses);
647
+ try {
648
+ await deps.client.updateEntity("workstream", resolvedWorkstreamId, {
649
+ status: rollup.status,
650
+ });
651
+ }
652
+ catch {
653
+ // best effort
654
+ }
655
+ }
656
+ }
657
+ async function enforceSpawnGuardForDispatch(input) {
658
+ const taskId = input.taskId?.trim() ?? "";
659
+ const workstreamId = input.workstreamId?.trim() ?? "";
660
+ const taskTitle = input.taskTitle?.trim() || null;
661
+ const workstreamTitle = input.workstreamTitle?.trim() || null;
662
+ const targetLabel = taskId
663
+ ? `task ${taskTitle ?? taskId}`
664
+ : workstreamId
665
+ ? `workstream ${workstreamTitle ?? workstreamId}`
666
+ : "dispatch target";
667
+ const spawnGuardResult = await checkSpawnGuardSafe({
668
+ domain: input.executionPolicy.domain,
669
+ // Spawn guard is task-scoped. Passing a workstream ID causes noisy 404s
670
+ // ("Task ... not found") and makes the UI feel perpetually degraded.
671
+ taskId: taskId || null,
672
+ initiativeId: input.initiativeId,
673
+ correlationId: input.correlationId,
674
+ runId: input.runId ?? null,
675
+ targetLabel,
676
+ });
677
+ if (!spawnGuardResult || typeof spawnGuardResult !== "object") {
678
+ return {
679
+ allowed: true,
680
+ retryable: false,
681
+ blockedReason: null,
682
+ spawnGuardResult,
683
+ };
684
+ }
685
+ const allowed = spawnGuardResult.allowed;
686
+ if (allowed !== false) {
687
+ return {
688
+ allowed: true,
689
+ retryable: false,
690
+ blockedReason: null,
691
+ spawnGuardResult,
692
+ };
693
+ }
694
+ const blockedReason = summarizeSpawnGuardBlockReason(spawnGuardResult);
695
+ const retryable = spawnGuardIsRateLimited(spawnGuardResult);
696
+ const blockedEvent = retryable
697
+ ? `${input.sourceEventPrefix}_spawn_guard_rate_limited`
698
+ : `${input.sourceEventPrefix}_spawn_guard_blocked`;
699
+ await emitActivitySafe({
700
+ initiativeId: input.initiativeId,
701
+ runId: input.runId ?? null,
702
+ correlationId: input.correlationId,
703
+ phase: "blocked",
704
+ level: retryable ? "warn" : "error",
705
+ message: retryable
706
+ ? `Spawn guard rate-limited ${targetLabel}; deferring launch.`
707
+ : `Spawn guard blocked ${targetLabel}.`,
708
+ metadata: {
709
+ event: blockedEvent,
710
+ agent_id: input.agentId ?? null,
711
+ task_id: taskId || null,
712
+ task_title: taskTitle,
713
+ workstream_id: workstreamId || null,
714
+ workstream_title: workstreamTitle,
715
+ domain: input.executionPolicy.domain,
716
+ required_skills: input.executionPolicy.requiredSkills,
717
+ blocked_reason: blockedReason,
718
+ spawn_guard: spawnGuardResult,
719
+ },
720
+ nextStep: retryable
721
+ ? "Retry dispatch when spawn rate limits recover."
722
+ : "Review decision and unblock guard checks before retry.",
723
+ });
724
+ if (!retryable && input.initiativeId && taskId) {
725
+ try {
726
+ await deps.client.updateEntity("task", taskId, { status: "blocked" });
727
+ }
728
+ catch {
729
+ // best effort
730
+ }
731
+ await syncParentRollupsForTask({
732
+ initiativeId: input.initiativeId,
733
+ taskId,
734
+ workstreamId: workstreamId || null,
735
+ milestoneId: input.milestoneId ?? null,
736
+ correlationId: input.correlationId,
737
+ });
738
+ }
739
+ if (!retryable) {
740
+ await requestDecisionSafe({
741
+ initiativeId: input.initiativeId,
742
+ correlationId: input.correlationId,
743
+ title: `Unblock ${targetLabel}`,
744
+ summary: [
745
+ `${targetLabel} failed spawn guard checks.`,
746
+ `Reason: ${blockedReason}`,
747
+ `Domain: ${input.executionPolicy.domain}`,
748
+ `Required skills: ${input.executionPolicy.requiredSkills.join(", ")}`,
749
+ ].join(" "),
750
+ urgency: "high",
751
+ options: [
752
+ "Approve exception and continue",
753
+ "Reassign task/domain",
754
+ "Pause and investigate quality gate",
755
+ ],
756
+ blocking: true,
757
+ decisionType: "spawn_guard_blocked",
758
+ workstreamId: workstreamId || null,
759
+ agentId: input.agentId ?? null,
760
+ sourceSystem: "openclaw",
761
+ conflictSource: "spawn_guard_blocked",
762
+ dedupeKey: [
763
+ "dispatch",
764
+ input.initiativeId ?? "none",
765
+ workstreamId || "none",
766
+ taskId || "none",
767
+ "spawn_guard_blocked",
768
+ ].join(":"),
769
+ recommendedAction: "Choose exception, reassignment, or pause before retrying dispatch.",
770
+ sourceRunId: input.runId ?? null,
771
+ sourceRef: {
772
+ run_id: input.runId ?? null,
773
+ correlation_id: input.correlationId,
774
+ task_id: taskId || null,
775
+ workstream_id: workstreamId || null,
776
+ domain: input.executionPolicy.domain,
777
+ },
778
+ evidenceRefs: [
779
+ {
780
+ evidence_type: "spawn_guard_result",
781
+ title: `Spawn guard blocked ${targetLabel}`,
782
+ summary: blockedReason,
783
+ payload: {
784
+ spawn_guard: spawnGuardResult,
785
+ required_skills: input.executionPolicy.requiredSkills,
786
+ },
787
+ },
788
+ ],
789
+ });
790
+ }
791
+ return {
792
+ allowed: false,
793
+ retryable,
794
+ blockedReason,
795
+ spawnGuardResult,
796
+ };
797
+ }
798
+ return {
799
+ withProvenanceMetadata,
800
+ emitActivitySafe,
801
+ requestDecisionSafe,
802
+ checkSpawnGuardSafe,
803
+ extractSpawnGuardModelTier,
804
+ buildPolicyEnforcedMessage,
805
+ resolveDispatchExecutionPolicy,
806
+ enforceSpawnGuardForDispatch,
807
+ syncParentRollupsForTask,
808
+ };
809
+ }