@useorgx/openclaw-plugin 0.7.18 → 0.7.23

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 (161) hide show
  1. package/dashboard/dist/assets/9gFmK3Kr.js +1 -0
  2. package/dashboard/dist/assets/9gFmK3Kr.js.br +0 -0
  3. package/dashboard/dist/assets/9gFmK3Kr.js.gz +0 -0
  4. package/dashboard/dist/assets/{DS79hzMu.js → BrMXbzQ-.js} +2 -2
  5. package/dashboard/dist/assets/BrMXbzQ-.js.br +0 -0
  6. package/dashboard/dist/assets/BrMXbzQ-.js.gz +0 -0
  7. package/dashboard/dist/assets/By0MIBj_.js +1 -0
  8. package/dashboard/dist/assets/By0MIBj_.js.br +0 -0
  9. package/dashboard/dist/assets/By0MIBj_.js.gz +0 -0
  10. package/dashboard/dist/assets/C1u2SGin.css +1 -0
  11. package/dashboard/dist/assets/C1u2SGin.css.br +0 -0
  12. package/dashboard/dist/assets/C1u2SGin.css.gz +0 -0
  13. package/dashboard/dist/assets/{467jKHFJ.js → CGJiHCIx.js} +1 -1
  14. package/dashboard/dist/assets/CGJiHCIx.js.br +0 -0
  15. package/dashboard/dist/assets/CGJiHCIx.js.gz +0 -0
  16. package/dashboard/dist/assets/CSd4rSuU.js +212 -0
  17. package/dashboard/dist/assets/CSd4rSuU.js.br +0 -0
  18. package/dashboard/dist/assets/CSd4rSuU.js.gz +0 -0
  19. package/dashboard/dist/assets/{5Ihga-4X.js → CZXS5i_5.js} +1 -1
  20. package/dashboard/dist/assets/CZXS5i_5.js.br +0 -0
  21. package/dashboard/dist/assets/CZXS5i_5.js.gz +0 -0
  22. package/dashboard/dist/assets/{a6qcPiWt.js → CbVWL74-.js} +1 -1
  23. package/dashboard/dist/assets/CbVWL74-.js.br +0 -0
  24. package/dashboard/dist/assets/CbVWL74-.js.gz +0 -0
  25. package/dashboard/dist/assets/{qDJ6rqcs.js → D-FuHfT8.js} +1 -1
  26. package/dashboard/dist/assets/D-FuHfT8.js.br +0 -0
  27. package/dashboard/dist/assets/D-FuHfT8.js.gz +0 -0
  28. package/dashboard/dist/assets/{BcJmNILk.js → D0PN5_vY.js} +1 -1
  29. package/dashboard/dist/assets/D0PN5_vY.js.br +0 -0
  30. package/dashboard/dist/assets/D0PN5_vY.js.gz +0 -0
  31. package/dashboard/dist/assets/DDCPrZRt.js +1 -0
  32. package/dashboard/dist/assets/DDCPrZRt.js.br +0 -0
  33. package/dashboard/dist/assets/DDCPrZRt.js.gz +0 -0
  34. package/dashboard/dist/assets/{B71dt9yu.js → DNQ-iFO2.js} +1 -1
  35. package/dashboard/dist/assets/DNQ-iFO2.js.br +0 -0
  36. package/dashboard/dist/assets/DNQ-iFO2.js.gz +0 -0
  37. package/dashboard/dist/assets/{PVi0vr9a.js → DhPuHPK7.js} +1 -1
  38. package/dashboard/dist/assets/DhPuHPK7.js.br +0 -0
  39. package/dashboard/dist/assets/DhPuHPK7.js.gz +0 -0
  40. package/dashboard/dist/assets/Dhz7qPtn.js +1 -0
  41. package/dashboard/dist/assets/Dhz7qPtn.js.br +0 -0
  42. package/dashboard/dist/assets/Dhz7qPtn.js.gz +0 -0
  43. package/dashboard/dist/assets/LOFrVoPD.js +1 -0
  44. package/dashboard/dist/assets/LOFrVoPD.js.br +0 -0
  45. package/dashboard/dist/assets/LOFrVoPD.js.gz +0 -0
  46. package/dashboard/dist/assets/OlLPtzdz.js +1 -0
  47. package/dashboard/dist/assets/OlLPtzdz.js.br +0 -0
  48. package/dashboard/dist/assets/OlLPtzdz.js.gz +0 -0
  49. package/dashboard/dist/assets/{sdoPH_Z1.js → RN4M9u9W.js} +2 -2
  50. package/dashboard/dist/assets/RN4M9u9W.js.br +0 -0
  51. package/dashboard/dist/assets/RN4M9u9W.js.gz +0 -0
  52. package/dashboard/dist/assets/VCHu272d.js +1 -0
  53. package/dashboard/dist/assets/VCHu272d.js.br +0 -0
  54. package/dashboard/dist/assets/VCHu272d.js.gz +0 -0
  55. package/dashboard/dist/assets/m2smti3F.js +1 -0
  56. package/dashboard/dist/assets/m2smti3F.js.br +0 -0
  57. package/dashboard/dist/assets/m2smti3F.js.gz +0 -0
  58. package/dashboard/dist/assets/{C3_j_W9V.js → nra1yvJX.js} +1 -1
  59. package/dashboard/dist/assets/nra1yvJX.js.br +0 -0
  60. package/dashboard/dist/assets/nra1yvJX.js.gz +0 -0
  61. package/dashboard/dist/assets/qLX6NZ-J.js +1 -0
  62. package/dashboard/dist/assets/qLX6NZ-J.js.br +0 -0
  63. package/dashboard/dist/assets/qLX6NZ-J.js.gz +0 -0
  64. package/dashboard/dist/index.html +2 -2
  65. package/dashboard/dist/index.html.br +0 -0
  66. package/dashboard/dist/index.html.gz +0 -0
  67. package/dist/agent-run-store.js +162 -24
  68. package/dist/cli/orgx.d.ts +3 -0
  69. package/dist/config/resolution.d.ts +7 -0
  70. package/dist/config/resolution.js +13 -5
  71. package/dist/contracts/onboarding-state.d.ts +2 -0
  72. package/dist/contracts/onboarding-state.js +23 -0
  73. package/dist/contracts/shared-types.d.ts +17 -0
  74. package/dist/http/helpers/auto-continue-engine.d.ts +62 -0
  75. package/dist/http/helpers/auto-continue-engine.js +329 -53
  76. package/dist/http/helpers/autopilot-runtime.js +5 -1
  77. package/dist/http/helpers/autopilot-slice-utils.js +25 -1
  78. package/dist/http/helpers/decision-mapper.d.ts +1 -0
  79. package/dist/http/helpers/decision-mapper.js +19 -2
  80. package/dist/http/helpers/dispatch-lifecycle.js +3 -0
  81. package/dist/http/helpers/mission-control.d.ts +1 -0
  82. package/dist/http/helpers/mission-control.js +5 -2
  83. package/dist/http/helpers/slice-run-projections.d.ts +27 -0
  84. package/dist/http/helpers/slice-run-projections.js +198 -10
  85. package/dist/http/helpers/triage-mapper.js +220 -6
  86. package/dist/http/index.d.ts +1 -0
  87. package/dist/http/index.js +94 -46
  88. package/dist/http/router.js +64 -9
  89. package/dist/http/routes/live-legacy.d.ts +19 -2
  90. package/dist/http/routes/live-legacy.js +110 -27
  91. package/dist/http/routes/live-snapshot.d.ts +16 -2
  92. package/dist/http/routes/live-snapshot.js +169 -25
  93. package/dist/http/routes/mission-control-actions.js +28 -0
  94. package/dist/http/routes/mission-control-read.d.ts +18 -0
  95. package/dist/http/routes/mission-control-read.js +130 -218
  96. package/dist/http/routes/onboarding.d.ts +1 -0
  97. package/dist/http/routes/onboarding.js +17 -0
  98. package/dist/index.d.ts +5 -0
  99. package/dist/index.js +199 -123
  100. package/dist/outbox.d.ts +0 -2
  101. package/dist/outbox.js +268 -150
  102. package/dist/reporting/rollups.js +18 -11
  103. package/dist/runtime-instance-store.js +212 -58
  104. package/dist/stores/materialized-snapshot-store.d.ts +18 -0
  105. package/dist/stores/materialized-snapshot-store.js +91 -0
  106. package/dist/stores/sqlite-state.d.ts +6 -0
  107. package/dist/stores/sqlite-state.js +179 -0
  108. package/package.json +6 -1
  109. package/dashboard/dist/assets/467jKHFJ.js.br +0 -0
  110. package/dashboard/dist/assets/467jKHFJ.js.gz +0 -0
  111. package/dashboard/dist/assets/5Ihga-4X.js.br +0 -0
  112. package/dashboard/dist/assets/5Ihga-4X.js.gz +0 -0
  113. package/dashboard/dist/assets/B71dt9yu.js.br +0 -0
  114. package/dashboard/dist/assets/B71dt9yu.js.gz +0 -0
  115. package/dashboard/dist/assets/BCudUvwg.js +0 -1
  116. package/dashboard/dist/assets/BCudUvwg.js.br +0 -0
  117. package/dashboard/dist/assets/BCudUvwg.js.gz +0 -0
  118. package/dashboard/dist/assets/BEnI6kNR.js +0 -1
  119. package/dashboard/dist/assets/BEnI6kNR.js.br +0 -0
  120. package/dashboard/dist/assets/BEnI6kNR.js.gz +0 -0
  121. package/dashboard/dist/assets/BcJmNILk.js.br +0 -0
  122. package/dashboard/dist/assets/BcJmNILk.js.gz +0 -0
  123. package/dashboard/dist/assets/C-MOJWHs.js +0 -1
  124. package/dashboard/dist/assets/C-MOJWHs.js.br +0 -0
  125. package/dashboard/dist/assets/C-MOJWHs.js.gz +0 -0
  126. package/dashboard/dist/assets/C-XuWXGi.js +0 -1
  127. package/dashboard/dist/assets/C-XuWXGi.js.br +0 -0
  128. package/dashboard/dist/assets/C-XuWXGi.js.gz +0 -0
  129. package/dashboard/dist/assets/C3_j_W9V.js.br +0 -0
  130. package/dashboard/dist/assets/C3_j_W9V.js.gz +0 -0
  131. package/dashboard/dist/assets/C9-UYhBb.js +0 -1
  132. package/dashboard/dist/assets/C9-UYhBb.js.br +0 -0
  133. package/dashboard/dist/assets/C9-UYhBb.js.gz +0 -0
  134. package/dashboard/dist/assets/C9yV06GS.js +0 -1
  135. package/dashboard/dist/assets/C9yV06GS.js.br +0 -0
  136. package/dashboard/dist/assets/C9yV06GS.js.gz +0 -0
  137. package/dashboard/dist/assets/CReugbyT.js +0 -1
  138. package/dashboard/dist/assets/CReugbyT.js.br +0 -0
  139. package/dashboard/dist/assets/CReugbyT.js.gz +0 -0
  140. package/dashboard/dist/assets/CSDhTbKy.js +0 -1
  141. package/dashboard/dist/assets/CSDhTbKy.js.br +0 -0
  142. package/dashboard/dist/assets/CSDhTbKy.js.gz +0 -0
  143. package/dashboard/dist/assets/CfMS9yIf.js +0 -1
  144. package/dashboard/dist/assets/CfMS9yIf.js.br +0 -0
  145. package/dashboard/dist/assets/CfMS9yIf.js.gz +0 -0
  146. package/dashboard/dist/assets/D2Kqcmv9.js +0 -212
  147. package/dashboard/dist/assets/D2Kqcmv9.js.br +0 -0
  148. package/dashboard/dist/assets/D2Kqcmv9.js.gz +0 -0
  149. package/dashboard/dist/assets/DS79hzMu.js.br +0 -0
  150. package/dashboard/dist/assets/DS79hzMu.js.gz +0 -0
  151. package/dashboard/dist/assets/PVi0vr9a.js.br +0 -0
  152. package/dashboard/dist/assets/PVi0vr9a.js.gz +0 -0
  153. package/dashboard/dist/assets/RZkbqlJk.css +0 -1
  154. package/dashboard/dist/assets/RZkbqlJk.css.br +0 -0
  155. package/dashboard/dist/assets/RZkbqlJk.css.gz +0 -0
  156. package/dashboard/dist/assets/a6qcPiWt.js.br +0 -0
  157. package/dashboard/dist/assets/a6qcPiWt.js.gz +0 -0
  158. package/dashboard/dist/assets/qDJ6rqcs.js.br +0 -0
  159. package/dashboard/dist/assets/qDJ6rqcs.js.gz +0 -0
  160. package/dashboard/dist/assets/sdoPH_Z1.js.br +0 -0
  161. package/dashboard/dist/assets/sdoPH_Z1.js.gz +0 -0
