oh-my-opencode 4.5.12 → 4.6.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 (147) hide show
  1. package/.agents/skills/opencode-qa/SKILL.md +194 -0
  2. package/.agents/skills/opencode-qa/references/cli-commands.md +188 -0
  3. package/.agents/skills/opencode-qa/references/db-investigation.md +197 -0
  4. package/.agents/skills/opencode-qa/references/events-hooks.md +110 -0
  5. package/.agents/skills/opencode-qa/references/sdk.md +96 -0
  6. package/.agents/skills/opencode-qa/references/server-api.md +200 -0
  7. package/.agents/skills/opencode-qa/references/testing-harness.md +218 -0
  8. package/.agents/skills/opencode-qa/references/tui-tmux.md +52 -0
  9. package/.agents/skills/opencode-qa/scripts/db-session-by-id.sh +53 -0
  10. package/.agents/skills/opencode-qa/scripts/db-session-by-name.sh +57 -0
  11. package/.agents/skills/opencode-qa/scripts/db-session-by-text.sh +158 -0
  12. package/.agents/skills/opencode-qa/scripts/export-roundtrip.sh +57 -0
  13. package/.agents/skills/opencode-qa/scripts/lib/common.sh +216 -0
  14. package/.agents/skills/opencode-qa/scripts/server-smoke.sh +64 -0
  15. package/.agents/skills/opencode-qa/scripts/sse-hook-probe.sh +106 -0
  16. package/.agents/skills/opencode-qa/scripts/tui-smoke.sh +89 -0
  17. package/README.ja.md +13 -3
  18. package/README.ko.md +13 -3
  19. package/README.md +24 -14
  20. package/README.ru.md +13 -3
  21. package/README.zh-cn.md +13 -3
  22. package/bin/oh-my-opencode.js +4 -3
  23. package/bin/oh-my-opencode.test.ts +35 -7
  24. package/bin/platform.d.ts +1 -1
  25. package/bin/platform.js +4 -4
  26. package/bin/platform.test.ts +31 -9
  27. package/dist/cli/cleanup-command.d.ts +4 -0
  28. package/dist/cli/cleanup.d.ts +11 -0
  29. package/dist/cli/cli-program.d.ts +2 -1
  30. package/dist/cli/index.js +1837 -450
  31. package/dist/cli/install-codex/codex-cache.d.ts +1 -0
  32. package/dist/cli/install-codex/codex-cleanup-config.d.ts +6 -0
  33. package/dist/cli/install-codex/codex-cleanup.d.ts +21 -0
  34. package/dist/cli/install-codex/codex-config-mcp.d.ts +1 -0
  35. package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -0
  36. package/dist/cli/install-codex/codex-config-reasoning.d.ts +1 -0
  37. package/dist/cli/install-codex/codex-config-toml.d.ts +2 -1
  38. package/dist/cli/install-codex/codex-installation-detection.d.ts +36 -0
  39. package/dist/cli/install-codex/codex-package-layout.d.ts +1 -0
  40. package/dist/cli/install-codex/codex-project-local-cleanup-best-effort.d.ts +7 -0
  41. package/dist/cli/install-codex/codex-project-local-cleanup.d.ts +35 -0
  42. package/dist/cli/install-codex/git-bash.d.ts +35 -0
  43. package/dist/cli/install-codex/index.d.ts +4 -0
  44. package/dist/cli/install-codex/toml-section-editor.d.ts +2 -0
  45. package/dist/cli/install-codex/types.d.ts +20 -0
  46. package/dist/cli/run/event-state.d.ts +1 -0
  47. package/dist/cli/run/poll-for-completion.d.ts +1 -0
  48. package/dist/cli/run/prompt-start.d.ts +7 -0
  49. package/dist/cli/star-request.d.ts +9 -0
  50. package/dist/config/schema/hooks.d.ts +0 -1
  51. package/dist/create-hooks.d.ts +0 -1
  52. package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
  53. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  54. package/dist/hooks/index.d.ts +0 -1
  55. package/dist/index.js +267 -114
  56. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
  57. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
  58. package/dist/plugin/messages-transform.d.ts +8 -1
  59. package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +6 -0
  60. package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +14 -0
  61. package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +7 -0
  62. package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +1 -0
  63. package/dist/shared/prompt-async-gate/timing.d.ts +1 -0
  64. package/dist/shared/prompt-async-gate/types.d.ts +2 -0
  65. package/dist/shared/prompt-async-gate.d.ts +1 -1
  66. package/package.json +22 -17
  67. package/packages/git-bash-mcp/dist/cli.js +367 -0
  68. package/packages/omo-codex/plugin/.mcp.json +11 -0
  69. package/packages/omo-codex/plugin/components/comment-checker/README.md +1 -1
  70. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +29 -0
  71. package/packages/omo-codex/plugin/components/git-bash/package.json +23 -0
  72. package/packages/omo-codex/plugin/components/git-bash/src/cli.ts +33 -0
  73. package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +180 -0
  74. package/packages/omo-codex/plugin/components/git-bash/src/index.ts +10 -0
  75. package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +195 -0
  76. package/packages/omo-codex/plugin/components/git-bash/tsconfig.build.json +13 -0
  77. package/packages/omo-codex/plugin/components/git-bash/tsconfig.json +25 -0
  78. package/packages/omo-codex/plugin/components/lsp/README.md +1 -1
  79. package/packages/omo-codex/plugin/components/lsp/src/cli.ts +5 -5
  80. package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +33 -0
  81. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +19 -27
  82. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +28 -0
  83. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-errors.test.ts +55 -0
  84. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +7 -5
  85. package/packages/omo-codex/plugin/components/rules/README.md +1 -1
  86. package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
  87. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +3 -1
  88. package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +97 -0
  89. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +5 -4
  90. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
  91. package/packages/omo-codex/plugin/components/ultrawork/README.md +2 -2
  92. package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +1 -0
  93. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
  94. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +2 -1
  95. package/packages/omo-codex/plugin/components/ultrawork/directive.md +31 -5
  96. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +27 -4
  97. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +25 -0
  98. package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -1
  99. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +27 -205
  100. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +230 -0
  101. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
  102. package/packages/omo-codex/plugin/hooks/hooks.json +24 -2
  103. package/packages/omo-codex/plugin/package-lock.json +19 -0
  104. package/packages/omo-codex/plugin/package.json +3 -1
  105. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -1
  106. package/packages/omo-codex/plugin/scripts/build-components.mjs +2 -1
  107. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +87 -0
  108. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +27 -2
  109. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +20 -0
  110. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +27 -205
  111. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +230 -0
  112. package/packages/omo-codex/plugin/test/aggregate.test.mjs +23 -8
  113. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +56 -11
  114. package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +34 -0
  115. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +21 -0
  116. package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +48 -0
  117. package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +76 -0
  118. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +66 -0
  119. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +32 -2
  120. package/packages/omo-codex/scripts/install/cache.mjs +5 -3
  121. package/packages/omo-codex/scripts/install/cli-args.mjs +112 -0
  122. package/packages/omo-codex/scripts/install/config.mjs +36 -1
  123. package/packages/omo-codex/scripts/install/delegated-command.mjs +25 -0
  124. package/packages/omo-codex/scripts/install/git-bash.mjs +99 -0
  125. package/packages/omo-codex/scripts/install/git-bash.test.mjs +174 -0
  126. package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
  127. package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +7 -1
  128. package/packages/omo-codex/scripts/install/permissions.d.mts +1 -0
  129. package/packages/omo-codex/scripts/install/permissions.mjs +26 -0
  130. package/packages/omo-codex/scripts/install/project-local-cleanup.mjs +229 -0
  131. package/packages/omo-codex/scripts/install/reasoning-config.mjs +14 -0
  132. package/packages/omo-codex/scripts/install/source-package-build.mjs +20 -0
  133. package/packages/omo-codex/scripts/install/toml-editor.mjs +19 -2
  134. package/packages/omo-codex/scripts/install-cli-args.test.mjs +146 -0
  135. package/packages/omo-codex/scripts/install-config-autonomous.test.mjs +48 -0
  136. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +62 -0
  137. package/packages/omo-codex/scripts/install-config.test.mjs +206 -0
  138. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +129 -0
  139. package/packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs +145 -0
  140. package/packages/omo-codex/scripts/install-local.mjs +91 -8
  141. package/packages/omo-codex/scripts/install-local.test.mjs +15 -0
  142. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +60 -0
  143. package/packages/omo-codex/scripts/install-packaged-local.test.mjs +67 -0
  144. package/packages/omo-codex/scripts/install-project-local-cleanup.test.mjs +277 -0
  145. package/packages/shared-skills/skills/review-work/SKILL.md +27 -2
  146. package/packages/shared-skills/skills/start-work/SKILL.md +20 -0
  147. package/dist/hooks/context-window-monitor.d.ts +0 -19
