@useorgx/openclaw-plugin 0.7.6 → 0.7.11

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 (127) hide show
  1. package/dashboard/dist/assets/{CnitK1MX.js → AqVoI3SF.js} +1 -1
  2. package/dashboard/dist/assets/AqVoI3SF.js.br +0 -0
  3. package/dashboard/dist/assets/AqVoI3SF.js.gz +0 -0
  4. package/dashboard/dist/assets/BC4WvnHJ.js +1 -0
  5. package/dashboard/dist/assets/BC4WvnHJ.js.br +0 -0
  6. package/dashboard/dist/assets/BC4WvnHJ.js.gz +0 -0
  7. package/dashboard/dist/assets/{DOFL9l8s.js → BG5mwTkg.js} +1 -1
  8. package/dashboard/dist/assets/BG5mwTkg.js.br +0 -0
  9. package/dashboard/dist/assets/BG5mwTkg.js.gz +0 -0
  10. package/dashboard/dist/assets/BNh-XYPV.js +1 -0
  11. package/dashboard/dist/assets/BNh-XYPV.js.br +0 -0
  12. package/dashboard/dist/assets/BNh-XYPV.js.gz +0 -0
  13. package/dashboard/dist/assets/{CFwPph5U.js → BepW_590.js} +1 -1
  14. package/dashboard/dist/assets/BepW_590.js.br +0 -0
  15. package/dashboard/dist/assets/BepW_590.js.gz +0 -0
  16. package/dashboard/dist/assets/BerAfzjq.js +1 -0
  17. package/dashboard/dist/assets/BerAfzjq.js.br +0 -0
  18. package/dashboard/dist/assets/BerAfzjq.js.gz +0 -0
  19. package/dashboard/dist/assets/Bp3N-QL5.js +212 -0
  20. package/dashboard/dist/assets/Bp3N-QL5.js.br +0 -0
  21. package/dashboard/dist/assets/Bp3N-QL5.js.gz +0 -0
  22. package/dashboard/dist/assets/C3dZRz9P.css +1 -0
  23. package/dashboard/dist/assets/C3dZRz9P.css.br +0 -0
  24. package/dashboard/dist/assets/C3dZRz9P.css.gz +0 -0
  25. package/dashboard/dist/assets/CD-q5mdP.js +1 -0
  26. package/dashboard/dist/assets/CD-q5mdP.js.br +0 -0
  27. package/dashboard/dist/assets/CD-q5mdP.js.gz +0 -0
  28. package/dashboard/dist/assets/{BgcAY5rE.js → CdvjC9G9.js} +1 -1
  29. package/dashboard/dist/assets/CdvjC9G9.js.br +0 -0
  30. package/dashboard/dist/assets/CdvjC9G9.js.gz +0 -0
  31. package/dashboard/dist/assets/Ck2agw-s.js +1 -0
  32. package/dashboard/dist/assets/Ck2agw-s.js.br +0 -0
  33. package/dashboard/dist/assets/Ck2agw-s.js.gz +0 -0
  34. package/dashboard/dist/assets/{D7DHFX0D.js → D2CH1H6k.js} +1 -1
  35. package/dashboard/dist/assets/D2CH1H6k.js.br +0 -0
  36. package/dashboard/dist/assets/D2CH1H6k.js.gz +0 -0
  37. package/dashboard/dist/assets/D9esz7jd.js +1 -0
  38. package/dashboard/dist/assets/D9esz7jd.js.br +0 -0
  39. package/dashboard/dist/assets/D9esz7jd.js.gz +0 -0
  40. package/dashboard/dist/assets/{77gGFBt6.js → DCP-C7fn.js} +1 -1
  41. package/dashboard/dist/assets/DCP-C7fn.js.br +0 -0
  42. package/dashboard/dist/assets/DCP-C7fn.js.gz +0 -0
  43. package/dashboard/dist/assets/{CSr2ZnTV.js → DJASCd69.js} +1 -1
  44. package/dashboard/dist/assets/DJASCd69.js.br +0 -0
  45. package/dashboard/dist/assets/DJASCd69.js.gz +0 -0
  46. package/dashboard/dist/assets/Dm9AybAp.js +1 -0
  47. package/dashboard/dist/assets/Dm9AybAp.js.br +0 -0
  48. package/dashboard/dist/assets/Dm9AybAp.js.gz +0 -0
  49. package/dashboard/dist/assets/{DxKG5zy8.js → Du1wfrXa.js} +1 -1
  50. package/dashboard/dist/assets/Du1wfrXa.js.br +0 -0
  51. package/dashboard/dist/assets/Du1wfrXa.js.gz +0 -0
  52. package/dashboard/dist/assets/{DpuQm1oF.js → beHYBbh6.js} +1 -1
  53. package/dashboard/dist/assets/beHYBbh6.js.br +0 -0
  54. package/dashboard/dist/assets/beHYBbh6.js.gz +0 -0
  55. package/dashboard/dist/index.html +2 -2
  56. package/dashboard/dist/index.html.br +0 -0
  57. package/dashboard/dist/index.html.gz +0 -0
  58. package/dist/hash-utils.js +2 -1
  59. package/dist/http/helpers/auto-continue-engine.d.ts +36 -0
  60. package/dist/http/helpers/auto-continue-engine.js +83 -17
  61. package/dist/http/helpers/autopilot-runtime.d.ts +1 -0
  62. package/dist/http/helpers/autopilot-runtime.js +31 -3
  63. package/dist/http/helpers/autopilot-slice-utils.d.ts +10 -0
  64. package/dist/http/helpers/autopilot-slice-utils.js +58 -0
  65. package/dist/http/helpers/humanize-slice-failure.d.ts +35 -0
  66. package/dist/http/helpers/humanize-slice-failure.js +137 -0
  67. package/dist/http/helpers/mission-control.d.ts +1 -0
  68. package/dist/http/helpers/mission-control.js +72 -5
  69. package/dist/http/index.js +65 -3
  70. package/dist/http/routes/live-misc.js +12 -4
  71. package/dist/http/routes/live-snapshot.js +10 -4
  72. package/dist/http/routes/mission-control-actions.js +5 -0
  73. package/dist/http/routes/mission-control-read.d.ts +1 -0
  74. package/dist/http/routes/mission-control-read.js +65 -4
  75. package/dist/index.d.ts +8 -1
  76. package/dist/index.js +21 -1
  77. package/dist/mcp-http-handler.js +6 -0
  78. package/dist/openclaw.plugin.json +1 -1
  79. package/dist/tools/core-tools.d.ts +27 -0
  80. package/dist/tools/core-tools.js +89 -0
  81. package/openclaw.plugin.json +1 -1
  82. package/package.json +1 -1
  83. package/dashboard/dist/assets/77gGFBt6.js.br +0 -0
  84. package/dashboard/dist/assets/77gGFBt6.js.gz +0 -0
  85. package/dashboard/dist/assets/BBpTN_SR.js +0 -1
  86. package/dashboard/dist/assets/BBpTN_SR.js.br +0 -0
  87. package/dashboard/dist/assets/BBpTN_SR.js.gz +0 -0
  88. package/dashboard/dist/assets/BVShoyjA.js +0 -1
  89. package/dashboard/dist/assets/BVShoyjA.js.br +0 -0
  90. package/dashboard/dist/assets/BVShoyjA.js.gz +0 -0
  91. package/dashboard/dist/assets/BgcAY5rE.js.br +0 -0
  92. package/dashboard/dist/assets/BgcAY5rE.js.gz +0 -0
  93. package/dashboard/dist/assets/C-PAoJF-.js +0 -1
  94. package/dashboard/dist/assets/C-PAoJF-.js.br +0 -0
  95. package/dashboard/dist/assets/C-PAoJF-.js.gz +0 -0
  96. package/dashboard/dist/assets/C0nA-iUG.js +0 -1
  97. package/dashboard/dist/assets/C0nA-iUG.js.br +0 -0
  98. package/dashboard/dist/assets/C0nA-iUG.js.gz +0 -0
  99. package/dashboard/dist/assets/C6GO-FKy.js +0 -1
  100. package/dashboard/dist/assets/C6GO-FKy.js.br +0 -0
  101. package/dashboard/dist/assets/C6GO-FKy.js.gz +0 -0
  102. package/dashboard/dist/assets/CFwPph5U.js.br +0 -0
  103. package/dashboard/dist/assets/CFwPph5U.js.gz +0 -0
  104. package/dashboard/dist/assets/CPjsbbgZ.js +0 -212
  105. package/dashboard/dist/assets/CPjsbbgZ.js.br +0 -0
  106. package/dashboard/dist/assets/CPjsbbgZ.js.gz +0 -0
  107. package/dashboard/dist/assets/CSr2ZnTV.js.br +0 -0
  108. package/dashboard/dist/assets/CSr2ZnTV.js.gz +0 -0
  109. package/dashboard/dist/assets/CgQDT6yL.js +0 -1
  110. package/dashboard/dist/assets/CgQDT6yL.js.br +0 -0
  111. package/dashboard/dist/assets/CgQDT6yL.js.gz +0 -0
  112. package/dashboard/dist/assets/CnitK1MX.js.br +0 -0
  113. package/dashboard/dist/assets/CnitK1MX.js.gz +0 -0
  114. package/dashboard/dist/assets/D7DHFX0D.js.br +0 -0
  115. package/dashboard/dist/assets/D7DHFX0D.js.gz +0 -0
  116. package/dashboard/dist/assets/DEip7uko.js +0 -1
  117. package/dashboard/dist/assets/DEip7uko.js.br +0 -0
  118. package/dashboard/dist/assets/DEip7uko.js.gz +0 -0
  119. package/dashboard/dist/assets/DHUSLc01.css +0 -1
  120. package/dashboard/dist/assets/DHUSLc01.css.br +0 -0
  121. package/dashboard/dist/assets/DHUSLc01.css.gz +0 -0
  122. package/dashboard/dist/assets/DOFL9l8s.js.br +0 -0
  123. package/dashboard/dist/assets/DOFL9l8s.js.gz +0 -0
  124. package/dashboard/dist/assets/DpuQm1oF.js.br +0 -0
  125. package/dashboard/dist/assets/DpuQm1oF.js.gz +0 -0
  126. package/dashboard/dist/assets/DxKG5zy8.js.br +0 -0
  127. package/dashboard/dist/assets/DxKG5zy8.js.gz +0 -0