@@ -3,7 +3,7 @@ import { existsSync } from "node:fs";
3
3
  import { readdir, stat, unlink } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
5
  import { dirname, join } from "node:path";
6
- import { normalizeActivityActionPhase, normalizeActivityActionType, } from "../../contracts/shared-types.js";
6
+ import { normalizeActivityActionPhase, normalizeActivityActionType, normalizeDecisionActionType, } from "../../contracts/shared-types.js";
7
7
  import { upsertAgentContext, upsertRunContext } from "../../agent-context-store.js";
8
8
  import { appendTeamCompletion } from "../../team-context-store.js";
9
9
  import { readOpenClawGatewayPort, readOpenClawSettingsSnapshot, } from "../../openclaw-settings.js";
@@ -11,7 +11,7 @@ import { resolveRuntimeHookToken, } from "../../runtime-instance-store.js";
11
11
  import { detectMcpHandshakeFailure, shouldKillWorker } from "../../worker-supervisor.js";
12
12
  import { humanizeSliceFailure, humanizeSliceFailureSummary } from "./humanize-slice-failure.js";
13
13
  import { getOrgxPluginConfigDir } from "../../paths.js";
14
- import { buildMissionControlGraph, DEFAULT_TOKEN_BUDGET_ASSUMPTIONS, dedupeStrings, detectBehaviorConfigDrift, deriveBehaviorAutomationLevel, deriveBehaviorConfigContext, deriveExecutionPolicy, evaluateScopeCompletion, isDispatchableWorkstreamStatus, isDoneStatus, isTodoStatus, readBudgetEnvNumber, selectSliceTasksByScope, SLICE_SCOPE_TIMEOUT_MULTIPLIER, spawnGuardIsRateLimited, summarizeSpawnGuardBlockReason, } from "./mission-control.js";
14
+ import { buildMissionControlGraph, DEFAULT_TOKEN_BUDGET_ASSUMPTIONS, dedupeStrings, detectBehaviorConfigDrift, deriveBehaviorAutomationLevel, deriveBehaviorConfigContext, deriveInitiativeLifecycleStatus, deriveExecutionPolicy, evaluateScopeCompletion, isDispatchableWorkstreamStatus, isDoneStatus, isTodoStatus, readBudgetEnvNumber, selectSliceTasksByScope, SLICE_SCOPE_TIMEOUT_MULTIPLIER, spawnGuardIsRateLimited, summarizeSpawnGuardBlockReason, } from "./mission-control.js";
15
15
  import { createAutopilotRuntime } from "./autopilot-runtime.js";