package/dist/index.js CHANGED
@@ -54030,7 +54030,7 @@ function buildTmuxAttachCommand(serverUrl, sessionId, directory = process.cwd())
54030
54030
  }
54031
54031
  function buildTmuxPlaceholderCommand(description) {
54032
54032
  const escapedDescription = shellEscapeForDoubleQuotedCommand(description);
54033
- return `${TMUX_COMMAND_SHELL} -c "printf '%s\\n%s\\n' "OMO subagent pane ready: ${escapedDescription}" "Focus this pane to attach."; exec tail -f /dev/null"`;
54033
+ return `${TMUX_COMMAND_SHELL} -c "printf '%s\\n%s\\n' "OMO subagent pane ready: ${escapedDescription}" "Focus this pane to attach."; while :; do sleep 86400; done"`;
54034
54034
  }
54035
54035
  var TMUX_COMMAND_SHELL = "/bin/sh";
54036
54036
  var init_pane_command = () => {};
@@ -55033,7 +55033,7 @@ async function withDispatchTimeout(operation, dispatchTimeoutMs, operationName)
55033
55033
  }
55034
55034
  }
55035
55035
  }
55036
- var DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS = 2000, DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS = 30000, DEFAULT_PROMPT_GATE_MESSAGES_FETCH_TIMEOUT_MS = 5000, DEFAULT_PROMPT_QUEUE_RETRY_MS = 250, promptGateMessagesFetchTimeoutMsForTesting;
55036
+ var DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS = 2000, DEFAULT_PROMPT_SEMANTIC_DEDUPE_HOLD_MS = 15000, DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS = 30000, DEFAULT_PROMPT_GATE_MESSAGES_FETCH_TIMEOUT_MS = 5000, DEFAULT_PROMPT_QUEUE_RETRY_MS = 250, promptGateMessagesFetchTimeoutMsForTesting;
55037
55037
 
55038
55038
  // src/shared/prompt-async-gate/pending-tool-turn.ts