@@ -9,10 +9,11 @@ import { appendTeamCompletion } from "../../team-context-store.js";
9
9
  import { readOpenClawGatewayPort, readOpenClawSettingsSnapshot, } from "../../openclaw-settings.js";
10
10
  import { resolveRuntimeHookToken, } from "../../runtime-instance-store.js";
11
11
  import { detectMcpHandshakeFailure, shouldKillWorker } from "../../worker-supervisor.js";
12
+ import { humanizeSliceFailure, humanizeSliceFailureSummary } from "./humanize-slice-failure.js";
12
13
  import { getOrgxPluginConfigDir } from "../../paths.js";
13
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
15
  import { createAutopilotRuntime } from "./autopilot-runtime.js";
15
- import { buildScopeDirective, buildSliceOutputInstructions, buildWorkstreamSlicePrompt, createCodexBinResolver, ensureAutopilotSliceSchemaPath, fileUpdatedAtEpochMs, parseSliceResult, readFileTailSafe, readSliceOutputFile, } from "./autopilot-slice-utils.js";
16
+ import { buildScopeDirective, buildSliceOutputInstructions, buildWorkstreamSlicePrompt, createCodexBinResolver, ensureAutopilotSliceSchemaPath, extractSessionIdFromLog, extractSessionIdFromOutput, fileUpdatedAtEpochMs, parseSliceResult, readFileTailSafe, readSliceOutputFile, } from "./autopilot-slice-utils.js";
16
17
  import { pickString } from "./value-utils.js";