16
16
  import { buildScopeDirective, buildSliceOutputInstructions, buildWorkstreamSlicePrompt, createCodexBinResolver, ensureAutopilotSliceSchemaPath, extractSessionIdFromLog, extractSessionIdFromOutput, fileUpdatedAtEpochMs, parseSliceResult, readFileTailSafe, readSliceOutputFile, } from "./autopilot-slice-utils.js";
17
17
  import { pickString } from "./value-utils.js";
@@ -28,7 +28,18 @@ function resolveAutopilotDefaultCwd(filename) {
28
28
  }
29
29
  return homedir();
30
30
  }
31
+ function captureAutopilotWorkerEnv() {
32
+ return {
33
+ ORGX_AUTOPILOT_CWD: (process.env.ORGX_AUTOPILOT_CWD ?? "").trim() || undefined,
34
+ ORGX_AUTOPILOT_EXECUTOR: (process.env.ORGX_AUTOPILOT_EXECUTOR ?? "").trim() || undefined,
35
+ ORGX_AUTOPILOT_WORKER_KIND: (process.env.ORGX_AUTOPILOT_WORKER_KIND ?? "").trim() || undefined,
36
+ ORGX_AUTOPILOT_MOCK_SCENARIO: (process.env.ORGX_AUTOPILOT_MOCK_SCENARIO ?? "").trim() || undefined,
37
+ ORGX_AUTOPILOT_MOCK_SLEEP_MS: (process.env.ORGX_AUTOPILOT_MOCK_SLEEP_MS ?? "").trim() || undefined,
38
+ ORGX_AUTOPILOT_SESSION_RESUME: (process.env.ORGX_AUTOPILOT_SESSION_RESUME ?? "").trim() || undefined,
39
+ };
40
+ }
31
41
  export function createAutoContinueEngine(deps) {
42
+ const defaultWorkerEnvOverrides = captureAutopilotWorkerEnv();
32
43
  const { client, safeErrorMessage, pidAlive, stopProcess, resolveOrgxAgentForDomain, checkSpawnGuardSafe, syncParentRollupsForTask, emitActivitySafe, requestDecisionSafe, registerArtifactSafe, applyAgentStatusUpdatesSafe, upsertRuntimeInstanceFromHook, broadcastRuntimeSse, clearSnapshotResponseCache, resolveByokEnvOverrides, } = deps;
33
44
  const randomUUID = deps.randomUUID ?? randomUuidFn;
34
45
  const fetchKickoffContextSafeFn = deps.fetchKickoffContextSafe ?? null;
@@ -731,8 +742,139 @@ export function createAutoContinueEngine(deps) {
731
742
  }
732
743
  return { queued: false, decisionIds: [] };
733
744
  };
745
+ const defaultInterventionDecisionOptions = () => [
746
+ {
747
+ id: "retry_slice",
748
+ label: "Retry this workstream slice",
749
+ description: "Retry once with the latest context and logs.",
750
+ consequences: "Autopilot retries this workstream slice immediately.",
751
+ implied_status: "approved",
752
+ action_type: "retry",
753
+ requires_note: false,
754
+ },
755
+ {
756
+ id: "pause_and_investigate",
757
+ label: "Pause autopilot and investigate",
758
+ description: "Pause orchestration and capture operator notes for handoff.",
759
+ consequences: "Autopilot pauses and waits for new operator guidance.",
760
+ implied_status: "declined",
761
+ action_type: "pause",
762
+ requires_note: true,
763
+ },
764
+ {
765
+ id: "skip_for_now",
766
+ label: "Skip this workstream for now",
767
+ description: "Defer this lane and keep other workstreams moving.",
768
+ consequences: "This lane is deferred while the rest of the queue continues.",
769
+ implied_status: "declined",
770
+ action_type: "defer",
771
+ requires_note: true,
772
+ },
773
+ ];
734
774
  const __filename = deps.filename;
735
775
  const autoContinueRuns = new Map();