55039
55039
  function getPromptQuery(input) {
@@ -55146,6 +55146,45 @@ var init_pending_tool_turn = __esm(() => {
55146
55146
  init_message_inspection_error();
55147
55147
  });
55148
55148
 
55149
+ // src/shared/prompt-async-gate/recent-dispatches.ts
55150
+ function recentDispatchKey(sessionID, dedupeKey) {
55151
+ return `${sessionID}\x00${dedupeKey}`;
55152
+ }
55153
+ function pruneRecentPromptDispatches(now = Date.now()) {
55154
+ for (const [key, dispatch] of recentPromptDispatches) {
55155
+ if (dispatch.expiresAt <= now) {
55156
+ recentPromptDispatches.delete(key);
55157
+ }
55158
+ }
55159
+ }
55160
+ function getRecentPromptDispatch(sessionID, dedupeKey) {
55161
+ pruneRecentPromptDispatches();
55162
+ return recentPromptDispatches.get(recentDispatchKey(sessionID, dedupeKey));
55163
+ }
55164
+ function rememberRecentPromptDispatch(args) {
55165
+ pruneRecentPromptDispatches();
55166
+ if (args.holdMs <= 0) {
55167
+ return;
55168
+ }
55169
+ recentPromptDispatches.set(recentDispatchKey(args.sessionID, args.dedupeKey), {
55170
+ source: args.source,
55171
+ expiresAt: Date.now() + args.holdMs
55172
+ });
55173
+ log("[prompt-async-gate] remembered semantic prompt dispatch", {
55174
+ sessionID: args.sessionID,
55175
+ source: args.source,
55176
+ holdMs: args.holdMs
55177
+ });
55178
+ }
55179
+ function deleteRecentPromptDispatch(sessionID, dedupeKey) {
55180
+ recentPromptDispatches.delete(recentDispatchKey(sessionID, dedupeKey));
55181
+ }
55182
+ var recentPromptDispatches;
55183
+ var init_recent_dispatches = __esm(() => {
55184
+ init_logger();
55185
+ recentPromptDispatches = new Map;
55186
+ });
55187
+
55149
55188
  // src/shared/prompt-async-gate/reservations.ts
55150
55189
  function setExpiredReservationHandler(handler) {
55151
55190
  expiredReservationHandler = handler;
@@ -55227,6 +55266,7 @@ async function dispatchAfterSessionIdle(args) {
55227
55266
  dedupeKey,
55228
55267
  settleMs,
55229
55268
  postDispatchHoldMs,
55269
+ semanticDedupeHoldMs,
55230
55270
  dispatchTimeoutMs,
55231
55271
  checkStatus,
55232
55272
  checkToolState,
@@ -55284,10 +55324,25 @@ async function dispatchAfterSessionIdle(args) {
55284
55324
  log(`[prompt-async-gate] ${sessionName} dispatching`, { sessionID, source });
55285
55325
  dispatchAttempted = true;
55286
55326
  const response = await withDispatchTimeout(dispatch(input), dispatchTimeoutMs, `[prompt-async-gate] ${sessionName} dispatch`);
55327
+ rememberRecentPromptDispatch({
55328
+ sessionID,
55329
+ dedupeKey,
55330
+ source,
55331
+ holdMs: semanticDedupeHoldMs
55332
+ });
55287
55333
  log(`[prompt-async-gate] ${sessionName} dispatched`, { sessionID, source });
55288
55334
  return { status: "dispatched", response };
55289
55335
  } catch (error) {
55290
- log(`[prompt-async-gate] ${sessionName} failed`, { sessionID, source, error: String(error) });
55336
+ if (dispatchAttempted) {
55337
+ rememberRecentPromptDispatch({
55338
+ sessionID,
55339
+ dedupeKey,
55340
+ source,
55341
+ holdMs: semanticDedupeHoldMs
55342
+ });
55343
+ }
55344
+ const errorText = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
55345
+ log(`[prompt-async-gate] ${sessionName} failed`, { sessionID, source, error: errorText });
55291
55346
  return { status: "failed", error, dispatchAttempted };
55292
55347
  } finally {
55293
55348
  finishPromptReservation(sessionID, reservation, dispatchAttempted, postDispatchHoldMs);
@@ -55297,6 +55352,7 @@ var init_session_idle_dispatch = __esm(() => {
55297
55352
  init_logger();
55298
55353
  init_session_idle_settle();
55299
55354
  init_pending_tool_turn();
55355
+ init_recent_dispatches();
55300
55356
  init_reservations();
55301
55357
  });
55302
55358
 
@@ -55404,6 +55460,7 @@ async function drainPromptQueue(sessionID, awaitedEntry) {
55404
55460
  dedupeKey: entry.dedupeKey,
55405
55461
  settleMs: entry.settleMs,
55406
55462
  postDispatchHoldMs: entry.postDispatchHoldMs,
55463
+ semanticDedupeHoldMs: entry.semanticDedupeHoldMs,
55407
55464
  dispatchTimeoutMs: entry.dispatchTimeoutMs,
55408
55465
  checkStatus: entry.checkStatus,
55409
55466
  checkToolState: entry.checkToolState,
@@ -55487,27 +55544,119 @@ var init_queue = __esm(() => {
55487
55544
  });
55488
55545
  });
55489
55546
 
55490
- // src/shared/prompt-async-gate.ts
55547
+ // src/shared/prompt-async-gate/semantic-dedupe.ts
55548
+ import { createHash } from "crypto";
55549
+ function isPlainRecord(value) {
55550
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
55551
+ return false;
55552
+ }
55553
+ const prototype = Object.getPrototypeOf(value);
55554
+ return prototype === Object.prototype || prototype === null;
55555
+ }
55556
+ function canonicalizePromptInputForDedupe(key, value, seen = new WeakSet, depth = 0) {
55557
+ if (key === "signal") {
55558
+ return "[AbortSignal]";
55559
+ }
55560
+ if (typeof value === "function") {
55561
+ return `[Function:${value.name}]`;
55562
+ }
55563
+ if (depth > MAX_PROMPT_DEDUPE_DEPTH) {
55564
+ return "[MaxDepth]";
55565
+ }
55566
+ if (Array.isArray(value)) {
55567
+ if (seen.has(value)) {
55568
+ return "[Circular]";
55569
+ }
55570
+ seen.add(value);
55571
+ try {
55572
+ return value.map((entry) => canonicalizePromptInputForDedupe("", entry, seen, depth + 1));
55573
+ } finally {
55574
+ seen.delete(value);
55575
+ }
55576
+ }
55577
+ if (!isPlainRecord(value)) {
55578
+ return value;
55579
+ }
55580
+ if (seen.has(value)) {
55581
+ return "[Circular]";
55582
+ }
55583
+ seen.add(value);
55584
+ try {
55585
+ const canonicalEntries = [];
55586
+ for (const entryKey of Object.keys(value).sort()) {
55587
+ canonicalEntries.push([
55588
+ entryKey,
55589
+ canonicalizePromptInputForDedupe(entryKey, value[entryKey], seen, depth + 1)
55590
+ ]);
55591
+ }
55592
+ return Object.fromEntries(canonicalEntries);
55593
+ } finally {
55594
+ seen.delete(value);
55595
+ }
55596
+ }
55597
+ function isContinuationTextPartLike(value) {
55598
+ if (!isPlainRecord(value)) {
55599
+ return false;
55600
+ }
55601
+ if (value.type !== "text" || typeof value.text !== "string") {
55602
+ return false;
55603
+ }
55604
+ if (!hasInternalInitiatorMarker(value.text)) {
55605
+ return false;
55606
+ }
55607
+ if (!isPlainRecord(value.metadata)) {
55608
+ return false;
55609
+ }
55610
+ return value.metadata.compaction_continue === true;
55611
+ }
55612
+ function hasContinuationPromptIntent(input) {
55613
+ if (!isPlainRecord(input) || !isPlainRecord(input.body) || !Array.isArray(input.body.parts)) {
55614
+ return false;
55615
+ }
55616
+ return input.body.parts.some((part) => isContinuationTextPartLike(part));
55617
+ }
55618
+ function normalizePromptInputForSemanticDedupe(input) {
55619
+ if (hasContinuationPromptIntent(input)) {
55620
+ return {
55621
+ __omo_internal_intent: "continuation"
55622
+ };
55623
+ }
55624
+ return canonicalizePromptInputForDedupe("", input);
55625
+ }
55491
55626
  function stringifyPromptInputForDedupe(input) {
55492
55627
  try {
55493
- const serialized = JSON.stringify(input, (key, value) => {
55494
- if (key === "signal") {
55495
- return "[AbortSignal]";
55496
- }
55497
- if (typeof value === "function") {
55498
- return `[Function:${value.name}]`;
55499
- }
55500
- return value;
55501
- });
55628
+ const serialized = JSON.stringify(normalizePromptInputForSemanticDedupe(input));
55502
55629
  return serialized ?? String(input);
55503
- } catch {
55504
- return String(input);
55630
+ } catch (error) {
55631
+ const errorTag = error instanceof Error ? error.name : String(error);
55632
+ return `${String(input)}:[unserializable:${errorTag}]`;
55505
55633
  }
55506
55634
  }
55507
- function createDefaultDedupeKey(source, input) {
55635
+ function createSemanticPromptDedupeKey(input) {
55508
55636
  const fingerprint = stringifyPromptInputForDedupe(input);
55509
- return `${source}:${fingerprint.length}:${fingerprint.slice(0, 8192)}`;
55637
+ const digest = createHash("sha256").update(fingerprint, "utf8").digest("hex");
55638
+ return `semantic:${digest}`;
55510
55639
  }
55640
+ function coalesceRecentSemanticPromptDispatch(args) {
55641
+ const recentDispatch = getRecentPromptDispatch(args.sessionID, args.dedupeKey);
55642
+ if (!recentDispatch) {
55643
+ return;
55644
+ }
55645
+ log("[prompt-async-gate] prompt coalesced with recent semantic dispatch", {
55646
+ sessionID: args.sessionID,
55647
+ source: args.source,
55648
+ queuedBy: recentDispatch.source
55649
+ });
55650
+ return { status: "queued", queuedBy: recentDispatch.source, position: 0 };
55651
+ }
55652
+ var MAX_PROMPT_DEDUPE_DEPTH = 64;
55653
+ var init_semantic_dedupe = __esm(() => {
55654
+ init_internal_initiator_marker();
55655
+ init_logger();
55656
+ init_recent_dispatches();
55657
+ });
55658
+
55659
+ // src/shared/prompt-async-gate.ts
55511
55660
  function hasObjectSessionPath(input) {
55512
55661
  return typeof input === "object" && input !== null && "path" in input && typeof input.path === "object" && input.path !== null && "id" in input.path && typeof input.path.id === "string";
55513
55662
  }
@@ -55537,9 +55686,10 @@ async function dispatchInternalPrompt(args) {
55537
55686
  source,
55538
55687
  settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS
55539
55688
  } = args;
55540
- const dedupeKey = args.dedupeKey ?? createDefaultDedupeKey(source, input);
55689
+ const dedupeKey = args.dedupeKey ?? createSemanticPromptDedupeKey(input);
55541
55690
  const queueRetryMs = args.queueRetryMs ?? DEFAULT_PROMPT_QUEUE_RETRY_MS;
55542
55691
  const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS;
55692
+ const semanticDedupeHoldMs = args.semanticDedupeHoldMs ?? (postDispatchHoldMs > 0 ? DEFAULT_PROMPT_SEMANTIC_DEDUPE_HOLD_MS : 0);
55543
55693
  const dispatchTimeoutMs = args.dispatchTimeoutMs ?? DEFAULT_PROMPT_DISPATCH_TIMEOUT_MS;
55544
55694
  const sessionName = args.mode === "async" ? "promptAsync" : "prompt";
55545
55695
  const dispatch = (() => {
@@ -55572,6 +55722,10 @@ async function dispatchInternalPrompt(args) {
55572
55722
  if (queuedBy !== undefined || isPromptQueueDraining(sessionID)) {
55573
55723
  return { status: "reserved", reservedBy: queuedBy ?? source };
55574
55724
  }
55725
+ const recentDispatchResult2 = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
55726
+ if (recentDispatchResult2) {
55727
+ return recentDispatchResult2;
55728
+ }
55575
55729
  return dispatchAfterSessionIdle({
55576
55730
  sessionName,
55577
55731
  client,
@@ -55581,6 +55735,7 @@ async function dispatchInternalPrompt(args) {
55581
55735
  dedupeKey,
55582
55736
  settleMs,
55583
55737
  postDispatchHoldMs,
55738
+ semanticDedupeHoldMs,
55584
55739
  dispatchTimeoutMs,
55585
55740
  checkStatus: args.checkStatus !== false,
55586
55741
  checkToolState: args.checkToolState !== false,
@@ -55588,6 +55743,10 @@ async function dispatchInternalPrompt(args) {
55588
55743
  });
55589
55744
  }
55590
55745
  if (args.queue !== false) {
55746
+ const recentDispatchResult2 = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
55747
+ if (recentDispatchResult2) {
55748
+ return recentDispatchResult2;
55749
+ }
55591
55750
  return enqueueInternalPrompt({
55592
55751
  id: nextPromptQueueID(),
55593
55752
  sessionID,
@@ -55598,6 +55757,7 @@ async function dispatchInternalPrompt(args) {
55598
55757
  dedupeKey,
55599
55758
  settleMs,
55600
55759
  postDispatchHoldMs,
55760
+ semanticDedupeHoldMs,
55601
55761
  dispatchTimeoutMs,
55602
55762
  queueRetryMs,
55603
55763
  checkStatus: args.checkStatus !== false,
@@ -55605,6 +55765,10 @@ async function dispatchInternalPrompt(args) {
55605
55765
  dispatch: async (_dispatchInput) => dispatchWithPathCompatibility(dispatch, input)
55606
55766
  });
55607
55767
  }
55768
+ const recentDispatchResult = coalesceRecentSemanticPromptDispatch({ sessionID, dedupeKey, source });
55769
+ if (recentDispatchResult) {
55770
+ return recentDispatchResult;
55771
+ }
55608
55772
  return dispatchAfterSessionIdle({
55609
55773
  sessionName,
55610
55774
  client,
@@ -55614,6 +55778,7 @@ async function dispatchInternalPrompt(args) {
55614
55778
  dedupeKey,
55615
55779
  settleMs,
55616
55780
  postDispatchHoldMs,
55781
+ semanticDedupeHoldMs,
55617
55782
  dispatchTimeoutMs,
55618
55783
  checkStatus: args.checkStatus !== false,
55619
55784
  checkToolState: args.checkToolState !== false,
@@ -55638,6 +55803,7 @@ function releasePromptAsyncReservation(sessionID, source, options) {
55638
55803
  return false;
55639
55804
  }
55640
55805
  deletePromptReservation(sessionID);
55806
+ deleteRecentPromptDispatch(sessionID, existing.dedupeKey);
55641
55807
  releaseInFlightPromptMatchingDedupe(sessionID, existing.dedupeKey);
55642
55808
  schedulePromptQueueDrain(sessionID, 0);
55643
55809
  log("[prompt-async-gate] promptAsync reservation released", {
@@ -55651,8 +55817,10 @@ var init_prompt_async_gate = __esm(() => {
55651
55817
  init_logger();
55652
55818
  init_session_idle_settle();
55653
55819
  init_queue();
55820
+ init_recent_dispatches();
55654
55821
  init_reservations();
55655
55822
  init_session_idle_dispatch();
55823
+ init_semantic_dedupe();
55656
55824
  });
55657
55825
 
55658
55826
  // src/shared/prompt-failure-classifier.ts
@@ -69458,80 +69626,6 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
69458
69626
  dispose: () => sessionStateStore.shutdown()
69459
69627
  };
69460
69628
  }
69461
- // src/hooks/context-window-monitor.ts
69462
- init_context_limit_resolver2();
69463
- init_compaction_marker();
69464
- init_event_session_id();
69465
- init_system_directive();
69466
- var CONTEXT_WARNING_THRESHOLD = 0.7;
69467
- function createContextReminder(actualLimit) {
69468
- const limitTokens = actualLimit.toLocaleString();
69469
- return `${createSystemDirective(SystemDirectiveTypes.CONTEXT_WINDOW_MONITOR)}
69470
-
69471
- You are using a ${limitTokens}-token context window.
69472
- You still have context remaining - do NOT rush or skip tasks.
69473
- Complete your work thoroughly and methodically.`;
69474
- }
69475
- function createContextWindowMonitorHook(_ctx, modelCacheState) {
69476
- const remindedSessions = new Set;
69477
- const tokenCache = new Map;
69478
- const toolExecuteAfter = async (input, output) => {
69479
- const { sessionID } = input;
69480
- if (remindedSessions.has(sessionID))
69481
- return;
69482
- const cached2 = tokenCache.get(sessionID);
69483
- if (!cached2)
69484
- return;
69485
- const actualLimit = resolveActualContextLimit(cached2.providerID, cached2.modelID, modelCacheState);
69486
- if (!actualLimit)
69487
- return;
69488
- const lastTokens = cached2.tokens;
69489
- const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0);
69490
- const actualUsagePercentage = totalInputTokens / actualLimit;
69491
- if (actualUsagePercentage < CONTEXT_WARNING_THRESHOLD)
69492
- return;
69493
- remindedSessions.add(sessionID);
69494
- const clampedPercentage = Math.min(Math.max(actualUsagePercentage, 0), 1);
69495
- const usedPct = (clampedPercentage * 100).toFixed(1);
69496
- const remainingPct = ((1 - clampedPercentage) * 100).toFixed(1);
69497
- const usedTokens = totalInputTokens.toLocaleString();
69498
- const limitTokens = actualLimit.toLocaleString();
69499
- output.output += `
69500
-
69501
- ${createContextReminder(actualLimit)}
69502
- [Context Status: ${usedPct}% used (${usedTokens}/${limitTokens} tokens), ${remainingPct}% remaining]`;
69503
- };
69504
- const eventHandler = async ({ event }) => {
69505
- const props = event.properties;
69506
- if (event.type === "session.deleted") {
69507
- const sessionID = resolveSessionEventID(props);
69508
- if (sessionID) {
69509
- remindedSessions.delete(sessionID);
69510
- tokenCache.delete(sessionID);
69511
- }
69512
- }
69513
- if (event.type === "message.updated") {
69514
- const info = props?.info;
69515
- const finish = info?.finish;
69516
- if (!info || info.role !== "assistant" || !finish)
69517
- return;
69518
- if (isCompactionAgent(info.agent))
69519
- return;
69520
- const sessionID = resolveMessageEventSessionID(props);
69521
- if (!sessionID || !info.providerID || !info.tokens)
69522
- return;
69523
- tokenCache.set(sessionID, {
69524
- providerID: info.providerID,
69525
- modelID: info.modelID ?? "",
69526
- tokens: info.tokens
69527
- });
69528
- }
69529
- };
69530
- return {
69531
- "tool.execute.after": toolExecuteAfter,
69532
- event: eventHandler
69533
- };
69534
- }
69535
69629
  // src/hooks/session-notification-content.ts
69536
69630
  init_shared();
69537
69631
  function extractMessageText(message) {
@@ -72900,7 +72994,7 @@ function findClosingDelimiter(content, openingLength) {
72900
72994
  }
72901
72995
  // packages/rules-engine/src/matcher.ts
72902
72996
  var import_picomatch = __toESM(require_picomatch2(), 1);
72903
- import { createHash } from "crypto";
72997
+ import { createHash as createHash2 } from "crypto";
72904
72998
  import { basename as basename6, relative as relative8 } from "path";
72905
72999
  var matcherCache = new Map;
72906
73000
  var MAX_MATCHER_CACHE_ENTRIES = 256;
@@ -72929,7 +73023,7 @@ function shouldApplyRule(metadata, currentFilePath, projectRoot) {
72929
73023
  return { applies: false };
72930
73024
  }
72931
73025
  function createContentHash(content) {
72932
- return createHash("sha256").update(content).digest("hex").slice(0, 16);
73026
+ return createHash2("sha256").update(content).digest("hex").slice(0, 16);
72933
73027
  }
72934
73028
  function isDuplicateByRealPath(realPath, cache) {
72935
73029
  return cache.has(realPath);
@@ -91169,6 +91263,12 @@ var initDeepSkill = {
91169
91263
  template: loadSharedSkillTemplate("init-deep"),
91170
91264
  argumentHint: "[--create-new] [--max-depth=N]"
91171
91265
  };
91266
+ // src/features/builtin-skills/skills/debugging.ts
91267
+ var debuggingSkill = {
91268
+ name: "debugging",
91269
+ description: "MUST USE for any real runtime debugging across ANY language or binary \u2014 crashes, silent failures, wrong responses, stuck processes, memory leaks, async misbehavior, unexplained timing, reverse engineering. Runs a hypothesis-driven loop: form \u22653 hypotheses, investigate in parallel, after 2 failed rounds spawn Oracles from orthogonal angles, confirm root cause, lock with a failing test, fix minimally, QA by actually USING the system, scrub artifacts. The actual HOW lives in `references/` \u2014 READ THEM. Triggers: 'debug this', 'why is X not working', 'hanging', 'attach a debugger', 'reverse engineer', 'pwndbg', 'gdb', 'lldb', 'node inspect', 'tsx debug', 'pdb', 'dlv', 'delve', 'rust-gdb', 'set a breakpoint', 'context window exploded', 'why is the response empty', 'attach the debugger', 'debug it', 'why is this happening', 'trace this bug', 'reproduce and fix', 'silent failure', 'HTTP 200 but empty', 'why did it stop', 'inspect the binary', 'reverse engineering', 'playwright'.",
91270
+ template: loadSharedSkillTemplate("debugging")
91271
+ };
91172
91272
  // src/features/builtin-skills/security-research/SKILL.md
91173
91273
  var SKILL_default = `# Security Research - Team Mode Vulnerability Audit
91174
91274
 
@@ -91604,6 +91704,7 @@ function createBuiltinSkills(options = {}) {
91604
91704
  reviewWorkSkill,
91605
91705
  removeAiSlopsSkill,
91606
91706
  initDeepSkill,
91707
+ debuggingSkill,
91607
91708
  securityResearchSkill,
91608
91709
  securityReviewSkill
91609
91710
  ];
@@ -92008,7 +92109,6 @@ var GitMasterConfigSchema = z17.object({
92008
92109
  import { z as z18 } from "zod";
92009
92110
  var HookNameSchema = z18.enum([
92010
92111
  "todo-continuation-enforcer",
92011
- "context-window-monitor",
92012
92112
  "session-recovery",
92013
92113
  "session-notification",
92014
92114
  "comment-checker",
@@ -114387,7 +114487,6 @@ function createRuntimeTmuxConfig(pluginConfig) {
114387
114487
  function createSessionHooks(args) {
114388
114488
  const { ctx, pluginConfig, modelCacheState, backgroundManager, modelFallbackControllerAccessor, isHookEnabled, safeHookEnabled } = args;
114389
114489
  const safeHook = (hookName, factory) => safeCreateHook(hookName, factory, { enabled: safeHookEnabled });
114390
- const contextWindowMonitor = isHookEnabled("context-window-monitor") ? safeHook("context-window-monitor", () => createContextWindowMonitorHook(ctx, modelCacheState)) : null;
114391
114490
  const preemptiveCompaction = isHookEnabled("preemptive-compaction") && pluginConfig.experimental?.preemptive_compaction ? safeHook("preemptive-compaction", () => createPreemptiveCompactionHook(ctx, pluginConfig, modelCacheState)) : null;
114392
114491
  const sessionRecovery = isHookEnabled("session-recovery") ? safeHook("session-recovery", () => createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental })) : null;
114393
114492
  let sessionNotification = null;
@@ -114485,7 +114584,6 @@ function createSessionHooks(args) {
114485
114584
  })) : null;
114486
114585
  const legacyPluginToast = isHookEnabled("legacy-plugin-toast") ? safeHook("legacy-plugin-toast", () => createLegacyPluginToastHook(ctx)) : null;
114487
114586
  return {
114488
- contextWindowMonitor,
114489
114587
  preemptiveCompaction,
114490
114588
  sessionRecovery,
114491
114589
  sessionNotification,
@@ -116063,7 +116161,8 @@ class ParentWakeNotifier {
116063
116161
  const role = this.getParentWakeMessageRole(message);
116064
116162
  if (role === "assistant") {
116065
116163
  const waiting = this.getParentWakeMessageFinish(message) === "tool-calls" || message.parts?.some((part) => this.parentWakePartIsWaitingOnTool(part)) === true;
116066
- return waiting ? { waiting: true, activityAt: getParentWakeMessageActivityAt(message) } : { waiting: false };
116164
+ const activityAt = getParentWakeMessageActivityAt(message);
116165
+ return waiting ? { waiting: true, activityAt } : { waiting: false, activityAt };
116067
116166
  }
116068
116167
  if (role === "user") {
116069
116168
  if (isSyntheticOrInternalUserMessage(message)) {
@@ -116162,16 +116261,18 @@ class ParentWakeNotifier {
116162
116261
  }
116163
116262
  const latestToolWaitAgeMs = toolWaitState.activityAt === undefined ? 0 : now - toolWaitState.activityAt;
116164
116263
  const deferAge = now - wake.toolCallDeferralStartedAt;
116264
+ const latestAssistantActivityAgeMs = toolWaitState.activityAt === undefined ? deferAge : now - toolWaitState.activityAt;
116165
116265
  if (wake.shouldReply && toolWaitState.waiting && deferAge >= this.options.toolCallDeferMaxMs && latestToolWaitAgeMs >= this.options.toolCallDeferMaxMs) {
116166
116266
  log("[background-agent] Sending parent wake after stale tool-call deferral window:", {
116167
116267
  sessionID
116168
116268
  });
116169
116269
  return { defer: false, skipPromptGateToolStateCheck: true };
116170
116270
  }
116171
- if (!toolWaitState.waiting && deferAge >= this.options.toolCallDeferMaxMs) {
116271
+ if (!toolWaitState.waiting && deferAge >= this.options.toolCallDeferMaxMs && latestAssistantActivityAgeMs >= this.options.toolCallDeferMaxMs) {
116172
116272
  log("[background-agent] Sending parent wake after stale assistant-text deferral window:", {
116173
116273
  sessionID,
116174
- deferAgeMs: deferAge
116274
+ deferAgeMs: deferAge,
116275
+ latestAssistantActivityAgeMs
116175
116276
  });
116176
116277
  return { defer: false, skipPromptGateToolStateCheck: true };
116177
116278
  }
@@ -119692,13 +119793,13 @@ async function findAvailablePort2(startPort = DEFAULT_PORT) {
119692
119793
 
119693
119794
  // src/features/mcp-oauth/oauth-authorization-flow.ts
119694
119795
  import { spawn as spawn3 } from "child_process";
119695
- import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
119796
+ import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
119696
119797
  import { createServer as createServer2 } from "http";
119697
119798
  function generateCodeVerifier() {
119698
119799
  return randomBytes2(32).toString("base64url");
119699
119800
  }
119700
119801
  function generateCodeChallenge(verifier) {
119701
- return createHash2("sha256").update(verifier).digest("base64url");
119802
+ return createHash3("sha256").update(verifier).digest("base64url");
119702
119803
  }
119703
119804
  function buildAuthorizationUrl(authorizationEndpoint, options) {
119704
119805
  const url = new URL(authorizationEndpoint);
@@ -140762,11 +140863,17 @@ init_model_normalization();
140762
140863
  var ASSISTANT_PREFILL_RECOVERY_TEXT = "[internal] Continue from the previous assistant state.";
140763
140864
  var ASSISTANT_PREFILL_UNSUPPORTED_PROVIDERS = new Set([
140764
140865
  "anthropic",
140765
- "google-vertex-anthropic"
140866
+ "aws-bedrock-anthropic",
140867
+ "github-copilot",
140868
+ "github-copilot-enterprise",
140869
+ "google-vertex-anthropic",
140870
+ "opencode",
140871
+ "opencode-go",
140872
+ "opencode-zen-proxy",
140873
+ "vercel"
140766
140874
  ]);
140767
140875
  var ASSISTANT_PREFILL_UNSUPPORTED_MODEL_PREFIXES = [
140768
- "claude-opus-4-7",
140769
- "claude-opus-4-6",
140876
+ "claude-opus-4",
140770
140877
  "claude-sonnet-4-6",
140771
140878
  "claude-mythos"
140772
140879
  ];
@@ -140817,15 +140924,26 @@ function findLastUserModel(messages) {
140817
140924
  }
140818
140925
  return;
140819
140926
  }
140927
+ function normalizeAssistantPrefillModelID(modelID) {
140928
+ const normalizedModelID = normalizeModelID(modelID.toLowerCase());
140929
+ return normalizedModelID.split(/[/.~:@]+/).find((segment) => segment.startsWith("claude-")) ?? normalizedModelID;
140930
+ }
140931
+ function hasAnthropicModelNamespace(modelID) {
140932
+ const normalizedModelID = normalizeModelID(modelID.toLowerCase());
140933
+ return /(?:^|[/.~:@])anthropic(?:$|[/.~:@])/.test(normalizedModelID);
140934
+ }
140935
+ function providerCanExposeUnsupportedAssistantPrefill(providerID, modelID) {
140936
+ return ASSISTANT_PREFILL_UNSUPPORTED_PROVIDERS.has(providerID) || hasAnthropicModelNamespace(modelID);
140937
+ }
140820
140938
  function shouldRepairAssistantPrefillForModel(model) {
140821
140939
  if (!model) {
140822
140940
  return false;
140823
140941
  }
140824
140942
  const providerID = model.providerID.toLowerCase();
140825
- if (!ASSISTANT_PREFILL_UNSUPPORTED_PROVIDERS.has(providerID)) {
140943
+ if (!providerCanExposeUnsupportedAssistantPrefill(providerID, model.modelID)) {
140826
140944
  return false;
140827
140945
  }
140828
- const modelID = normalizeModelID(model.modelID.toLowerCase());
140946
+ const modelID = normalizeAssistantPrefillModelID(model.modelID);
140829
140947
  return ASSISTANT_PREFILL_UNSUPPORTED_MODEL_PREFIXES.some((prefix) => modelID.startsWith(prefix));
140830
140948
  }
140831
140949
  function isCompactionContinuationPart(part) {
@@ -140886,9 +141004,10 @@ async function runMessagesTransformHookSafely(hookName, handler, input, output)
140886
141004
  try {
140887
141005
  await Promise.resolve(handler(input, output));
140888
141006
  } catch (error) {
141007
+ const hookError = error instanceof Error ? error : new Error(String(error));
140889
141008
  log("[messages-transform] hook execution failed", {
140890
141009
  hook: hookName,
140891
- error
141010
+ error: hookError
140892
141011
  });
140893
141012
  }
140894
141013
  }
@@ -141514,6 +141633,33 @@ function normalizeSessionStatusToIdle(input) {
141514
141633
 
141515
141634
  // src/plugin/event.ts
141516
141635
  init_event_session_id();
141636
+
141637
+ // src/plugin/user-abort-interrupted-recovery-guard.ts
141638
+ var USER_ABORT_ERROR_NAMES = new Set(["MessageAbortedError", "AbortError"]);
141639
+ function createUserAbortInterruptedRecoveryGuard() {
141640
+ const abortedSessions = new Set;
141641
+ return {
141642
+ noteSessionError(sessionID, errorName) {
141643
+ if (!errorName || !USER_ABORT_ERROR_NAMES.has(errorName)) {
141644
+ return false;
141645
+ }
141646
+ abortedSessions.add(sessionID);
141647
+ return true;
141648
+ },
141649
+ shouldSkipRecovery(sessionID) {
141650
+ const shouldSkip = abortedSessions.has(sessionID);
141651
+ if (shouldSkip) {
141652
+ abortedSessions.delete(sessionID);
141653
+ }
141654
+ return shouldSkip;
141655
+ },
141656
+ clear(sessionID) {
141657
+ abortedSessions.delete(sessionID);
141658
+ }
141659
+ };
141660
+ }
141661
+
141662
+ // src/plugin/event.ts
141517
141663
  function isRecord26(value) {
141518
141664
  return typeof value === "object" && value !== null;
141519
141665
  }
@@ -141598,6 +141744,7 @@ function createEventHandler2(args) {
141598
141744
  const lastKnownModelBySession = new Map;
141599
141745
  const modelFallbackContinuationsInFlight = new Set;
141600
141746
  const lastDispatchedModelFallbackContinuationKeys = new Map;
141747
+ const userAbortInterruptedRecoveryGuard = createUserAbortInterruptedRecoveryGuard();
141601
141748
  const resolveFallbackProviderID = (sessionID, providerHint) => {
141602
141749
  const normalizedProviderHint = providerHint?.trim();
141603
141750
  if (normalizedProviderHint) {
@@ -141651,7 +141798,6 @@ function createEventHandler2(args) {
141651
141798
  await runEventHookSafely("sessionNotification", hooks2.sessionNotification, input);
141652
141799
  await runEventHookSafely("todoContinuationEnforcer", hooks2.todoContinuationEnforcer?.handler, input);
141653
141800
  await runEventHookSafely("unstableAgentBabysitter", hooks2.unstableAgentBabysitter?.event, input);
141654
- await runEventHookSafely("contextWindowMonitor", hooks2.contextWindowMonitor?.event, input);
141655
141801
  await runEventHookSafely("preemptiveCompaction", hooks2.preemptiveCompaction?.event, input);
141656
141802
  await runEventHookSafely("directoryAgentsInjector", hooks2.directoryAgentsInjector?.event, input);
141657
141803
  await runEventHookSafely("directoryReadmeInjector", hooks2.directoryReadmeInjector?.event, input);
@@ -141713,6 +141859,10 @@ function createEventHandler2(args) {
141713
141859
  if (!sessionID || !hooks2.sessionRecovery?.handleInterruptedToolResultsOnIdle) {
141714
141860
  return false;
141715
141861
  }
141862
+ if (userAbortInterruptedRecoveryGuard.shouldSkipRecovery(sessionID)) {
141863
+ log("[event] interrupted tool recovery skipped after user abort", { sessionID });
141864
+ return false;
141865
+ }
141716
141866
  return hooks2.sessionRecovery.handleInterruptedToolResultsOnIdle(sessionID);
141717
141867
  };
141718
141868
  const dispatchIdleOnlyHooks = async (input) => {
@@ -141872,10 +142022,6 @@ function createEventHandler2(args) {
141872
142022
  const emittedAt = recentSyntheticIdles.get(sessionID);
141873
142023
  if (emittedAt !== undefined && now - emittedAt < DEDUP_WINDOW_MS2) {
141874
142024
  recentSyntheticIdles.delete(sessionID);
141875
- const lastAnyIdleAt = recentAnyIdles.get(sessionID);
141876
- if (lastAnyIdleAt === emittedAt) {
141877
- recentAnyIdles.delete(sessionID);
141878
- }
141879
142025
  }
141880
142026
  }
141881
142027
  const recovered = await recoverInterruptedToolResultsOnIdleEvent(input);
@@ -141964,6 +142110,7 @@ function createEventHandler2(args) {
141964
142110
  lastKnownModelBySession.delete(sessionID);
141965
142111
  modelFallbackContinuationsInFlight.delete(sessionID);
141966
142112
  lastDispatchedModelFallbackContinuationKeys.delete(sessionID);
142113
+ userAbortInterruptedRecoveryGuard.clear(sessionID);
141967
142114
  if (modelFallback) {
141968
142115
  clearPendingModelFallback(modelFallback, sessionID);
141969
142116
  clearSessionFallbackChain(modelFallback, sessionID);
@@ -142042,6 +142189,10 @@ function createEventHandler2(args) {
142042
142189
  lastKnownModelBySession.set(sessionID, { providerID, modelID });
142043
142190
  setSessionModel(sessionID, { providerID, modelID });
142044
142191
  }
142192
+ userAbortInterruptedRecoveryGuard.clear(sessionID);
142193
+ }
142194
+ if (sessionID && role === "assistant") {
142195
+ userAbortInterruptedRecoveryGuard.noteSessionError(sessionID, extractErrorName3(info?.error));
142045
142196
  }
142046
142197
  if (sessionID && role === "assistant" && !isRuntimeFallbackEnabled && isModelFallbackEnabled) {
142047
142198
  try {
@@ -142157,6 +142308,9 @@ function createEventHandler2(args) {
142157
142308
  const errorName = extractErrorName3(error);
142158
142309
  const errorMessage = extractErrorMessage3(error);
142159
142310
  const errorInfo = { name: errorName, message: errorMessage };
142311
+ if (sessionID) {
142312
+ userAbortInterruptedRecoveryGuard.noteSessionError(sessionID, errorName);
142313
+ }
142160
142314
  if (hooks2.sessionRecovery?.isRecoverableError(error)) {
142161
142315
  const messageInfo = {
142162
142316
  id: props?.messageID,
@@ -142356,7 +142510,6 @@ function createToolExecuteAfterHandler3(args) {
142356
142510
  await hooks2.toolOutputTruncator?.["tool.execute.after"]?.(hookInput, output);
142357
142511
  await hooks2.claudeCodeHooks?.["tool.execute.after"]?.(hookInput, output);
142358
142512
  await hooks2.preemptiveCompaction?.["tool.execute.after"]?.(hookInput, output);
142359
- await hooks2.contextWindowMonitor?.["tool.execute.after"]?.(hookInput, output);
142360
142513
  await hooks2.commentChecker?.["tool.execute.after"]?.(hookInput, output);
142361
142514
  await hooks2.directoryAgentsInjector?.["tool.execute.after"]?.(hookInput, output);
142362
142515
  await hooks2.directoryReadmeInjector?.["tool.execute.after"]?.(hookInput, output);