17
18
  function resolveAutopilotDefaultCwd(filename) {
18
19
  let cursor = dirname(filename);
@@ -63,6 +64,35 @@ export function createAutoContinueEngine(deps) {
63
64
  ? "reject"
64
65
  : "approve";
65
66
  const autoContinueSliceRuns = new Map();
67
+ const workstreamSessionStore = new Map();
68
+ function sessionResumeEnabled() {
69
+ const raw = (process.env.ORGX_AUTOPILOT_SESSION_RESUME ?? "").trim().toLowerCase();
70
+ if (!raw)
71
+ return false;
72
+ return !(raw === "0" || raw === "false" || raw === "no" || raw === "off");
73
+ }
74
+ function setWorkstreamSession(workstreamId, entry) {
75
+ workstreamSessionStore.set(workstreamId, entry);
76
+ }
77
+ function getWorkstreamSession(workstreamId) {
78
+ return workstreamSessionStore.get(workstreamId) ?? null;
79
+ }
80
+ function clearWorkstreamSession(initiativeId) {
81
+ for (const [key, entry] of workstreamSessionStore.entries()) {
82
+ if (entry.initiativeId === initiativeId) {
83
+ workstreamSessionStore.delete(key);
84
+ }
85
+ }
86
+ }
87
+ function listWorkstreamSessions(initiativeId) {
88
+ const results = [];
89
+ for (const entry of workstreamSessionStore.values()) {
90
+ if (!initiativeId || entry.initiativeId === initiativeId) {
91
+ results.push(entry);
92
+ }
93
+ }
94
+ return results;
95
+ }
66
96
  /** Spread into any metadata object to flag mock-worker activity. */
67
97
  function mockMeta(slice) {
68
98
  return slice.isMockWorker ? { mock: true } : {};
@@ -1700,8 +1730,8 @@ export function createAutoContinueEngine(deps) {
1700
1730
  const decisionResult = await requestDecisionQueued({
1701
1731
  initiativeId: run.initiativeId,
1702
1732
  correlationId: slice.runId,
1703
- title: `Autopilot slice MCP failed: ${slice.workstreamTitle ?? slice.workstreamId}`,
1704
- summary: `MCP handshake failed${mcpHandshake.server ? ` for ${mcpHandshake.server}` : ""}. Review logs/output and decide whether to retry or pause autopilot.`,
1733
+ title: `Agent couldn't connect to tools: ${slice.workstreamTitle ?? slice.workstreamId}`,
1734
+ summary: humanizeSliceFailureSummary(`MCP handshake failed${mcpHandshake.server ? ` for ${mcpHandshake.server}` : ""}.`),
1705
1735
  urgency: "high",
1706
1736
  options: [
1707
1737
  "Retry this workstream slice",
@@ -1819,8 +1849,8 @@ export function createAutoContinueEngine(deps) {
1819
1849
  const decisionResult = await requestDecisionQueued({
1820
1850
  initiativeId: run.initiativeId,
1821
1851
  correlationId: slice.runId,
1822
- title: `Autopilot slice ${humanLabel}: ${slice.workstreamTitle ?? slice.workstreamId}`,
1823
- summary: "The slice was terminated because it stopped making progress. Review logs/output and decide whether to retry or pause autopilot.",
1852
+ title: `Agent ${humanLabel === "timed out" ? "ran out of time" : "stopped making progress"}: ${slice.workstreamTitle ?? slice.workstreamId}`,
1853
+ summary: humanizeSliceFailureSummary(slice.lastError ?? `Autopilot slice ${humanLabel}`),
1824
1854
  urgency: "high",
1825
1855
  options: [
1826
1856
  "Retry this workstream slice",
@@ -1904,6 +1934,25 @@ export function createAutoContinueEngine(deps) {
1904
1934
  const raw = readSliceOutputFile(slice.outputPath);
1905
1935
  const parsed = raw ? parseSliceResult(raw) : null;
1906
1936
  const parsedStatus = parsed?.status ?? "error";
1937
+ // Session capture: extract CLI session ID from output or log for future resume.
1938
+ if (sessionResumeEnabled()) {
1939
+ const outputSessionId = raw ? extractSessionIdFromOutput(raw, slice.sourceClient) : null;
1940
+ const logSessionId = outputSessionId
1941
+ ? null
1942
+ : extractSessionIdFromLog(readFileTailSafe(slice.logPath, 32_000), slice.sourceClient);
1943
+ const capturedSessionId = outputSessionId ?? logSessionId ?? null;
1944
+ if (capturedSessionId) {
1945
+ slice.cliSessionId = capturedSessionId;
1946
+ setWorkstreamSession(slice.workstreamId, {
1947
+ sessionId: capturedSessionId,
1948
+ workstreamId: slice.workstreamId,
1949
+ initiativeId: slice.initiativeId,
1950
+ sourceClient: slice.sourceClient,
1951
+ capturedAt: new Date().toISOString(),
1952
+ fromRunId: slice.runId,
1953
+ });
1954
+ }
1955
+ }
1907
1956
  const defaultDecisionBlocking = parsedStatus === "completed" ? false : true;
1908
1957
  const allDecisions = Array.isArray(parsed?.decisions_needed)
1909
1958
  ? (parsed?.decisions_needed ?? [])
@@ -2335,17 +2384,18 @@ export function createAutoContinueEngine(deps) {
2335
2384
  };
2336
2385
  if (!blockingDecisionQueued) {
2337
2386
  const blockedLike = slice.status === "blocked";
2387
+ const fallbackRawError = parsed?.summary ?? slice.lastError ??
2388
+ (blockedLike
2389
+ ? "Execution is blocked and needs intervention."
2390
+ : "Agent process exited without a valid output contract.");
2391
+ const fallbackHumanized = humanizeSliceFailure(fallbackRawError);
2338
2392
  fallbackDecisionResult = await requestDecisionQueued({
2339
2393
  initiativeId: run.initiativeId,
2340
2394
  correlationId: slice.runId,
2341
2395
  title: blockedLike
2342
- ? `Autopilot slice blocked: ${slice.workstreamTitle ?? slice.workstreamId}`
2343
- : `Autopilot slice failed: ${slice.workstreamTitle ?? slice.workstreamId}`,
2344
- summary: parsed?.summary ??
2345
- slice.lastError ??
2346
- (blockedLike
2347
- ? "The slice reported a blocked/decision-required state without a blocking decision payload. Review logs/output and decide whether to retry, unblock, or skip."
2348
- : "The slice failed without producing a valid output contract. Review logs/output and decide whether to retry or pause autopilot."),
2396
+ ? `Agent needs your help: ${slice.workstreamTitle ?? slice.workstreamId}`
2397
+ : `${fallbackHumanized.headline}: ${slice.workstreamTitle ?? slice.workstreamId}`,
2398
+ summary: fallbackHumanized.explanation,
2349
2399
  urgency: "high",
2350
2400
  options: [
2351
2401
  "Retry this workstream slice",
@@ -2421,12 +2471,14 @@ export function createAutoContinueEngine(deps) {
2421
2471
  decisions.length === 0 &&
2422
2472
  statusUpdateResult.applied === 0;
2423
2473
  if (!parsed || parsedStatus === "error" || completionHadNoOutcome) {
2474
+ const rawError = slice.lastError ?? (completionHadNoOutcome
2475
+ ? "Completed without verifiable outcomes or artifacts."
2476
+ : "Agent process exited without a valid output contract.");
2477
+ const humanized = humanizeSliceFailure(rawError);
2424
2478
  const attentionTitle = completionHadNoOutcome
2425
- ? `Autopilot slice needs verification: ${slice.workstreamTitle ?? slice.workstreamId}`
2426
- : `Autopilot slice failed: ${slice.workstreamTitle ?? slice.workstreamId}`;
2427
- const attentionSummary = completionHadNoOutcome
2428
- ? "The slice reported completion but did not produce artifacts or status updates. Decide whether to retry, request stronger output, or mark tasks manually."
2429
- : "The slice exited without a valid output contract. Review logs/output and decide whether to retry or pause autopilot.";
2479
+ ? `Agent finished but produced nothing: ${slice.workstreamTitle ?? slice.workstreamId}`
2480
+ : `${humanized.headline}: ${slice.workstreamTitle ?? slice.workstreamId}`;
2481
+ const attentionSummary = humanized.explanation;
2430
2482
  const decisionResult = await requestDecisionQueued({
2431
2483
  initiativeId: run.initiativeId,
2432
2484
  correlationId: slice.runId,
@@ -3463,6 +3515,9 @@ export function createAutoContinueEngine(deps) {
3463
3515
  catch {
3464
3516
  // best effort
3465
3517
  }
3518
+ // Session resume: check if a previous session exists for this workstream.
3519
+ const priorSession = sessionResumeEnabled() ? getWorkstreamSession(selectedWorkstreamId) : null;
3520
+ const resumedFromSessionId = priorSession?.sessionId ?? null;
3466
3521
  const spawned = spawnCodexSliceWorker({
3467
3522
  runId: sliceRunId,
3468
3523
  prompt,
@@ -3470,6 +3525,7 @@ export function createAutoContinueEngine(deps) {
3470
3525
  logPath,
3471
3526
  outputPath,
3472
3527
  outputSchemaPath: schemaPath,
3528
+ resumeSessionId: resumedFromSessionId,
3473
3529
  env: {
3474
3530
  ORGX_SOURCE_CLIENT: executorSourceClient,
3475
3531
  ORGX_RUN_ID: sliceRunId,
@@ -3523,6 +3579,8 @@ export function createAutoContinueEngine(deps) {
3523
3579
  scopeMilestoneIds: scopeMilestoneIds,
3524
3580
  lastError: null,
3525
3581
  isMockWorker: workerKind === "mock",
3582
+ cliSessionId: null,
3583
+ resumedFromSessionId,
3526
3584
  };
3527
3585
  autoContinueSliceRuns.set(sliceRunId, slice);
3528
3586
  try {
@@ -4094,6 +4152,7 @@ export function createAutoContinueEngine(deps) {
4094
4152
  run.activeTaskId = null;
4095
4153
  run.activeRunId = null;
4096
4154
  run.activeTaskTokenEstimate = null;
4155
+ clearWorkstreamSession(input.initiativeId);
4097
4156
  }
4098
4157
  syncLegacyRunPointers(run);
4099
4158
  autoContinueRuns.set(input.initiativeId, run);
@@ -4191,5 +4250,12 @@ export function createAutoContinueEngine(deps) {
4191
4250
  getAutoContinueLaneForWorkstream,
4192
4251
  scheduleAutoFixForWorkstream,
4193
4252
  startAutoContinueRun,
4253
+ // Session store (for resume support)
4254
+ workstreamSessionStore,
4255
+ getWorkstreamSession,
4256
+ setWorkstreamSession,
4257
+ clearWorkstreamSession,
4258
+ listWorkstreamSessions,
4259
+ sessionResumeEnabled,
4194
4260
  };
4195
4261
  }
@@ -21,6 +21,7 @@ export declare function createAutopilotRuntime(deps: CreateAutopilotRuntimeDeps)
21
21
  outputPath: string;
22
22
  outputSchemaPath?: string;
23
23
  env: Record<string, string | undefined>;
24
+ resumeSessionId?: string | null;
24
25
  }) => {
25
26
  pid: number | null;
26
27
  };
@@ -190,6 +190,12 @@ export function createAutopilotRuntime(deps) {
190
190
  return false;
191
191
  }
192
192
  }
193
+ function sessionResumeEnabled() {
194
+ const raw = (process.env.ORGX_AUTOPILOT_SESSION_RESUME ?? "").trim().toLowerCase();
195
+ if (!raw)
196
+ return false;
197
+ return !(raw === "0" || raw === "false" || raw === "no" || raw === "off");
198
+ }
193
199
  function spawnCodexSliceWorker(input) {
194
200
  ensurePrivateDirForFile(input.logPath);
195
201
  ensurePrivateDirForFile(input.outputPath);
@@ -335,8 +341,15 @@ export function createAutopilotRuntime(deps) {
335
341
  claudeExtraArgs.push("--print");
336
342
  if (!hasOutputFormat)
337
343
  claudeExtraArgs.push("--output-format", "json");
338
- if (!hasNoSessionPersistence)
344
+ // Session resume: when resumeSessionId is provided, use --resume and skip --no-session-persistence.
345
+ // When feature is enabled but no resume, omit --no-session-persistence to persist for future resume.
346
+ const resumeId = typeof input.resumeSessionId === "string" ? input.resumeSessionId.trim() : "";
347
+ if (resumeId) {
348
+ claudeExtraArgs.push("--resume", resumeId);
349
+ }
350
+ else if (!sessionResumeEnabled() && !hasNoSessionPersistence) {
339
351
  claudeExtraArgs.push("--no-session-persistence");
352
+ }
340
353
  if (!hasPermissionMode)
341
354
  claudeExtraArgs.push("--permission-mode", "bypassPermissions");
342
355
  if (!hasDangerousSkipPermissions && !hasAllowDangerousSkipPermissions) {
@@ -469,10 +482,25 @@ export function createAutopilotRuntime(deps) {
469
482
  }
470
483
  const codexInfo = deps.resolveCodexBinInfo();
471
484
  const codexBin = codexInfo.bin;
472
- const rawArgs = (process.env.ORGX_CODEX_ARGS ?? "").trim();
485
+ const codexResumeId = typeof input.resumeSessionId === "string" ? input.resumeSessionId.trim() : "";
486
+ // Session resume: use "resume <id>" subcommand instead of "exec --ephemeral"
487
+ // When feature enabled but no resume ID: omit --ephemeral to persist for future resume
488
+ let rawArgs;
489
+ let defaultArgs;
490
+ if (codexResumeId) {
491
+ rawArgs = "";
492
+ defaultArgs = ["resume", codexResumeId, "--full-auto", "--skip-git-repo-check"];
493
+ }
494
+ else {
495
+ rawArgs = (process.env.ORGX_CODEX_ARGS ?? "").trim();
496
+ const featureEnabled = sessionResumeEnabled();
497
+ defaultArgs = featureEnabled
498
+ ? ["exec", "--full-auto", "--skip-git-repo-check"]
499
+ : ["exec", "--ephemeral", "--full-auto", "--skip-git-repo-check"];
500
+ }
473
501
  const normalizedArgs = normalizeCodexArgs(rawArgs.length > 0
474
502
  ? rawArgs.split(/\s+/).filter(Boolean)
475
- : ["exec", "--ephemeral", "--full-auto", "--skip-git-repo-check"]);
503
+ : defaultArgs);
476
504
  const args = hasExplicitCodexSubcommand(normalizedArgs)
477
505
  ? normalizedArgs
478
506
  : ["exec", ...normalizedArgs];
@@ -2,6 +2,16 @@ export declare function ensureAutopilotSliceSchemaPath(schemaFilename: string):
2
2
  export declare function parseSliceResult<T extends object>(raw: string): T | null;
3
3
  export declare function readSliceOutputFile(pathname: string): string | null;
4
4
  export declare function readFileTailSafe(pathname: string, maxChars?: number): string;
5
+ /**
6
+ * Extract a CLI session ID from structured output JSON (Claude/Codex envelope).
7
+ * Looks for `session_id`, `sessionId`, or `conversation_id` fields.
8
+ */
9
+ export declare function extractSessionIdFromOutput(raw: string, _sourceClient?: string): string | null;
10
+ /**
11
+ * Extract a CLI session ID from worker log text.
12
+ * Matches patterns emitted by Claude Code and Codex CLIs.
13
+ */
14
+ export declare function extractSessionIdFromLog(logContent: string, _sourceClient?: string): string | null;
5
15
  export declare function fileUpdatedAtEpochMs(pathname: string, fallbackEpochMs: number): number;
6
16
  export type CodexBinInfo = {
7
17
  bin: string;
@@ -470,6 +470,64 @@ export function readFileTailSafe(pathname, maxChars = 64_000) {
470
470
  return "";
471
471
  }
472
472
  }
473
+ // ---------------------------------------------------------------------------
474
+ // Session ID extraction (for session resume support)
475
+ // ---------------------------------------------------------------------------
476
+ const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
477
+ /**
478
+ * Extract a CLI session ID from structured output JSON (Claude/Codex envelope).
479
+ * Looks for `session_id`, `sessionId`, or `conversation_id` fields.
480
+ */
481
+ export function extractSessionIdFromOutput(raw, _sourceClient) {
482
+ if (!raw || typeof raw !== "string")
483
+ return null;
484
+ const parsed = parseJsonSafe(raw.trim());
485
+ if (parsed && typeof parsed === "object") {
486
+ // Walk common envelope shapes: top-level, result, structured_output, final_output
487
+ const candidates = [
488
+ parsed,
489
+ parsed.result,
490
+ parsed.structured_output,
491
+ parsed.final_output,
492
+ ];
493
+ for (const obj of candidates) {
494
+ if (!obj || typeof obj !== "object")
495
+ continue;
496
+ const record = obj;
497
+ for (const key of ["session_id", "sessionId", "conversation_id"]) {
498
+ const value = record[key];
499
+ if (typeof value === "string" && UUID_RE.test(value))
500
+ return value.trim();
501
+ }
502
+ }
503
+ }
504
+ // Fallback: scan raw text for session_id: <uuid> pattern
505
+ const inline = raw.match(/session_id\s*[:=]\s*"?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"?/i);
506
+ if (inline && typeof inline[1] === "string")
507
+ return inline[1];
508
+ return null;
509
+ }
510
+ /**
511
+ * Extract a CLI session ID from worker log text.
512
+ * Matches patterns emitted by Claude Code and Codex CLIs.
513
+ */
514
+ export function extractSessionIdFromLog(logContent, _sourceClient) {
515
+ if (!logContent || typeof logContent !== "string")
516
+ return null;
517
+ // Claude Code: "Resume this session with: claude --resume <uuid>"
518
+ const claudeResume = logContent.match(/claude\s+--resume\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
519
+ if (claudeResume && typeof claudeResume[1] === "string")
520
+ return claudeResume[1];
521
+ // Codex: "codex resume <uuid>"
522
+ const codexResume = logContent.match(/codex\s+resume\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
523
+ if (codexResume && typeof codexResume[1] === "string")
524
+ return codexResume[1];
525
+ // Generic: "Session: <uuid>", "session_id: <uuid>", "saving session <uuid>"
526
+ const generic = logContent.match(/(?:Session|session_id|saving\s+session)\s*[:=]?\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i);
527
+ if (generic && typeof generic[1] === "string")
528
+ return generic[1];
529
+ return null;
530
+ }
473
531
  export function fileUpdatedAtEpochMs(pathname, fallbackEpochMs) {
474
532
  try {
475
533
  const st = statSync(pathname);
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Translates raw auto-continue engine error strings into human-readable
3
+ * decision context suitable for operator display.
4
+ *
5
+ * Used at decision creation time to enrich the `summary` field and
6
+ * generate structured options when the engine produces terse diagnostics.
7
+ */
8
+ export interface HumanizedSliceFailure {
9
+ /** One-line human headline, e.g. "Agent work stopped unexpectedly" */
10
+ headline: string;
11
+ /** Multi-sentence explanation for operators */
12
+ explanation: string;
13
+ /** Parsed structured data extracted from the raw string */
14
+ structuredDetails: {
15
+ exitCode?: string;
16
+ signal?: string;
17
+ elapsedMs?: string;
18
+ idleMs?: string;
19
+ server?: string;
20
+ reason?: string;
21
+ };
22
+ /** Severity for UI theming */
23
+ severity: "critical" | "warning" | "info";
24
+ }
25
+ /**
26
+ * Translate a raw auto-continue error/context string into a human-friendly
27
+ * decision description. Falls back gracefully for unrecognised patterns.
28
+ */
29
+ export declare function humanizeSliceFailure(raw: string): HumanizedSliceFailure;
30
+ /**
31
+ * Generate a human-friendly summary string from a raw error, suitable for
32
+ * the `summary` field when creating decisions. Combines the headline and
33
+ * explanation into a concise paragraph.
34
+ */
35
+ export declare function humanizeSliceFailureSummary(raw: string): string;
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Translates raw auto-continue engine error strings into human-readable
3
+ * decision context suitable for operator display.
4
+ *
5
+ * Used at decision creation time to enrich the `summary` field and
6
+ * generate structured options when the engine produces terse diagnostics.
7
+ */
8
+ const PATTERNS = [
9
+ {
10
+ test: /Worker exited without structured output.*?code=(\S+?),\s*signal=(\S+?)\)/i,
11
+ headline: "Agent work stopped unexpectedly",
12
+ explain: (_raw, m) => {
13
+ const code = m[1] === "null" ? "none" : m[1];
14
+ const signal = m[2] === "null" ? "none" : m[2];
15
+ const parts = [
16
+ "The agent process exited before producing results.",
17
+ ];
18
+ if (signal !== "none" && signal !== "null") {
19
+ parts.push(`It was terminated by signal ${signal}, which usually means the worker was stopped externally or ran out of resources.`);
20
+ }
21
+ else {
22
+ parts.push("This usually means the worker crashed or was terminated before completing.");
23
+ }
24
+ parts.push(`Exit code: ${code}. Signal: ${signal}.`);
25
+ return parts.join(" ");
26
+ },
27
+ severity: "critical",
28
+ extractDetails: (_raw, m) => ({
29
+ exitCode: m[1] === "null" ? undefined : m[1],
30
+ signal: m[2] === "null" ? undefined : m[2],
31
+ }),
32
+ },
33
+ {
34
+ test: /MCP handshake failed(?:\s+for\s+(\S+))?/i,
35
+ headline: "Agent couldn't connect to tools",
36
+ explain: (_raw, m) => {
37
+ const server = m[1] ? ` (${m[1]})` : "";
38
+ return `The agent failed to establish a connection with its tool server${server}. This typically means the MCP server is unreachable, misconfigured, or took too long to respond. Retrying often resolves transient connection issues.`;
39
+ },
40
+ severity: "critical",
41
+ extractDetails: (_raw, m) => ({
42
+ server: m[1] || undefined,
43
+ }),
44
+ },
45
+ {
46
+ test: /(?:Slice|Autopilot slice) timed out after (\d+)/i,
47
+ headline: "Agent ran out of time",
48
+ explain: (_raw, m) => {
49
+ const ms = parseInt(m[1], 10);
50
+ const mins = Math.round(ms / 60_000);
51
+ return `The agent exceeded its ${mins > 0 ? `${mins}-minute` : `${ms}ms`} time limit without completing. This can happen when the task is too large for a single work slice or when the agent is waiting on a slow external resource. Consider breaking the work into smaller steps or increasing the time budget.`;
52
+ },
53
+ severity: "warning",
54
+ extractDetails: (_raw, m) => ({
55
+ elapsedMs: m[1],
56
+ }),
57
+ },
58
+ {
59
+ test: /(?:Slice|Autopilot slice) stalled.*?(?:no progress for (\d+))?/i,
60
+ headline: "Agent stopped making progress",
61
+ explain: (_raw, m) => {
62
+ const mins = m[1] ? Math.round(parseInt(m[1], 10) / 60_000) : null;
63
+ const duration = mins ? ` for ${mins} minute${mins !== 1 ? "s" : ""}` : "";
64
+ return `The agent stopped producing output${duration}. This can indicate it's stuck in a loop, waiting on an unresponsive resource, or unable to proceed with the current approach. Reviewing the session logs may reveal the specific bottleneck.`;
65
+ },
66
+ severity: "warning",
67
+ extractDetails: (_raw, m) => ({
68
+ idleMs: m[1] || undefined,
69
+ }),
70
+ },
71
+ {
72
+ test: /blocked.*?(?:needs|requires?) intervention/i,
73
+ headline: "Agent needs your help to continue",
74
+ explain: () => "The agent reached a point where it cannot proceed without human guidance. This may be a question about requirements, a permission needed, or an ambiguity that needs resolution.",
75
+ severity: "warning",
76
+ },
77
+ {
78
+ test: /blocked.*?without.*?(?:blocking )?decision payload/i,
79
+ headline: "Agent paused without clear reason",
80
+ explain: () => "The agent reported it was blocked but did not specify what decision is needed. Review the session logs for context about what the agent was working on when it paused.",
81
+ severity: "warning",
82
+ },
83
+ {
84
+ test: /code=(\d+)/i,
85
+ headline: "Agent encountered an error",
86
+ explain: (_raw, m) => `The agent process exited with error code ${m[1]}. This indicates the agent hit an unexpected condition during execution. Reviewing the session logs will show what operation failed.`,
87
+ severity: "critical",
88
+ extractDetails: (_raw, m) => ({
89
+ exitCode: m[1],
90
+ }),
91
+ },
92
+ {
93
+ test: /completed.*?(?:without|no) (?:verifiable )?(?:outcomes?|artifacts?|status updates?)/i,
94
+ headline: "Agent finished but produced nothing",
95
+ explain: () => "The agent reported completion but did not produce any artifacts, status updates, or measurable outcomes. This may mean the work was done informally (e.g., analysis without a written output) or the agent couldn't find actionable work.",
96
+ severity: "info",
97
+ },
98
+ {
99
+ test: /Spawn guard denied/i,
100
+ headline: "Agent dispatch was blocked by safety check",
101
+ explain: (_raw) => "The system's safety checks prevented this agent from starting. This is typically due to capacity limits, domain restrictions, or quality gate requirements that haven't been met.",
102
+ severity: "warning",
103
+ },
104
+ ];
105
+ /**
106
+ * Translate a raw auto-continue error/context string into a human-friendly
107
+ * decision description. Falls back gracefully for unrecognised patterns.
108
+ */
109
+ export function humanizeSliceFailure(raw) {
110
+ for (const pattern of PATTERNS) {
111
+ const match = raw.match(pattern.test);
112
+ if (match) {
113
+ return {
114
+ headline: pattern.headline,
115
+ explanation: pattern.explain(raw, match),
116
+ structuredDetails: pattern.extractDetails?.(raw, match) ?? {},
117
+ severity: pattern.severity,
118
+ };
119
+ }
120
+ }
121
+ // Fallback for unrecognised patterns
122
+ return {
123
+ headline: "Agent work needs attention",
124
+ explanation: raw,
125
+ structuredDetails: {},
126
+ severity: "warning",
127
+ };
128
+ }
129
+ /**
130
+ * Generate a human-friendly summary string from a raw error, suitable for
131
+ * the `summary` field when creating decisions. Combines the headline and
132
+ * explanation into a concise paragraph.
133
+ */
134
+ export function humanizeSliceFailureSummary(raw) {
135
+ const h = humanizeSliceFailure(raw);
136
+ return `${h.headline}. ${h.explanation}`;
137
+ }
@@ -71,6 +71,7 @@ export interface MissionControlExecutionPolicy {
71
71
  export declare const SLICE_SCOPE_MAX_TASKS: Record<SliceScope, number>;
72
72
  export declare const SLICE_SCOPE_TIMEOUT_MULTIPLIER: Record<SliceScope, number>;
73
73
  export declare const ORGX_SKILL_BY_DOMAIN: Record<string, string>;
74
+ export declare function safeErrorMessage(err: unknown): string;
74
75
  interface BudgetEnvBounds {
75
76
  min?: number;
76
77
  max?: number;
@@ -1,4 +1,5 @@
1
1
  import { pickNumber, pickString, toIsoString } from "./value-utils.js";
2
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2
3
  export const SLICE_SCOPE_MAX_TASKS = {
3
4
  task: 6,
4
5
  milestone: 15,
@@ -18,11 +19,74 @@ export const ORGX_SKILL_BY_DOMAIN = {
18
19
  design: "orgx-design-agent",
19
20
  orchestration: "orgx-orchestrator-agent",
20
21
  };
21
- function safeErrorMessage(err) {
22
- if (err instanceof Error)
23
- return err.message;
24
- if (typeof err === "string")
25
- return err;
22
+ export function safeErrorMessage(err) {
23
+ const raw = err instanceof Error
24
+ ? err.message
25
+ : typeof err === "string"
26
+ ? err
27
+ : err && typeof err === "object" && "message" in err && typeof err.message === "string"
28
+ ? (err.message ?? "")
29
+ : "";
30
+ const parseStructuredMessage = (value) => {
31
+ const trimmed = value.trim();
32
+ if (!trimmed)
33
+ return null;
34
+ const parseObjectMessage = (parsed) => {
35
+ if (!parsed || typeof parsed !== "object")
36
+ return null;
37
+ const root = parsed;
38
+ const nested = root.error && typeof root.error === "object" ? root.error : null;
39
+ const envelope = nested ?? root;
40
+ return ((typeof envelope.message === "string" && envelope.message.trim()) ||
41
+ (typeof envelope.detail === "string" && envelope.detail.trim()) ||
42
+ (!nested && typeof root.error === "string" && root.error.trim()) ||
43
+ null);
44
+ };
45
+ try {
46
+ const parsed = JSON.parse(trimmed);
47
+ const direct = parseObjectMessage(parsed);
48
+ if (direct)
49
+ return direct;
50
+ }
51
+ catch {
52
+ // fall through and attempt extraction of embedded JSON blocks
53
+ }
54
+ const firstBrace = trimmed.indexOf("{");
55
+ const lastBrace = trimmed.lastIndexOf("}");
56
+ if (firstBrace >= 0 && lastBrace > firstBrace) {
57
+ const candidate = trimmed.slice(firstBrace, lastBrace + 1);
58
+ try {
59
+ const parsed = JSON.parse(candidate);
60
+ return parseObjectMessage(parsed);
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ return null;
67
+ };
68
+ const stripStructuredNoise = (value) => value
69
+ .replace(/"requestId"\s*:\s*"[^"]*"/gi, "")
70
+ .replace(/"timestamp"\s*:\s*"[^"]*"/gi, "")
71
+ .replace(/"docsUrl"\s*:\s*"[^"]*"/gi, "")
72
+ .replace(/\brequest[_\s-]?id[:=]\s*[\w-]+/gi, "")
73
+ .replace(/\btimestamp[:=]\s*\S+/gi, "")
74
+ .replace(/\bdocsUrl[:=]\s*\S+/gi, "")
75
+ .replace(/[{}]/g, " ")
76
+ .replace(/\s{2,}/g, " ")
77
+ .trim();
78
+ const normalizedMessage = parseStructuredMessage(raw) ?? raw;
79
+ const sanitized = stripStructuredNoise(normalizedMessage);
80
+ const normalized = sanitized.toLowerCase();
81
+ if (normalized.length > 0) {
82
+ if (normalized.includes("internal_error") || normalized.includes("internal server error")) {
83
+ return "temporary server issue";
84
+ }
85
+ if (normalized.includes("failed to list decision") || normalized.includes("failed to load decision")) {
86
+ return "decision data temporarily unavailable";
87
+ }
88
+ return sanitized;
89
+ }
26
90
  return "Unexpected error";
27
91
  }
28
92
  function toNullableBoolean(value) {
@@ -641,6 +705,9 @@ export async function listEntitiesSafe(client, type, filters) {
641
705
  }
642
706
  }
643
707
  export async function buildMissionControlGraph(client, initiativeId, options) {
708
+ if (!UUID_RE.test(initiativeId)) {
709
+ throw new Error(`buildMissionControlGraph: initiativeId must be a UUID, got "${initiativeId}"`);
710
+ }
644
711
  const degraded = [];
645
712
  const preloadedInitiative = options?.initiativeEntity ?? null;
646
713
  const [initiativeResult, workstreamResult, milestoneResult, taskResult] = await Promise.all([