776
+ /**
777
+ * Rehydrate an AutoContinueRun from persisted initiative metadata.
778
+ * Called when the in-memory Map is empty (e.g. after server restart) to
779
+ * restore the last-known autopilot state so the dashboard toggle stays
780
+ * accurate.
781
+ */
782
+ async function restoreAutoContinueRun(initiativeId) {
783
+ // Already in memory — nothing to restore.
784
+ if (autoContinueRuns.has(initiativeId)) {
785
+ return autoContinueRuns.get(initiativeId) ?? null;
786
+ }
787
+ try {
788
+ const entity = await fetchInitiativeEntity(initiativeId);
789
+ if (!entity)
790
+ return null;
791
+ const meta = entity && typeof entity === "object"
792
+ ? entity.metadata ?? {}
793
+ : {};
794
+ const enabled = meta.auto_continue_enabled;
795
+ const status = meta.auto_continue_status;
796
+ if (!enabled || !status)
797
+ return null;
798
+ // Reconstruct lane objects from persisted array.
799
+ const rawLanes = Array.isArray(meta.auto_continue_lane_states)
800
+ ? meta.auto_continue_lane_states
801
+ : [];
802
+ const laneByWorkstreamId = {};
803
+ for (const raw of rawLanes) {
804
+ const wsId = String(raw.workstream_id ?? "").trim();
805
+ if (!wsId)
806
+ continue;
807
+ laneByWorkstreamId[wsId] = {
808
+ workstreamId: wsId,
809
+ state: raw.state ?? LaneState.IDLE,
810
+ activeRunId: raw.active_run_id ?? null,
811
+ activeTaskIds: Array.isArray(raw.active_task_ids) ? raw.active_task_ids : [],
812
+ blockedReason: raw.blocked_reason ?? null,
813
+ waitingOnWorkstreamIds: Array.isArray(raw.waiting_on_workstream_ids)
814
+ ? raw.waiting_on_workstream_ids
815
+ : [],
816
+ retryAt: raw.retry_at ?? null,
817
+ updatedAt: raw.updated_at ?? new Date().toISOString(),
818
+ };
819
+ }
820
+ const now = new Date().toISOString();
821
+ const run = {
822
+ initiativeId,
823
+ workspaceId: typeof meta.workspace_id === "string" && meta.workspace_id.trim().length > 0
824
+ ? meta.workspace_id.trim()
825
+ : null,
826
+ agentId: "",
827
+ agentName: null,
828
+ includeVerification: Boolean(meta.auto_continue_include_verification),
829
+ allowedWorkstreamIds: Array.isArray(meta.auto_continue_workstream_filter)
830
+ ? meta.auto_continue_workstream_filter
831
+ : null,
832
+ stopAfterSlice: false,
833
+ ignoreSpawnGuardRateLimit: Boolean(meta.auto_continue_ignore_spawn_guard_rate_limit),
834
+ maxParallelSlices: normalizeMaxParallelSlices(meta.auto_continue_max_parallel, AUTO_CONTINUE_MAX_PARALLEL_DEFAULT),
835
+ parallelMode: normalizeParallelMode(meta.auto_continue_parallel_mode),
836
+ scope: "task",
837
+ tokenBudget: normalizeTokenBudget(meta.auto_continue_token_budget, defaultAutoContinueTokenBudget()),
838
+ tokensUsed: typeof meta.auto_continue_tokens_used === "number" ? meta.auto_continue_tokens_used : 0,
839
+ status: status,
840
+ stopReason: meta.auto_continue_stop_reason ?? null,
841
+ stopRequested: false,
842
+ startedAt: meta.auto_continue_started_at ?? now,
843
+ stoppedAt: meta.auto_continue_stopped_at ?? null,
844
+ updatedAt: meta.auto_continue_updated_at ?? now,
845
+ lastError: meta.auto_continue_last_error ?? null,
846
+ lastTaskId: meta.auto_continue_last_task_id ?? null,
847
+ lastRunId: meta.auto_continue_last_run_id ?? null,
848
+ activeSliceRunIds: Array.isArray(meta.auto_continue_active_run_ids)
849
+ ? meta.auto_continue_active_run_ids
850
+ : [],
851
+ activeTaskIds: Array.isArray(meta.auto_continue_active_task_ids)
852
+ ? meta.auto_continue_active_task_ids
853
+ : [],
854
+ laneByWorkstreamId,
855
+ blockedWorkstreamIds: Array.isArray(meta.auto_continue_blocked_workstream_ids)
856
+ ? meta.auto_continue_blocked_workstream_ids
857
+ : [],
858
+ activeTaskId: meta.auto_continue_active_task_id ?? null,
859
+ activeRunId: meta.auto_continue_active_run_id ?? null,
860
+ activeTaskTokenEstimate: typeof meta.auto_continue_active_task_token_estimate === "number"
861
+ ? meta.auto_continue_active_task_token_estimate
862
+ : null,
863
+ workerEnvOverrides: null,
864
+ lastInitiativeStatus: typeof meta.status === "string" && meta.status.trim().length > 0
865
+ ? meta.status.trim()
866
+ : null,
867
+ };
868
+ ensureRunInternals(run);
869
+ syncLegacyRunPointers(run);
870
+ // Insert into in-memory map so subsequent lookups are fast.
871
+ autoContinueRuns.set(initiativeId, run);
872
+ return run;
873
+ }
874
+ catch {
875
+ return null;
876
+ }
877
+ }
736
878
  const localInitiativeStatusOverrides = new Map();
737
879
  const localTaskStatusOverrides = new Map();
738
880
  const localMilestoneStatusOverrides = new Map();
@@ -929,6 +1071,38 @@ export function createAutoContinueEngine(deps) {
929
1071
  autoContinueSliceChildren.delete(id);
930
1072
  autoContinueSliceLastHeartbeatMs.delete(id);
931
1073
  };
1074
+ const stopActiveSliceProcesses = async (sliceRunIds) => {
1075
+ for (const rawRunId of sliceRunIds) {
1076
+ const sliceRunId = rawRunId.trim();
1077
+ if (!sliceRunId)
1078
+ continue;
1079
+ const child = autoContinueSliceChildren.get(sliceRunId) ?? null;
1080
+ try {
1081
+ if (child && child.exitCode === null && !child.killed) {
1082
+ child.kill("SIGTERM");
1083
+ }
1084
+ }
1085
+ catch {
1086
+ // best effort
1087
+ }
1088
+ const slice = autoContinueSliceRuns.get(sliceRunId) ?? null;
1089
+ const pid = slice?.pid ?? child?.pid ?? null;
1090
+ if (pid && pidAlive(pid)) {
1091
+ try {
1092
+ await stopProcess(pid);
1093
+ }
1094
+ catch {
1095
+ // best effort
1096
+ }
1097
+ }
1098
+ if (slice) {
1099
+ slice.pid = null;
1100
+ slice.updatedAt = new Date().toISOString();
1101
+ autoContinueSliceRuns.set(sliceRunId, slice);
1102
+ }
1103
+ clearAutoContinueSliceTransientState(sliceRunId);
1104
+ }
1105
+ };
932
1106
  const AUTO_CONTINUE_SLICE_TIMEOUT_MS = readBudgetEnvNumber("ORGX_AUTOPILOT_SLICE_TIMEOUT_MS", 55 * 60_000,
933
1107
  // Keep test runs fast; real-world defaults are still ~1h unless overridden.
934
1108
  { min: 250, max: 6 * 60 * 60_000 });
@@ -1116,6 +1290,57 @@ export function createAutoContinueEngine(deps) {
1116
1290
  run.maxParallelSlices = normalizeMaxParallelSlices(run.maxParallelSlices, AUTO_CONTINUE_MAX_PARALLEL_DEFAULT);
1117
1291
  run.parallelMode = normalizeParallelMode(run.parallelMode);
1118
1292
  run.tokenBudget = normalizeTokenBudget(run.tokenBudget, defaultAutoContinueTokenBudget());
1293
+ if (!run.workerEnvOverrides || typeof run.workerEnvOverrides !== "object") {
1294
+ run.workerEnvOverrides = null;
1295
+ }
1296
+ run.workspaceId =
1297
+ typeof run.workspaceId === "string" && run.workspaceId.trim().length > 0
1298
+ ? run.workspaceId.trim()
1299
+ : null;
1300
+ run.lastInitiativeStatus =
1301
+ typeof run.lastInitiativeStatus === "string" && run.lastInitiativeStatus.trim().length > 0
1302
+ ? run.lastInitiativeStatus.trim()
1303
+ : null;
1304
+ };
1305
+ const laneStateToChildStatus = (laneState) => {
1306
+ if (laneState === LaneState.RUNNING)
1307
+ return "in_progress";
1308
+ if (laneState === LaneState.BLOCKED)
1309
+ return "blocked";
1310
+ if (laneState === LaneState.WAITING_DEPENDENCY || laneState === LaneState.RATE_LIMITED) {
1311
+ return "paused";
1312
+ }
1313
+ if (laneState === LaneState.COMPLETED)
1314
+ return "completed";
1315
+ return "todo";
1316
+ };
1317
+ const deriveInitiativeStatusFromRun = (run) => {
1318
+ ensureRunInternals(run);
1319
+ const childStatuses = Object.values(run.laneByWorkstreamId ?? {}).map((lane) => laneStateToChildStatus(lane.state));
1320
+ if (run.status === RunStatus.RUNNING || run.status === RunStatus.STOPPING) {
1321
+ return deriveInitiativeLifecycleStatus("active", childStatuses.length > 0 ? childStatuses : ["in_progress"]);
1322
+ }
1323
+ if (run.stopReason === "blocked" || run.stopReason === "error") {
1324
+ return "blocked";
1325
+ }
1326
+ if (run.stopReason === "completed") {
1327
+ const scopedRun = run.stopAfterSlice ||
1328
+ (Array.isArray(run.allowedWorkstreamIds) && run.allowedWorkstreamIds.length > 0);
1329
+ return scopedRun ? "paused" : "completed";
1330
+ }
1331
+ if (run.stopReason === "budget_exhausted" || run.stopReason === "stopped") {
1332
+ return "paused";
1333
+ }
1334
+ return childStatuses.length > 0
1335
+ ? deriveInitiativeLifecycleStatus("paused", childStatuses)
1336
+ : "paused";
1337
+ };
1338
+ const syncInitiativeLifecycleStatus = async (run) => {
1339
+ const nextStatus = deriveInitiativeStatusFromRun(run);
1340
+ if (run.lastInitiativeStatus === nextStatus)
1341
+ return;
1342
+ await client.updateEntity("initiative", run.initiativeId, { status: nextStatus });
1343
+ run.lastInitiativeStatus = nextStatus;
1119
1344
  };
1120
1345
  const recordLocalStatusOverrides = (input) => {
1121
1346
  const initiativeId = input.initiativeId.trim();
@@ -1390,6 +1615,7 @@ export function createAutoContinueEngine(deps) {
1390
1615
  updated_at: lane.updatedAt,
1391
1616
  }));
1392
1617
  const patch = {
1618
+ ...(input.run.workspaceId ? { workspace_id: input.run.workspaceId } : {}),
1393
1619
  auto_continue_enabled: input.run.status === RunStatus.RUNNING || input.run.status === RunStatus.STOPPING,
1394
1620
  auto_continue_status: input.run.status,
1395
1621
  auto_continue_stop_reason: input.run.stopReason,
@@ -1415,6 +1641,7 @@ export function createAutoContinueEngine(deps) {
1415
1641
  ...(input.run.lastError ? { auto_continue_last_error: input.run.lastError } : {}),
1416
1642
  };
1417
1643
  await updateInitiativeMetadata(input.initiativeId, patch);
1644
+ await syncInitiativeLifecycleStatus(input.run);
1418
1645
  }
1419
1646
  async function stopAutoContinueRun(input) {
1420
1647
  const decisionRequired = input.reason === "blocked" && input.decisionRequired === true;
@@ -1429,6 +1656,7 @@ export function createAutoContinueEngine(deps) {
1429
1656
  const now = new Date().toISOString();
1430
1657
  ensureRunInternals(input.run);
1431
1658
  const activeRunIds = listActiveSliceRunIds(input.run);
1659
+ await stopActiveSliceProcesses(activeRunIds);
1432
1660
  input.run.status = RunStatus.STOPPED;
1433
1661
  input.run.stopReason = input.reason;
1434
1662
  input.run.stoppedAt = now;
@@ -1458,18 +1686,6 @@ export function createAutoContinueEngine(deps) {
1458
1686
  for (const runId of activeRunIds) {
1459
1687
  clearAutoContinueSliceTransientState(runId);
1460
1688
  }
1461
- // Only pause the initiative on non-terminal stops (error, blocked, user-requested).
1462
- // Completed / budget-exhausted runs should not override the initiative status.
1463
- if (input.reason !== "completed" && input.reason !== "budget_exhausted") {
1464
- try {
1465
- await client.updateEntity("initiative", input.run.initiativeId, {
1466
- status: "paused",
1467
- });
1468
- }
1469
- catch {
1470
- // best effort
1471
- }
1472
- }
1473
1689
  try {
1474
1690
  await updateInitiativeAutoContinueState({
1475
1691
  initiativeId: input.run.initiativeId,
@@ -1590,7 +1806,7 @@ export function createAutoContinueEngine(deps) {
1590
1806
  old_state: LaneState.RUNNING,
1591
1807
  new_state: input.reason === "completed" || input.reason === "stopped" ? "idle" : input.reason === "blocked" ? "blocked" : input.reason === "error" ? "error" : "idle",
1592
1808
  reason: input.reason,
1593
- workspace_id: input.run.allowedWorkstreamIds?.[0] ?? null,
1809
+ workspace_id: input.run.workspaceId ?? null,
1594
1810
  },
1595
1811
  });
1596
1812
  }
@@ -1748,11 +1964,7 @@ export function createAutoContinueEngine(deps) {
1748
1964
  title: `Agent couldn't connect to tools: ${slice.workstreamTitle ?? slice.workstreamId}`,
1749
1965
  summary: humanizeSliceFailureSummary(`MCP handshake failed${mcpHandshake.server ? ` for ${mcpHandshake.server}` : ""}.`),
1750
1966
  urgency: "high",
1751
- options: [
1752
- "Retry this workstream slice",
1753
- "Pause autopilot and investigate",
1754
- "Skip this workstream for now",
1755
- ],
1967
+ options: defaultInterventionDecisionOptions(),
1756
1968
  blocking: true,
1757
1969
  decisionType: "autopilot_failure",
1758
1970
  workstreamId: slice.workstreamId,
@@ -1870,11 +2082,7 @@ export function createAutoContinueEngine(deps) {
1870
2082
  title: stallDecisionTitle,
1871
2083
  summary: humanizeSliceFailureSummary(slice.lastError ?? `Autopilot slice ${humanLabel}`),
1872
2084
  urgency: "high",
1873
- options: [
1874
- "Retry this workstream slice",
1875
- "Pause autopilot and investigate",
1876
- "Skip this workstream for now",
1877
- ],
2085
+ options: defaultInterventionDecisionOptions(),
1878
2086
  blocking: true,
1879
2087
  decisionType: "autopilot_failure",
1880
2088
  workstreamId: slice.workstreamId,
@@ -1972,6 +2180,68 @@ export function createAutoContinueEngine(deps) {
1972
2180
  }
1973
2181
  }
1974
2182
  const defaultDecisionBlocking = parsedStatus === "completed" ? false : true;
2183
+ const normalizeDecisionOptions = (value) => {
2184
+ if (!Array.isArray(value))
2185
+ return [];
2186
+ const normalized = [];
2187
+ for (const rawOption of value) {
2188
+ if (typeof rawOption === "string") {
2189
+ const label = rawOption.trim();
2190
+ if (label.length > 0)
2191
+ normalized.push(label);
2192
+ continue;
2193
+ }
2194
+ if (!rawOption || typeof rawOption !== "object" || Array.isArray(rawOption)) {
2195
+ continue;
2196
+ }
2197
+ const optionRecord = rawOption;
2198
+ const label = (typeof optionRecord.label === "string" && optionRecord.label.trim()) ||
2199
+ (typeof optionRecord.title === "string" && optionRecord.title.trim()) ||
2200
+ (typeof optionRecord.name === "string" && optionRecord.name.trim()) ||
2201
+ null;
2202
+ if (!label)
2203
+ continue;
2204
+ const normalizedRecord = { label };
2205
+ const id = (typeof optionRecord.id === "string" && optionRecord.id.trim()) ||
2206
+ (typeof optionRecord.option_id === "string" && optionRecord.option_id.trim()) ||
2207
+ null;
2208
+ if (id)
2209
+ normalizedRecord.id = id;
2210
+ const description = (typeof optionRecord.description === "string" && optionRecord.description.trim()) ||
2211
+ null;
2212
+ if (description)
2213
+ normalizedRecord.description = description;
2214
+ const consequences = (typeof optionRecord.consequences === "string" && optionRecord.consequences.trim()) ||
2215
+ (typeof optionRecord.impact === "string" && optionRecord.impact.trim()) ||
2216
+ null;
2217
+ if (consequences)
2218
+ normalizedRecord.consequences = consequences;
2219
+ const impliedStatusRaw = typeof optionRecord.implied_status === "string"
2220
+ ? optionRecord.implied_status
2221
+ : typeof optionRecord.status === "string"
2222
+ ? optionRecord.status
2223
+ : null;
2224
+ if (impliedStatusRaw) {
2225
+ const implied = impliedStatusRaw.trim().toLowerCase();
2226
+ if (implied === "approved" ||
2227
+ implied === "declined" ||
2228
+ implied === "cancelled" ||
2229
+ implied === "rejected") {
2230
+ normalizedRecord.implied_status = implied;
2231
+ }
2232
+ }
2233
+ const actionType = normalizeDecisionActionType(optionRecord.action_type ?? optionRecord.type ?? optionRecord.verb ?? optionRecord.action);
2234
+ if (actionType)
2235
+ normalizedRecord.action_type = actionType;
2236
+ if (optionRecord.requires_note === true ||
2237
+ optionRecord.requiresNote === true ||
2238
+ optionRecord.note_required === true) {
2239
+ normalizedRecord.requires_note = true;
2240
+ }
2241
+ normalized.push(normalizedRecord);
2242
+ }
2243
+ return normalized.slice(0, 8);
2244
+ };
1975
2245
  const allDecisions = Array.isArray(parsed?.decisions_needed)
1976
2246
  ? (parsed?.decisions_needed ?? [])
1977
2247
  .filter((item) => Boolean(item && typeof item.question === "string" && item.question.trim()))
@@ -2115,9 +2385,7 @@ export function createAutoContinueEngine(deps) {
2115
2385
  title: normalizedQuestion,
2116
2386
  summary: decision.summary ?? parsed?.summary ?? null,
2117
2387
  urgency: decision.urgency ?? "high",
2118
- options: Array.isArray(decision.options)
2119
- ? decision.options.filter((opt) => typeof opt === "string" && opt.trim())
2120
- : [],
2388
+ options: normalizeDecisionOptions(decision.options),
2121
2389
  blocking: isBlocking,
2122
2390
  decisionType: isBlocking
2123
2391
  ? "autopilot_blocking_decision"
@@ -2511,11 +2779,7 @@ export function createAutoContinueEngine(deps) {
2511
2779
  title: fallbackDecisionTitle,
2512
2780
  summary: fallbackDecisionSummary,
2513
2781
  urgency: "high",
2514
- options: [
2515
- "Retry this workstream slice",
2516
- "Pause autopilot and investigate",
2517
- "Skip this workstream for now",
2518
- ],
2782
+ options: defaultInterventionDecisionOptions(),
2519
2783
  blocking: true,
2520
2784
  decisionType: looksLikeNoOutcome
2521
2785
  ? "autopilot_completed_without_outcome"
@@ -2597,11 +2861,7 @@ export function createAutoContinueEngine(deps) {
2597
2861
  title: attentionTitle,
2598
2862
  summary: attentionSummary,
2599
2863
  urgency: "high",
2600
- options: [
2601
- "Retry this workstream slice",
2602
- "Pause autopilot and investigate",
2603
- "Skip this workstream for now",
2604
- ],
2864
+ options: defaultInterventionDecisionOptions(),
2605
2865
  blocking: true,
2606
2866
  decisionType: completionHadNoOutcome
2607
2867
  ? "autopilot_completed_without_outcome"
@@ -3609,16 +3869,27 @@ export function createAutoContinueEngine(deps) {
3609
3869
  const logsDir = join(getOrgxPluginConfigDir(), AUTO_CONTINUE_SLICE_LOG_DIRNAME);
3610
3870
  const logPath = join(logsDir, `${sliceRunId}.log`);
3611
3871
  const outputPath = join(logsDir, `${sliceRunId}.output.json`);
3612
- const configuredWorkerCwd = (process.env.ORGX_AUTOPILOT_CWD ?? "").trim();
3872
+ const workerEnvOverrides = run.workerEnvOverrides ?? defaultWorkerEnvOverrides;
3873
+ const configuredWorkerCwd = (workerEnvOverrides?.ORGX_AUTOPILOT_CWD ??
3874
+ process.env.ORGX_AUTOPILOT_CWD ??
3875
+ "").trim();
3613
3876
  let workerCwd = configuredWorkerCwd || resolveAutopilotDefaultCwd(__filename);
3614
3877
  // LaunchAgents sometimes start with cwd="/". Fall back to plugin root (or home if unresolved).
3615
3878
  if (!workerCwd || workerCwd === "/") {
3616
3879
  workerCwd = resolveAutopilotDefaultCwd(__filename);
3617
3880
  }
3618
3881
  const sliceAgent = resolveOrgxAgentForDomain(executionPolicy.domain);
3619
- const workerKind = (process.env.ORGX_AUTOPILOT_WORKER_KIND ?? "").trim().toLowerCase();
3882
+ const workerKind = (workerEnvOverrides?.ORGX_AUTOPILOT_WORKER_KIND ??
3883
+ process.env.ORGX_AUTOPILOT_WORKER_KIND ??
3884
+ "")
3885
+ .trim()
3886
+ .toLowerCase();
3620
3887
  const inferredExecutor = workerKind === "claude-code" || workerKind === "claude_code" ? "claude-code" : "codex";
3621
- const executorRaw = (process.env.ORGX_AUTOPILOT_EXECUTOR ?? "").trim().toLowerCase() || inferredExecutor;
3888
+ const executorRaw = (workerEnvOverrides?.ORGX_AUTOPILOT_EXECUTOR ??
3889
+ process.env.ORGX_AUTOPILOT_EXECUTOR ??
3890
+ "")
3891
+ .trim()
3892
+ .toLowerCase() || inferredExecutor;
3622
3893
  const executorSourceClient = executorRaw === "claude-code" || executorRaw === "claude_code" ? "claude-code" : "codex";
3623
3894
  let runtimeHookUrl = null;
3624
3895
  let runtimeHookToken = null;
@@ -3643,6 +3914,7 @@ export function createAutoContinueEngine(deps) {
3643
3914
  outputSchemaPath: schemaPath,
3644
3915
  resumeSessionId: resumedFromSessionId,
3645
3916
  env: {
3917
+ ...(workerEnvOverrides ?? {}),
3646
3918
  ORGX_SOURCE_CLIENT: executorSourceClient,
3647
3919
  ORGX_RUN_ID: sliceRunId,
3648
3920
  ORGX_CORRELATION_ID: sliceRunId,
@@ -3820,12 +4092,6 @@ export function createAutoContinueEngine(deps) {
3820
4092
  // Clear stale errors when a new slice dispatches successfully.
3821
4093
  run.lastError = null;
3822
4094
  run.updatedAt = now;
3823
- try {
3824
- await client.updateEntity("initiative", run.initiativeId, { status: "active" });
3825
- }
3826
- catch {
3827
- // best effort
3828
- }
3829
4095
  try {
3830
4096
  await updateInitiativeAutoContinueState({
3831
4097
  initiativeId: run.initiativeId,
@@ -3918,6 +4184,7 @@ export function createAutoContinueEngine(deps) {
3918
4184
  const sourceEvent = (input.event ?? "").trim() || null;
3919
4185
  const requestedByAgentId = (input.requestedByAgentId ?? "").trim() || null;
3920
4186
  const requestedByAgentName = (input.requestedByAgentName ?? "").trim() || null;
4187
+ const autoFixWorkerEnv = captureAutopilotWorkerEnv();
3921
4188
  const providedGraceMs = typeof input.graceMs === "number" && Number.isFinite(input.graceMs)
3922
4189
  ? Math.floor(input.graceMs)
3923
4190
  : null;
@@ -4093,6 +4360,7 @@ export function createAutoContinueEngine(deps) {
4093
4360
  null;
4094
4361
  const dispatchRun = await startAutoContinueRun({
4095
4362
  initiativeId,
4363
+ workspaceId: latestRun?.workspaceId ?? null,
4096
4364
  agentId: dispatchAgentId,
4097
4365
  agentName: dispatchAgentName,
4098
4366
  // Auto-fix retries should follow current defaults unless an operator explicitly
@@ -4104,6 +4372,7 @@ export function createAutoContinueEngine(deps) {
4104
4372
  parallelMode: latestRun?.parallelMode ?? "iwmt",
4105
4373
  stopAfterSlice: true,
4106
4374
  ignoreSpawnGuardRateLimit: latestRun?.ignoreSpawnGuardRateLimit ?? false,
4375
+ workerEnvOverrides: autoFixWorkerEnv,
4107
4376
  });
4108
4377
  await tickAutoContinueRun(dispatchRun);
4109
4378
  await emitActivitySafe({
@@ -4198,11 +4467,15 @@ export function createAutoContinueEngine(deps) {
4198
4467
  }
4199
4468
  async function startAutoContinueRun(input) {
4200
4469
  const now = new Date().toISOString();
4470
+ const nextWorkerEnvOverrides = input.workerEnvOverrides && typeof input.workerEnvOverrides === "object"
4471
+ ? { ...input.workerEnvOverrides }
4472
+ : { ...defaultWorkerEnvOverrides };
4201
4473
  const existing = autoContinueRuns.get(input.initiativeId) ?? null;
4202
4474
  const existingIsLive = existing?.status === RunStatus.RUNNING || existing?.status === RunStatus.STOPPING;
4203
4475
  const run = existing ??
4204
4476
  {
4205
4477
  initiativeId: input.initiativeId,
4478
+ workspaceId: null,
4206
4479
  agentId: input.agentId,
4207
4480
  agentName: input.agentName ?? null,
4208
4481
  includeVerification: false,
@@ -4230,8 +4503,14 @@ export function createAutoContinueEngine(deps) {
4230
4503
  activeTaskId: null,
4231
4504
  activeRunId: null,
4232
4505
  activeTaskTokenEstimate: null,
4506
+ workerEnvOverrides: null,
4507
+ lastInitiativeStatus: null,
4233
4508
  };
4234
4509
  ensureRunInternals(run);
4510
+ run.workspaceId =
4511
+ typeof input.workspaceId === "string" && input.workspaceId.trim().length > 0
4512
+ ? input.workspaceId.trim()
4513
+ : run.workspaceId;
4235
4514
  run.agentId = input.agentId;
4236
4515
  run.agentName =
4237
4516
  typeof input.agentName === "string" && input.agentName.trim().length > 0
@@ -4244,6 +4523,7 @@ export function createAutoContinueEngine(deps) {
4244
4523
  run.stopAfterSlice = Boolean(input.stopAfterSlice);
4245
4524
  run.ignoreSpawnGuardRateLimit = Boolean(input.ignoreSpawnGuardRateLimit);
4246
4525
  run.scope = input.scope ?? "task";
4526
+ run.workerEnvOverrides = nextWorkerEnvOverrides;
4247
4527
  const hasExplicitTokenBudgetInput = input.tokenBudget !== null &&
4248
4528
  input.tokenBudget !== undefined &&
4249
4529
  !(typeof input.tokenBudget === "string" && input.tokenBudget.trim().length === 0);
@@ -4280,11 +4560,6 @@ export function createAutoContinueEngine(deps) {
4280
4560
  }
4281
4561
  syncLegacyRunPointers(run);
4282
4562
  autoContinueRuns.set(input.initiativeId, run);
4283
- void client
4284
- .updateEntity("initiative", input.initiativeId, { status: "active" })
4285
- .catch(() => {
4286
- // best effort
4287
- });
4288
4563
  void updateInitiativeAutoContinueState({
4289
4564
  initiativeId: input.initiativeId,
4290
4565
  run,
@@ -4343,7 +4618,7 @@ export function createAutoContinueEngine(deps) {
4343
4618
  old_state: LaneState.IDLE,
4344
4619
  new_state: LaneState.RUNNING,
4345
4620
  reason: "started",
4346
- workspace_id: run.allowedWorkstreamIds?.[0] ?? null,
4621
+ workspace_id: run.workspaceId ?? null,
4347
4622
  },
4348
4623
  });
4349
4624
  }
@@ -4470,6 +4745,7 @@ export function createAutoContinueEngine(deps) {
4470
4745
  getAutoContinueLaneForWorkstream,
4471
4746
  scheduleAutoFixForWorkstream,
4472
4747
  startAutoContinueRun,
4748
+ restoreAutoContinueRun,
4473
4749
  skipCurrentWorkstream,
4474
4750
  getCanonicalAutopilotState,
4475
4751
  // Session store (for resume support)
@@ -199,7 +199,11 @@ export function createAutopilotRuntime(deps) {
199
199
  function spawnCodexSliceWorker(input) {
200
200
  ensurePrivateDirForFile(input.logPath);
201
201
  ensurePrivateDirForFile(input.outputPath);
202
- const workerKind = (process.env.ORGX_AUTOPILOT_WORKER_KIND ?? "").trim().toLowerCase();
202
+ const workerKind = (input.env.ORGX_AUTOPILOT_WORKER_KIND ??
203
+ process.env.ORGX_AUTOPILOT_WORKER_KIND ??
204
+ "")
205
+ .trim()
206
+ .toLowerCase();
203
207
  if (workerKind === "mock") {
204
208
  const scriptPath = resolve(dirname(deps.filename), "..", "..", "scripts", "mock-autopilot-slice-worker.mjs");
205
209
  const logStream = createSafeAppendStream(input.logPath);
@@ -33,7 +33,27 @@ function autopilotSliceSchema() {
33
33
  const decisionProperties = {
34
34
  question: { type: "string", minLength: 1 },
35
35
  summary: { type: ["string", "null"] },
36
- options: { type: ["array", "null"], items: { type: "string" } },
36
+ options: {
37
+ type: ["array", "null"],
38
+ items: {
39
+ type: ["string", "object"],
40
+ minLength: 1,
41
+ additionalProperties: false,
42
+ required: ["label"],
43
+ properties: {
44
+ id: { type: ["string", "null"] },
45
+ label: { type: "string", minLength: 1 },
46
+ description: { type: ["string", "null"] },
47
+ consequences: { type: ["string", "null"] },
48
+ implied_status: {
49
+ type: ["string", "null"],
50
+ enum: ["approved", "declined", "cancelled", "rejected", null],
51
+ },
52
+ action_type: { type: ["string", "null"] },
53
+ requires_note: { type: ["boolean", "null"] },
54
+ },
55
+ },
56
+ },
37
57
  urgency: {
38
58
  type: ["string", "null"],
39
59
  enum: ["low", "medium", "high", "urgent", null],
@@ -859,6 +879,8 @@ export function buildSliceOutputInstructions(input) {
859
879
  "- Artifacts must be verifiable: include URLs or local paths, plus verification steps.",
860
880
  "- Include `confidence_score` for each artifact (`0` to `1`; use `null` when unknown).",
861
881
  "- If you need a human decision, include it in decisions_needed.",
882
+ "- Prefer structured decision options objects with: id, label, description, consequences, implied_status, action_type, requires_note.",
883
+ "- String options are still accepted, but structured options are required for precise decision routing.",
862
884
  "- For every decisions_needed entry, ALWAYS set blocking explicitly (true or false).",
863
885
  "- If status is blocked, needs_decision, or error: include at least one decisions_needed entry with blocking=true.",
864
886
  "- Status/decision consistency is strict:",
@@ -959,6 +981,8 @@ export function buildWorkstreamSlicePrompt(input) {
959
981
  "- Artifacts must be verifiable: include URLs or local paths, plus verification steps.",
960
982
  "- Include `confidence_score` for each artifact (`0` to `1`; use `null` when unknown).",
961
983
  "- If you need a human decision, include it in decisions_needed.",
984
+ "- Prefer structured decision options objects with: id, label, description, consequences, implied_status, action_type, requires_note.",
985
+ "- String options are still accepted, but structured options are required for precise decision routing.",
962
986
  "- For every decisions_needed entry, ALWAYS set blocking explicitly (true or false).",
963
987
  "- If status is blocked, needs_decision, or error: include at least one decisions_needed entry with blocking=true.",
964
988
  "- Status/decision consistency is strict:",
@@ -5,6 +5,7 @@ type LiveDecisionOption = {
5
5
  id: string;
6
6
  label: string;
7
7
  description: string | null;
8
+ consequences: string | null;
8
9
  impliedStatus: LiveDecisionOptionStatus | null;
9
10
  actionType: DecisionActionType | null;
10
11
  requiresNote: boolean;