opencode-matrixx 2.6.1 → 2.6.2

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.
package/dist/index.js CHANGED
@@ -4551,7 +4551,7 @@ var require_picomatch2 = __commonJS(function(exports, module) {
4551
4551
  });
4552
4552
 
4553
4553
  // src/hooks/matrix-loop/constants.ts
4554
- var HOOK_NAME5 = "matrix-loop", DEFAULT_STATE_FILE = ".matrixx/matrix-loop.local.md", DEFAULT_MAX_ITERATIONS = 100, DEFAULT_COMPLETION_PROMISE = "DONE", DEFAULT_VERIFICATION_AGENT = "merovingian", DEFAULT_VERIFICATION_TIMEOUT_MS = 30000, DEFAULT_VERIFICATION_MAX_RETRIES = 2;
4554
+ var HOOK_NAME6 = "matrix-loop", DEFAULT_STATE_FILE = ".matrixx/matrix-loop.local.md", DEFAULT_MAX_ITERATIONS = 100, DEFAULT_COMPLETION_PROMISE = "DONE", DEFAULT_VERIFICATION_AGENT = "merovingian", DEFAULT_VERIFICATION_TIMEOUT_MS = 30000, DEFAULT_VERIFICATION_MAX_RETRIES = 2;
4555
4555
 
4556
4556
  // src/hooks/matrix-loop/storage.ts
4557
4557
  var exports_storage = {};
@@ -55689,6 +55689,75 @@ function createCompactionTodoPreserverHook(ctx) {
55689
55689
  };
55690
55690
  return { capture, event };
55691
55691
  }
55692
+ // src/hooks/context-mode-enforcer/constants.ts
55693
+ var HOOK_NAME3 = "context-mode-enforcer";
55694
+ var WARN_MESSAGE_READ = "Use ctx_search / ctx_batch_execute / ctx_execute for analysis \u2014 raw read/grep/glob bypasses FTS5 sandbox and pollutes context. Use Read only with hashline IDs for Edit.";
55695
+ var BLOCK_MESSAGE_GREP_GLOB = "Blocked: raw grep/glob is forbidden when context-mode is enforced. Use ctx_search (indexed) \u2192 grep/glob fallback only when ctx_* unavailable, or ctx_batch_execute / ctx_execute for multi-file analysis.";
55696
+ var WARN_MESSAGE_BASH_READ = "Use ctx_* / Read tool instead of `cat`/`head`/`tail`/`grep` via bash for analysis. Raw bash file reads bypass sandbox.";
55697
+
55698
+ // src/hooks/context-mode-enforcer/hook.ts
55699
+ var BASH_FILE_READ_RE = /^\s*(cat|head|tail)\s+/;
55700
+ var BASH_GREP_RE = /\bgrep\b/;
55701
+ function resolveContextModeConfig(pluginConfig) {
55702
+ const cfg = pluginConfig.context_mode;
55703
+ return {
55704
+ enabled: cfg?.enabled ?? true,
55705
+ enforce: cfg?.enforce ?? false,
55706
+ blockedTools: new Set((cfg?.blocked_tools ?? ["grep", "glob"]).map((t) => t.toLowerCase()))
55707
+ };
55708
+ }
55709
+ function createContextModeEnforcerHook(pluginConfig) {
55710
+ const { enabled, enforce, blockedTools } = resolveContextModeConfig(pluginConfig);
55711
+ return {
55712
+ "tool.execute.before": async (input, output) => {
55713
+ if (!enabled)
55714
+ return;
55715
+ const tool = input.tool.toLowerCase();
55716
+ const isBlocked = blockedTools.has(tool);
55717
+ const isRead = tool === "read";
55718
+ const isGrepGlob = tool === "grep" || tool === "glob";
55719
+ const isBash = tool === "bash";
55720
+ if (isBash) {
55721
+ const command = output.args.command;
55722
+ if (typeof command !== "string")
55723
+ return;
55724
+ const isFileRead = BASH_FILE_READ_RE.test(command);
55725
+ const isGrep = BASH_GREP_RE.test(command);
55726
+ if (!isFileRead && !isGrep)
55727
+ return;
55728
+ if (enforce && blockedTools.has("bash")) {
55729
+ log(`[${HOOK_NAME3}] BLOCKED bash file read`, {
55730
+ sessionID: input.sessionID,
55731
+ command: command.slice(0, 120)
55732
+ });
55733
+ throw new Error(`${BLOCK_MESSAGE_GREP_GLOB}
55734
+
55735
+ Bash command: ${command.slice(0, 200)}`);
55736
+ }
55737
+ output.message = WARN_MESSAGE_BASH_READ;
55738
+ log(`[${HOOK_NAME3}] warned on bash`, { sessionID: input.sessionID, command: command.slice(0, 120) });
55739
+ return;
55740
+ }
55741
+ if (isRead) {
55742
+ if (!isBlocked)
55743
+ return;
55744
+ output.message = WARN_MESSAGE_READ;
55745
+ log(`[${HOOK_NAME3}] warned on read (soft)`, { sessionID: input.sessionID });
55746
+ return;
55747
+ }
55748
+ if (isGrepGlob) {
55749
+ if (!isBlocked)
55750
+ return;
55751
+ if (enforce) {
55752
+ log(`[${HOOK_NAME3}] BLOCKED ${tool}`, { sessionID: input.sessionID });
55753
+ throw new Error(BLOCK_MESSAGE_GREP_GLOB);
55754
+ }
55755
+ output.message = WARN_MESSAGE_READ;
55756
+ log(`[${HOOK_NAME3}] warned on ${tool} (soft)`, { sessionID: input.sessionID });
55757
+ }
55758
+ }
55759
+ };
55760
+ }
55692
55761
  // src/hooks/context-window-monitor.ts
55693
55762
  var ANTHROPIC_DISPLAY_LIMIT = 1e6;
55694
55763
  var ANTHROPIC_ACTUAL_LIMIT2 = process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : 200000;
@@ -55881,7 +55950,7 @@ ${guidance}`;
55881
55950
  import { join as join35 } from "path";
55882
55951
 
55883
55952
  // src/hooks/design-intent-preserver/constants.ts
55884
- var HOOK_NAME3 = "design-intent-preserver";
55953
+ var HOOK_NAME4 = "design-intent-preserver";
55885
55954
  var PLANS_DIR2 = ".matrixx/plans";
55886
55955
  var PHASE_TRANSITION_PATTERN = /\bphase[\s\-:]*(\d+)\b/i;
55887
55956
  var CONSTRUCT_INDICATORS = /\b(?:construct|web[\s-]?designer|frontend[\s-]?ui[\s-]?ux)\b|@designer/i;
@@ -55968,7 +56037,7 @@ function createDesignIntentPreserverHook(_ctx) {
55968
56037
  ---
55969
56038
 
55970
56039
  ${originalText}`;
55971
- log(`[${HOOK_NAME3}] injected design intent guardrail`, {
56040
+ log(`[${HOOK_NAME4}] injected design intent guardrail`, {
55972
56041
  sessionID: input.sessionID,
55973
56042
  planPath
55974
56043
  });
@@ -59515,18 +59584,18 @@ function extractPromptText3(parts) {
59515
59584
  }
59516
59585
  // src/features/ultrawork-state/manager.ts
59517
59586
  init_logger();
59518
- var HOOK_NAME4 = "ultrawork-state";
59587
+ var HOOK_NAME5 = "ultrawork-state";
59519
59588
  var sessionStates = new Map;
59520
59589
  function getUltraworkState(sessionID) {
59521
59590
  return sessionStates.get(sessionID);
59522
59591
  }
59523
59592
  function enableUltrawork(sessionID) {
59524
59593
  sessionStates.set(sessionID, "enabled");
59525
- log(`[${HOOK_NAME4}] Ultrawork enabled for session`, { sessionID });
59594
+ log(`[${HOOK_NAME5}] Ultrawork enabled for session`, { sessionID });
59526
59595
  }
59527
59596
  function disableUltrawork(sessionID) {
59528
59597
  sessionStates.set(sessionID, "disabled");
59529
- log(`[${HOOK_NAME4}] Ultrawork disabled for session`, { sessionID });
59598
+ log(`[${HOOK_NAME5}] Ultrawork disabled for session`, { sessionID });
59530
59599
  }
59531
59600
  // src/hooks/keyword-detector/hook.ts
59532
59601
  function createKeywordDetectorHook(ctx, _collector) {
@@ -59659,7 +59728,7 @@ function createLoopStateController(options) {
59659
59728
  };
59660
59729
  const success2 = writeState(directory, state3, stateDir);
59661
59730
  if (success2) {
59662
- log(`[${HOOK_NAME5}] Loop started`, {
59731
+ log(`[${HOOK_NAME6}] Loop started`, {
59663
59732
  sessionID,
59664
59733
  maxIterations: state3.max_iterations,
59665
59734
  completionPromise: state3.completion_promise
@@ -59674,7 +59743,7 @@ function createLoopStateController(options) {
59674
59743
  }
59675
59744
  const success2 = clearState(directory, stateDir);
59676
59745
  if (success2) {
59677
- log(`[${HOOK_NAME5}] Loop cancelled`, { sessionID, iteration: state3.iteration });
59746
+ log(`[${HOOK_NAME6}] Loop cancelled`, { sessionID, iteration: state3.iteration });
59678
59747
  }
59679
59748
  return success2;
59680
59749
  },
@@ -59751,7 +59820,7 @@ async function detectCompletionInSessionMessages(ctx, options) {
59751
59820
  return false;
59752
59821
  } catch (err) {
59753
59822
  setTimeout(() => {
59754
- log(`[${HOOK_NAME5}] Session messages check failed`, {
59823
+ log(`[${HOOK_NAME6}] Session messages check failed`, {
59755
59824
  sessionID: options.sessionID,
59756
59825
  error: String(err)
59757
59826
  });
@@ -59832,7 +59901,7 @@ async function verifyCompletion(ctx, options) {
59832
59901
  query: { directory: options.directory }
59833
59902
  });
59834
59903
  if (createResult.error || !createResult.data?.id) {
59835
- log(`[${HOOK_NAME5}] Verification session creation failed`);
59904
+ log(`[${HOOK_NAME6}] Verification session creation failed`);
59836
59905
  return { verified: true };
59837
59906
  }
59838
59907
  verifySessionID = createResult.data.id;
@@ -59862,16 +59931,16 @@ async function verifyCompletion(ctx, options) {
59862
59931
  return parseVerificationResponse(assistantText);
59863
59932
  }
59864
59933
  } catch {
59865
- log(`[${HOOK_NAME5}] Verification poll error`);
59934
+ log(`[${HOOK_NAME6}] Verification poll error`);
59866
59935
  }
59867
59936
  await new Promise((resolve5) => {
59868
59937
  setTimeout(resolve5, pollIntervalMs);
59869
59938
  });
59870
59939
  }
59871
- log(`[${HOOK_NAME5}] Verification timed out, assuming pass`);
59940
+ log(`[${HOOK_NAME6}] Verification timed out, assuming pass`);
59872
59941
  return { verified: true };
59873
59942
  } catch (err) {
59874
- log(`[${HOOK_NAME5}] Verification error: ${String(err)}`);
59943
+ log(`[${HOOK_NAME6}] Verification error: ${String(err)}`);
59875
59944
  return { verified: true };
59876
59945
  } finally {
59877
59946
  if (verifySessionID) {
@@ -59947,7 +60016,7 @@ async function injectContinuationPrompt(ctx, options) {
59947
60016
 
59948
60017
  // src/hooks/matrix-loop/matrix-loop-event-handler.ts
59949
60018
  async function handleVerificationFailure(ctx, options, state3, sessionID, reason, failedCount) {
59950
- log(`[${HOOK_NAME5}] Verification failed`, { sessionID, reason });
60019
+ log(`[${HOOK_NAME6}] Verification failed`, { sessionID, reason });
59951
60020
  const newState = options.loopState.incrementIteration();
59952
60021
  if (newState) {
59953
60022
  newState.verification_failed_count = failedCount + 1;
@@ -59965,7 +60034,7 @@ ${buildContinuationPrompt(newState ?? state3)}`,
59965
60034
  apiTimeoutMs: options.apiTimeoutMs
59966
60035
  });
59967
60036
  } catch (err) {
59968
- log(`[${HOOK_NAME5}] Failed to inject verification feedback`, { sessionID, error: String(err) });
60037
+ log(`[${HOOK_NAME6}] Failed to inject verification feedback`, { sessionID, error: String(err) });
59969
60038
  }
59970
60039
  }
59971
60040
  function createMatrixLoopEventHandler(ctx, options) {
@@ -59977,13 +60046,13 @@ function createMatrixLoopEventHandler(ctx, options) {
59977
60046
  if (!sessionID)
59978
60047
  return;
59979
60048
  if (inFlightSessions.has(sessionID)) {
59980
- log(`[${HOOK_NAME5}] Skipped: handler in flight`, { sessionID });
60049
+ log(`[${HOOK_NAME6}] Skipped: handler in flight`, { sessionID });
59981
60050
  return;
59982
60051
  }
59983
60052
  inFlightSessions.add(sessionID);
59984
60053
  try {
59985
60054
  if (options.sessionRecovery.isRecovering(sessionID)) {
59986
- log(`[${HOOK_NAME5}] Skipped: in recovery`, { sessionID });
60055
+ log(`[${HOOK_NAME6}] Skipped: in recovery`, { sessionID });
59987
60056
  return;
59988
60057
  }
59989
60058
  const state3 = options.loopState.getState();
@@ -59995,11 +60064,11 @@ function createMatrixLoopEventHandler(ctx, options) {
59995
60064
  const exists = await options.checkSessionExists(state3.session_id);
59996
60065
  if (!exists) {
59997
60066
  options.loopState.clear();
59998
- log(`[${HOOK_NAME5}] Cleared orphaned state from deleted session`, { orphanedSessionId: state3.session_id, currentSessionId: sessionID });
60067
+ log(`[${HOOK_NAME6}] Cleared orphaned state from deleted session`, { orphanedSessionId: state3.session_id, currentSessionId: sessionID });
59999
60068
  return;
60000
60069
  }
60001
60070
  } catch (err) {
60002
- log(`[${HOOK_NAME5}] Failed to check session existence`, { sessionId: state3.session_id, error: String(err) });
60071
+ log(`[${HOOK_NAME6}] Failed to check session existence`, { sessionId: state3.session_id, error: String(err) });
60003
60072
  }
60004
60073
  }
60005
60074
  return;
@@ -60018,7 +60087,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60018
60087
  messages = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
60019
60088
  } catch (err) {
60020
60089
  setTimeout(() => {
60021
- log(`[${HOOK_NAME5}] Session messages fetch failed`, { sessionID, error: String(err) });
60090
+ log(`[${HOOK_NAME6}] Session messages fetch failed`, { sessionID, error: String(err) });
60022
60091
  }, 0);
60023
60092
  messages = [];
60024
60093
  }
@@ -60031,7 +60100,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60031
60100
  preFetchedMessages: messages
60032
60101
  });
60033
60102
  if (completionViaTranscript || completionViaApi) {
60034
- log(`[${HOOK_NAME5}] Completion detected!`, { sessionID, iteration: state3.iteration, promise: state3.completion_promise, detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api" });
60103
+ log(`[${HOOK_NAME6}] Completion detected!`, { sessionID, iteration: state3.iteration, promise: state3.completion_promise, detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api" });
60035
60104
  if (options.verification?.enabled === true) {
60036
60105
  const maxRetries = options.verification?.maxRetries ?? DEFAULT_VERIFICATION_MAX_RETRIES;
60037
60106
  const failedCount = state3.verification_failed_count ?? 0;
@@ -60066,7 +60135,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60066
60135
  return;
60067
60136
  }
60068
60137
  if (state3.iteration >= state3.max_iterations) {
60069
- log(`[${HOOK_NAME5}] Max iterations reached`, { sessionID, iteration: state3.iteration, max: state3.max_iterations });
60138
+ log(`[${HOOK_NAME6}] Max iterations reached`, { sessionID, iteration: state3.iteration, max: state3.max_iterations });
60070
60139
  options.loopState.clear();
60071
60140
  await ctx.client.tui.showToast({ body: { title: "Matrix Loop Stopped", message: `Max iterations (${state3.max_iterations}) reached without completion`, variant: "warning", duration: 5000 } }).catch((err) => {
60072
60141
  log("[matrix-loop] Max iterations toast failed:", err);
@@ -60075,17 +60144,17 @@ function createMatrixLoopEventHandler(ctx, options) {
60075
60144
  }
60076
60145
  const newState = options.loopState.incrementIteration();
60077
60146
  if (!newState) {
60078
- log(`[${HOOK_NAME5}] Failed to increment iteration`, { sessionID });
60147
+ log(`[${HOOK_NAME6}] Failed to increment iteration`, { sessionID });
60079
60148
  return;
60080
60149
  }
60081
- log(`[${HOOK_NAME5}] Continuing loop`, { sessionID, iteration: newState.iteration, max: newState.max_iterations });
60150
+ log(`[${HOOK_NAME6}] Continuing loop`, { sessionID, iteration: newState.iteration, max: newState.max_iterations });
60082
60151
  await ctx.client.tui.showToast({ body: { title: "Matrix Loop", message: `Iteration ${newState.iteration}/${newState.max_iterations}`, variant: "info", duration: 2000 } }).catch((err) => {
60083
60152
  log("[matrix-loop] Iteration toast failed:", err);
60084
60153
  });
60085
60154
  try {
60086
60155
  await injectContinuationPrompt(ctx, { sessionID, prompt: buildContinuationPrompt(newState), directory: options.directory, apiTimeoutMs: options.apiTimeoutMs });
60087
60156
  } catch (err) {
60088
- log(`[${HOOK_NAME5}] Failed to inject continuation`, { sessionID, error: String(err) });
60157
+ log(`[${HOOK_NAME6}] Failed to inject continuation`, { sessionID, error: String(err) });
60089
60158
  }
60090
60159
  return;
60091
60160
  } finally {
@@ -60099,7 +60168,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60099
60168
  const state3 = options.loopState.getState();
60100
60169
  if (state3?.session_id === sessionInfo.id) {
60101
60170
  options.loopState.clear();
60102
- log(`[${HOOK_NAME5}] Session deleted, loop cleared`, { sessionID: sessionInfo.id });
60171
+ log(`[${HOOK_NAME6}] Session deleted, loop cleared`, { sessionID: sessionInfo.id });
60103
60172
  }
60104
60173
  options.sessionRecovery.clear(sessionInfo.id);
60105
60174
  return;
@@ -60112,7 +60181,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60112
60181
  const state3 = options.loopState.getState();
60113
60182
  if (state3?.session_id === sessionID) {
60114
60183
  options.loopState.clear();
60115
- log(`[${HOOK_NAME5}] User aborted, loop cleared`, { sessionID });
60184
+ log(`[${HOOK_NAME6}] User aborted, loop cleared`, { sessionID });
60116
60185
  }
60117
60186
  options.sessionRecovery.clear(sessionID);
60118
60187
  }
@@ -60205,7 +60274,7 @@ function createMcpStartupNotificationHook(ctx) {
60205
60274
  };
60206
60275
  }
60207
60276
  // src/hooks/mouse-notepad/constants.ts
60208
- var HOOK_NAME6 = "mouse-notepad";
60277
+ var HOOK_NAME7 = "mouse-notepad";
60209
60278
  var NOTEPAD_DIRECTIVE = `
60210
60279
  <Work_Context>
60211
60280
  ## Notepad Location (for recording learnings)
@@ -60252,14 +60321,14 @@ function createMouseNotepadHook(ctx) {
60252
60321
  return;
60253
60322
  }
60254
60323
  output.args.prompt = NOTEPAD_DIRECTIVE + prompt;
60255
- log(`[${HOOK_NAME6}] Injected notepad directive to task`, {
60324
+ log(`[${HOOK_NAME7}] Injected notepad directive to task`, {
60256
60325
  sessionID: input.sessionID
60257
60326
  });
60258
60327
  }
60259
60328
  };
60260
60329
  }
60261
60330
  // src/hooks/non-interactive-env/constants.ts
60262
- var HOOK_NAME7 = "non-interactive-env";
60331
+ var HOOK_NAME8 = "non-interactive-env";
60263
60332
  var NON_INTERACTIVE_ENV = {
60264
60333
  CI: "true",
60265
60334
  DEBIAN_FRONTEND: "noninteractive",
@@ -60350,7 +60419,7 @@ function createNonInteractiveEnvHook(_ctx) {
60350
60419
  }
60351
60420
  const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, "unix");
60352
60421
  output.args.command = `${envPrefix} ${command}`;
60353
- log(`[${HOOK_NAME7}] Prepended non-interactive env vars to git command`, {
60422
+ log(`[${HOOK_NAME8}] Prepended non-interactive env vars to git command`, {
60354
60423
  sessionID: input.sessionID,
60355
60424
  envPrefix
60356
60425
  });
@@ -60358,7 +60427,7 @@ function createNonInteractiveEnvHook(_ctx) {
60358
60427
  };
60359
60428
  }
60360
60429
  // src/hooks/oracle-md-only/constants.ts
60361
- var HOOK_NAME8 = "oracle-md-only";
60430
+ var HOOK_NAME9 = "oracle-md-only";
60362
60431
  var ORACLE_AGENT = "oracle";
60363
60432
  var ALLOWED_EXTENSIONS = [".md"];
60364
60433
  var BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"];
@@ -60491,7 +60560,7 @@ function createOracleMdOnlyHook(ctx) {
60491
60560
  const prompt = output.args.prompt;
60492
60561
  if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
60493
60562
  output.args.prompt = PLANNING_CONSULT_WARNING + prompt;
60494
- log(`[${HOOK_NAME8}] Injected read-only planning warning to ${toolName}`, {
60563
+ log(`[${HOOK_NAME9}] Injected read-only planning warning to ${toolName}`, {
60495
60564
  sessionID: input.sessionID,
60496
60565
  tool: toolName,
60497
60566
  agent: agentName
@@ -60507,17 +60576,17 @@ function createOracleMdOnlyHook(ctx) {
60507
60576
  return;
60508
60577
  }
60509
60578
  if (!isAllowedFile(filePath, ctx.directory)) {
60510
- log(`[${HOOK_NAME8}] Blocked: Oracle can only write to .matrixx/*.md`, {
60579
+ log(`[${HOOK_NAME9}] Blocked: Oracle can only write to .matrixx/*.md`, {
60511
60580
  sessionID: input.sessionID,
60512
60581
  tool: toolName,
60513
60582
  filePath,
60514
60583
  agent: agentName
60515
60584
  });
60516
- throw new Error(`[${HOOK_NAME8}] ${getAgentDisplayName("oracle")} can only write/edit .md files inside .matrixx/ directory. ` + `Attempted to modify: ${filePath}. ` + `${getAgentDisplayName("oracle")} is a READ-ONLY planner. Use /start-work to execute the plan. ` + `APOLOGIZE TO THE USER, REMIND OF YOUR PLAN WRITING PROCESSES, TELL USER WHAT YOU WILL GOING TO DO AS THE PROCESS, WRITE THE PLAN`);
60585
+ throw new Error(`[${HOOK_NAME9}] ${getAgentDisplayName("oracle")} can only write/edit .md files inside .matrixx/ directory. ` + `Attempted to modify: ${filePath}. ` + `${getAgentDisplayName("oracle")} is a READ-ONLY planner. Use /start-work to execute the plan. ` + `APOLOGIZE TO THE USER, REMIND OF YOUR PLAN WRITING PROCESSES, TELL USER WHAT YOU WILL GOING TO DO AS THE PROCESS, WRITE THE PLAN`);
60517
60586
  }
60518
60587
  const normalizedPath = filePath.toLowerCase().replace(/\\/g, "/");
60519
60588
  if (normalizedPath.includes(".matrixx/plans/") || normalizedPath.includes(".matrixx\\plans\\")) {
60520
- log(`[${HOOK_NAME8}] Injecting workflow reminder for plan write`, {
60589
+ log(`[${HOOK_NAME9}] Injecting workflow reminder for plan write`, {
60521
60590
  sessionID: input.sessionID,
60522
60591
  tool: toolName,
60523
60592
  filePath,
@@ -60525,7 +60594,7 @@ function createOracleMdOnlyHook(ctx) {
60525
60594
  });
60526
60595
  output.message = (output.message || "") + ORACLE_WORKFLOW_REMINDER;
60527
60596
  }
60528
- log(`[${HOOK_NAME8}] Allowed: .matrixx/*.md write permitted`, {
60597
+ log(`[${HOOK_NAME9}] Allowed: .matrixx/*.md write permitted`, {
60529
60598
  sessionID: input.sessionID,
60530
60599
  tool: toolName,
60531
60600
  filePath,
@@ -60596,7 +60665,7 @@ async function getGitHead(directory) {
60596
60665
  }
60597
60666
 
60598
60667
  // src/hooks/plan-persister/hook.ts
60599
- var HOOK_NAME9 = "plan-persister";
60668
+ var HOOK_NAME10 = "plan-persister";
60600
60669
  function createPlanPersister(ctx, options) {
60601
60670
  const { directory } = options;
60602
60671
  const capture = async (sessionID) => {
@@ -60615,7 +60684,7 @@ function createPlanPersister(ctx, options) {
60615
60684
  const response = await ctx.client.session.todo({ path: { id: sessionID } });
60616
60685
  todos = normalizeTodos(response);
60617
60686
  } catch (err) {
60618
- log(`[${HOOK_NAME9}] Failed to fetch todos`, { sessionID, error: String(err) });
60687
+ log(`[${HOOK_NAME10}] Failed to fetch todos`, { sessionID, error: String(err) });
60619
60688
  return;
60620
60689
  }
60621
60690
  const content = readPlanFile(planPath);
@@ -60637,9 +60706,9 @@ function createPlanPersister(ctx, options) {
60637
60706
  ensurePlanDir(directory);
60638
60707
  const ok = atomicWrite(planPath, finalContent);
60639
60708
  if (ok) {
60640
- log(`[${HOOK_NAME9}] Plan file updated`, { planPath, completed, total });
60709
+ log(`[${HOOK_NAME10}] Plan file updated`, { planPath, completed, total });
60641
60710
  } else {
60642
- log(`[${HOOK_NAME9}] Failed to write plan file`, { planPath });
60711
+ log(`[${HOOK_NAME10}] Failed to write plan file`, { planPath });
60643
60712
  }
60644
60713
  };
60645
60714
  const event = async ({ event: evt }) => {
@@ -62640,7 +62709,7 @@ var RETRYABLE_ERROR_PATTERNS = [
62640
62709
  /(?:^|\s)503(?:\s|$)/,
62641
62710
  /(?:^|\s)529(?:\s|$)/
62642
62711
  ];
62643
- var HOOK_NAME10 = "runtime-fallback";
62712
+ var HOOK_NAME11 = "runtime-fallback";
62644
62713
 
62645
62714
  // src/hooks/runtime-fallback/fallback-models.ts
62646
62715
  init_logger();
@@ -62731,7 +62800,7 @@ function getRawFallbackModelsForSession(sessionID, agent, pluginConfig) {
62731
62800
  if (result)
62732
62801
  return result;
62733
62802
  }
62734
- log(`[${HOOK_NAME10}] No category/agent fallback models resolved for session`, { sessionID, agent });
62803
+ log(`[${HOOK_NAME11}] No category/agent fallback models resolved for session`, { sessionID, agent });
62735
62804
  return;
62736
62805
  }
62737
62806
 
@@ -62760,21 +62829,21 @@ function findNextAvailableFallback(state3, fallbackModels, cooldownSeconds) {
62760
62829
  if (!isModelInCooldown(candidate, state3, cooldownSeconds)) {
62761
62830
  return candidate;
62762
62831
  }
62763
- log(`[${HOOK_NAME10}] Skipping fallback model in cooldown`, { model: candidate, index: i });
62832
+ log(`[${HOOK_NAME11}] Skipping fallback model in cooldown`, { model: candidate, index: i });
62764
62833
  }
62765
62834
  return;
62766
62835
  }
62767
62836
  function prepareFallback(sessionID, state3, fallbackModels, config2) {
62768
62837
  if (state3.attemptCount >= config2.max_fallback_attempts) {
62769
- log(`[${HOOK_NAME10}] Max fallback attempts reached`, { sessionID, attempts: state3.attemptCount });
62838
+ log(`[${HOOK_NAME11}] Max fallback attempts reached`, { sessionID, attempts: state3.attemptCount });
62770
62839
  return { success: false, error: "Max fallback attempts reached", maxAttemptsReached: true };
62771
62840
  }
62772
62841
  const nextModel = findNextAvailableFallback(state3, fallbackModels, config2.cooldown_seconds);
62773
62842
  if (!nextModel) {
62774
- log(`[${HOOK_NAME10}] No available fallback models`, { sessionID });
62843
+ log(`[${HOOK_NAME11}] No available fallback models`, { sessionID });
62775
62844
  return { success: false, error: "No available fallback models (all in cooldown or exhausted)" };
62776
62845
  }
62777
- log(`[${HOOK_NAME10}] Preparing fallback`, {
62846
+ log(`[${HOOK_NAME11}] Preparing fallback`, {
62778
62847
  sessionID,
62779
62848
  from: state3.currentModel,
62780
62849
  to: nextModel,
@@ -62860,9 +62929,9 @@ function createAutoRetryHelpers(deps) {
62860
62929
  const abortSessionRequest = async (sessionID, source) => {
62861
62930
  try {
62862
62931
  await ctx.client.session.abort({ path: { id: sessionID } });
62863
- log(`[${HOOK_NAME10}] Aborted in-flight session request (${source})`, { sessionID });
62932
+ log(`[${HOOK_NAME11}] Aborted in-flight session request (${source})`, { sessionID });
62864
62933
  } catch (error51) {
62865
- log(`[${HOOK_NAME10}] Failed to abort in-flight session request (${source})`, {
62934
+ log(`[${HOOK_NAME11}] Failed to abort in-flight session request (${source})`, {
62866
62935
  sessionID,
62867
62936
  error: String(error51)
62868
62937
  });
@@ -62886,7 +62955,7 @@ function createAutoRetryHelpers(deps) {
62886
62955
  if (!state3)
62887
62956
  return;
62888
62957
  if (sessionRetryInFlight.has(sessionID)) {
62889
- log(`[${HOOK_NAME10}] Overriding in-flight retry due to session timeout`, { sessionID });
62958
+ log(`[${HOOK_NAME11}] Overriding in-flight retry due to session timeout`, { sessionID });
62890
62959
  }
62891
62960
  await abortSessionRequest(sessionID, "session.timeout");
62892
62961
  sessionRetryInFlight.delete(sessionID);
@@ -62896,7 +62965,7 @@ function createAutoRetryHelpers(deps) {
62896
62965
  const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
62897
62966
  if (fallbackModels.length === 0)
62898
62967
  return;
62899
- log(`[${HOOK_NAME10}] Session fallback timeout reached`, {
62968
+ log(`[${HOOK_NAME11}] Session fallback timeout reached`, {
62900
62969
  sessionID,
62901
62970
  timeoutSeconds: config2.timeout_seconds,
62902
62971
  currentModel: state3.currentModel
@@ -62910,7 +62979,7 @@ function createAutoRetryHelpers(deps) {
62910
62979
  };
62911
62980
  const autoRetryWithFallback = async (sessionID, newModel, resolvedAgent, source) => {
62912
62981
  if (sessionRetryInFlight.has(sessionID)) {
62913
- log(`[${HOOK_NAME10}] Retry already in flight, skipping (${source})`, { sessionID });
62982
+ log(`[${HOOK_NAME11}] Retry already in flight, skipping (${source})`, { sessionID });
62914
62983
  return;
62915
62984
  }
62916
62985
  const agentSettings = resolvedAgent ? pluginConfig?.agents?.[resolvedAgent] : undefined;
@@ -62919,7 +62988,7 @@ function createAutoRetryHelpers(deps) {
62919
62988
  reasoningEffort: agentSettings.reasoningEffort
62920
62989
  } : undefined);
62921
62990
  if (!retryModelPayload) {
62922
- log(`[${HOOK_NAME10}] Invalid model format (missing provider prefix): ${newModel}`);
62991
+ log(`[${HOOK_NAME11}] Invalid model format (missing provider prefix): ${newModel}`);
62923
62992
  const state3 = sessionStates2.get(sessionID);
62924
62993
  if (state3?.pendingFallbackModel) {
62925
62994
  state3.pendingFallbackModel = undefined;
@@ -62935,7 +63004,7 @@ function createAutoRetryHelpers(deps) {
62935
63004
  });
62936
63005
  const retryParts = getLastUserRetryParts(messagesResp);
62937
63006
  if (retryParts.length > 0) {
62938
- log(`[${HOOK_NAME10}] Auto-retrying with fallback model (${source})`, {
63007
+ log(`[${HOOK_NAME11}] Auto-retrying with fallback model (${source})`, {
62939
63008
  sessionID,
62940
63009
  model: newModel
62941
63010
  });
@@ -62954,10 +63023,10 @@ function createAutoRetryHelpers(deps) {
62954
63023
  });
62955
63024
  retryDispatched = true;
62956
63025
  } else {
62957
- log(`[${HOOK_NAME10}] No user message found for auto-retry (${source})`, { sessionID });
63026
+ log(`[${HOOK_NAME11}] No user message found for auto-retry (${source})`, { sessionID });
62958
63027
  }
62959
63028
  } catch (retryError) {
62960
- log(`[${HOOK_NAME10}] Auto-retry failed (${source})`, { sessionID, error: String(retryError) });
63029
+ log(`[${HOOK_NAME11}] Auto-retry failed (${source})`, { sessionID, error: String(retryError) });
62961
63030
  } finally {
62962
63031
  sessionRetryInFlight.delete(sessionID);
62963
63032
  if (!retryDispatched) {
@@ -63011,7 +63080,7 @@ function createAutoRetryHelpers(deps) {
63011
63080
  }
63012
63081
  }
63013
63082
  if (cleanedCount > 0) {
63014
- log(`[${HOOK_NAME10}] Cleaned up ${cleanedCount} stale session states`);
63083
+ log(`[${HOOK_NAME11}] Cleaned up ${cleanedCount} stale session states`);
63015
63084
  }
63016
63085
  };
63017
63086
  return {
@@ -63042,7 +63111,7 @@ function createChatMessageHandler(deps) {
63042
63111
  state3.pendingFallbackModel = undefined;
63043
63112
  return;
63044
63113
  }
63045
- log(`[${HOOK_NAME10}] Detected manual model change, resetting fallback state`, {
63114
+ log(`[${HOOK_NAME11}] Detected manual model change, resetting fallback state`, {
63046
63115
  sessionID,
63047
63116
  from: state3.currentModel,
63048
63117
  to: requestedModel
@@ -63054,7 +63123,7 @@ function createChatMessageHandler(deps) {
63054
63123
  if (state3.currentModel === state3.originalModel)
63055
63124
  return;
63056
63125
  const activeModel = state3.currentModel;
63057
- log(`[${HOOK_NAME10}] Applying fallback model override`, {
63126
+ log(`[${HOOK_NAME11}] Applying fallback model override`, {
63058
63127
  sessionID,
63059
63128
  from: input.model,
63060
63129
  to: activeModel
@@ -63241,7 +63310,7 @@ function resolveFallbackBootstrapModel(options) {
63241
63310
  const agentConfig = options.resolvedAgent && agentConfigs ? agentConfigs[options.resolvedAgent] : undefined;
63242
63311
  const agentModel = typeof agentConfig?.model === "string" ? agentConfig.model : undefined;
63243
63312
  if (agentModel) {
63244
- log(`[${HOOK_NAME10}] Derived model from agent config for ${options.source}`, {
63313
+ log(`[${HOOK_NAME11}] Derived model from agent config for ${options.source}`, {
63245
63314
  sessionID: options.sessionID,
63246
63315
  agent: options.resolvedAgent,
63247
63316
  model: agentModel
@@ -63252,7 +63321,7 @@ function resolveFallbackBootstrapModel(options) {
63252
63321
  if (agentCategory) {
63253
63322
  const agentCategoryModel = options.pluginConfig?.categories?.[agentCategory]?.model;
63254
63323
  if (typeof agentCategoryModel === "string" && agentCategoryModel.length > 0) {
63255
- log(`[${HOOK_NAME10}] Derived model from agent category config for ${options.source}`, {
63324
+ log(`[${HOOK_NAME11}] Derived model from agent category config for ${options.source}`, {
63256
63325
  sessionID: options.sessionID,
63257
63326
  agent: options.resolvedAgent,
63258
63327
  category: agentCategory,
@@ -63264,7 +63333,7 @@ function resolveFallbackBootstrapModel(options) {
63264
63333
  const sessionCategory = SessionCategoryRegistry.get(options.sessionID);
63265
63334
  const categoryModel = sessionCategory ? options.pluginConfig?.categories?.[sessionCategory]?.model : undefined;
63266
63335
  if (typeof categoryModel === "string" && categoryModel.length > 0) {
63267
- log(`[${HOOK_NAME10}] Derived model from session category config for ${options.source}`, {
63336
+ log(`[${HOOK_NAME11}] Derived model from session category config for ${options.source}`, {
63268
63337
  sessionID: options.sessionID,
63269
63338
  category: sessionCategory,
63270
63339
  model: categoryModel
@@ -63294,7 +63363,7 @@ async function dispatchFallbackRetry(deps, helpers, options) {
63294
63363
  await helpers.autoRetryWithFallback(options.sessionID, result.newModel, options.resolvedAgent, options.source);
63295
63364
  return;
63296
63365
  }
63297
- log(`[${HOOK_NAME10}] Fallback preparation failed`, {
63366
+ log(`[${HOOK_NAME11}] Fallback preparation failed`, {
63298
63367
  sessionID: options.sessionID,
63299
63368
  source: options.source,
63300
63369
  error: result.error
@@ -63347,14 +63416,14 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
63347
63416
  sessionStatusRetryKeys.set(sessionID, retryKey);
63348
63417
  if (sessionRetryInFlight.has(sessionID)) {
63349
63418
  if (timeoutEnabled) {
63350
- log(`[${HOOK_NAME10}] Overriding in-flight retry due to provider auto-retry signal`, {
63419
+ log(`[${HOOK_NAME11}] Overriding in-flight retry due to provider auto-retry signal`, {
63351
63420
  sessionID,
63352
63421
  model
63353
63422
  });
63354
63423
  await helpers.abortSessionRequest(sessionID, "session.status.retry-signal");
63355
63424
  sessionRetryInFlight.delete(sessionID);
63356
63425
  } else {
63357
- log(`[${HOOK_NAME10}] session.status retry skipped - retry already in flight`, { sessionID });
63426
+ log(`[${HOOK_NAME11}] session.status retry skipped - retry already in flight`, { sessionID });
63358
63427
  return;
63359
63428
  }
63360
63429
  }
@@ -63377,7 +63446,7 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
63377
63446
  });
63378
63447
  if (!initialModel) {
63379
63448
  sessionStatusRetryKeys.delete(sessionID);
63380
- log(`[${HOOK_NAME10}] session.status retry missing model info, cannot fallback`, { sessionID });
63449
+ log(`[${HOOK_NAME11}] session.status retry missing model info, cannot fallback`, { sessionID });
63381
63450
  return;
63382
63451
  }
63383
63452
  state3 = createFallbackState(initialModel);
@@ -63386,20 +63455,20 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
63386
63455
  sessionLastAccess.set(sessionID, Date.now());
63387
63456
  if (state3.pendingFallbackModel) {
63388
63457
  if (timeoutEnabled) {
63389
- log(`[${HOOK_NAME10}] Clearing pending fallback due to provider auto-retry signal`, {
63458
+ log(`[${HOOK_NAME11}] Clearing pending fallback due to provider auto-retry signal`, {
63390
63459
  sessionID,
63391
63460
  pendingFallbackModel: state3.pendingFallbackModel
63392
63461
  });
63393
63462
  state3.pendingFallbackModel = undefined;
63394
63463
  } else {
63395
- log(`[${HOOK_NAME10}] session.status retry skipped (pending fallback in progress)`, {
63464
+ log(`[${HOOK_NAME11}] session.status retry skipped (pending fallback in progress)`, {
63396
63465
  sessionID,
63397
63466
  pendingFallbackModel: state3.pendingFallbackModel
63398
63467
  });
63399
63468
  return;
63400
63469
  }
63401
63470
  }
63402
- log(`[${HOOK_NAME10}] Detected provider auto-retry signal in session.status`, {
63471
+ log(`[${HOOK_NAME11}] Detected provider auto-retry signal in session.status`, {
63403
63472
  sessionID,
63404
63473
  model: state3.currentModel,
63405
63474
  retryAttempt: status.attempt
@@ -63435,7 +63504,7 @@ function createEventHandler(deps, helpers) {
63435
63504
  const sessionID = sessionInfo?.id;
63436
63505
  const model = sessionInfo?.model;
63437
63506
  if (sessionID && model) {
63438
- log(`[${HOOK_NAME10}] Session created with model`, { sessionID, model });
63507
+ log(`[${HOOK_NAME11}] Session created with model`, { sessionID, model });
63439
63508
  sessionStates2.set(sessionID, createFallbackState(model));
63440
63509
  sessionLastAccess.set(sessionID, Date.now());
63441
63510
  }
@@ -63444,7 +63513,7 @@ function createEventHandler(deps, helpers) {
63444
63513
  const sessionInfo = props?.info;
63445
63514
  const sessionID = sessionInfo?.id;
63446
63515
  if (sessionID) {
63447
- log(`[${HOOK_NAME10}] Cleaning up session state`, { sessionID });
63516
+ log(`[${HOOK_NAME11}] Cleaning up session state`, { sessionID });
63448
63517
  cancelledSessions.delete(sessionID);
63449
63518
  sessionStates2.delete(sessionID);
63450
63519
  sessionLastAccess.delete(sessionID);
@@ -63464,7 +63533,7 @@ function createEventHandler(deps, helpers) {
63464
63533
  }
63465
63534
  cancelledSessions.add(sessionID);
63466
63535
  resetRetryState(sessionID);
63467
- log(`[${HOOK_NAME10}] Cleared fallback retry state on session.stop`, { sessionID });
63536
+ log(`[${HOOK_NAME11}] Cleared fallback retry state on session.stop`, { sessionID });
63468
63537
  };
63469
63538
  const handleMessageUpdated = (props) => {
63470
63539
  const info = props?.info;
@@ -63480,11 +63549,11 @@ function createEventHandler(deps, helpers) {
63480
63549
  return;
63481
63550
  if (cancelledSessions.has(sessionID)) {
63482
63551
  resetRetryState(sessionID);
63483
- log(`[${HOOK_NAME10}] Cleared fallback retry state for cancelled session on idle`, { sessionID });
63552
+ log(`[${HOOK_NAME11}] Cleared fallback retry state for cancelled session on idle`, { sessionID });
63484
63553
  return;
63485
63554
  }
63486
63555
  if (sessionAwaitingFallbackResult.has(sessionID)) {
63487
- log(`[${HOOK_NAME10}] session.idle while awaiting fallback result; keeping timeout armed`, { sessionID });
63556
+ log(`[${HOOK_NAME11}] session.idle while awaiting fallback result; keeping timeout armed`, { sessionID });
63488
63557
  return;
63489
63558
  }
63490
63559
  const hadTimeout = sessionFallbackTimeouts.has(sessionID);
@@ -63496,7 +63565,7 @@ function createEventHandler(deps, helpers) {
63496
63565
  state3.pendingFallbackModel = undefined;
63497
63566
  }
63498
63567
  if (hadTimeout) {
63499
- log(`[${HOOK_NAME10}] Cleared fallback timeout after session completion`, { sessionID });
63568
+ log(`[${HOOK_NAME11}] Cleared fallback timeout after session completion`, { sessionID });
63500
63569
  }
63501
63570
  };
63502
63571
  const handleSessionError = async (props) => {
@@ -63504,18 +63573,18 @@ function createEventHandler(deps, helpers) {
63504
63573
  const error51 = props?.error;
63505
63574
  const agent = props?.agent;
63506
63575
  if (!sessionID) {
63507
- log(`[${HOOK_NAME10}] session.error without sessionID, skipping`);
63576
+ log(`[${HOOK_NAME11}] session.error without sessionID, skipping`);
63508
63577
  return;
63509
63578
  }
63510
63579
  const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
63511
63580
  if (isAbortError(error51)) {
63512
63581
  cancelledSessions.add(sessionID);
63513
63582
  resetRetryState(sessionID);
63514
- log(`[${HOOK_NAME10}] session.error matched cancellation; cleared retry state`, { sessionID, resolvedAgent });
63583
+ log(`[${HOOK_NAME11}] session.error matched cancellation; cleared retry state`, { sessionID, resolvedAgent });
63515
63584
  return;
63516
63585
  }
63517
63586
  if (sessionRetryInFlight.has(sessionID)) {
63518
- log(`[${HOOK_NAME10}] session.error skipped - retry in flight`, {
63587
+ log(`[${HOOK_NAME11}] session.error skipped - retry in flight`, {
63519
63588
  sessionID,
63520
63589
  retryInFlight: true
63521
63590
  });
@@ -63523,7 +63592,7 @@ function createEventHandler(deps, helpers) {
63523
63592
  }
63524
63593
  sessionAwaitingFallbackResult.delete(sessionID);
63525
63594
  helpers.clearSessionFallbackTimeout(sessionID);
63526
- log(`[${HOOK_NAME10}] session.error received`, {
63595
+ log(`[${HOOK_NAME11}] session.error received`, {
63527
63596
  sessionID,
63528
63597
  agent,
63529
63598
  resolvedAgent,
@@ -63532,7 +63601,7 @@ function createEventHandler(deps, helpers) {
63532
63601
  errorType: classifyErrorType(error51)
63533
63602
  });
63534
63603
  if (!isRetryableError(error51, config2.retry_on_errors)) {
63535
- log(`[${HOOK_NAME10}] Error not retryable, skipping fallback`, {
63604
+ log(`[${HOOK_NAME11}] Error not retryable, skipping fallback`, {
63536
63605
  sessionID,
63537
63606
  retryable: false,
63538
63607
  statusCode: extractStatusCode(error51, config2.retry_on_errors),
@@ -63544,7 +63613,7 @@ function createEventHandler(deps, helpers) {
63544
63613
  let state3 = sessionStates2.get(sessionID);
63545
63614
  const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
63546
63615
  if (fallbackModels.length === 0) {
63547
- log(`[${HOOK_NAME10}] No fallback models configured`, { sessionID, agent });
63616
+ log(`[${HOOK_NAME11}] No fallback models configured`, { sessionID, agent });
63548
63617
  return;
63549
63618
  }
63550
63619
  if (!state3) {
@@ -63556,7 +63625,7 @@ function createEventHandler(deps, helpers) {
63556
63625
  pluginConfig
63557
63626
  });
63558
63627
  if (!initialModel) {
63559
- log(`[${HOOK_NAME10}] No model info available, cannot fallback`, { sessionID });
63628
+ log(`[${HOOK_NAME11}] No model info available, cannot fallback`, { sessionID });
63560
63629
  return;
63561
63630
  }
63562
63631
  state3 = createFallbackState(initialModel);
@@ -63696,7 +63765,7 @@ function createMessageUpdateHandler(deps, helpers) {
63696
63765
  }
63697
63766
  const hasVisible = await checkVisibleResponse(ctx, sessionID, info);
63698
63767
  if (!hasVisible) {
63699
- log(`[${HOOK_NAME10}] Assistant update observed without visible final response; keeping fallback timeout`, {
63768
+ log(`[${HOOK_NAME11}] Assistant update observed without visible final response; keeping fallback timeout`, {
63700
63769
  sessionID,
63701
63770
  model
63702
63771
  });
@@ -63709,17 +63778,17 @@ function createMessageUpdateHandler(deps, helpers) {
63709
63778
  if (state3?.pendingFallbackModel) {
63710
63779
  state3.pendingFallbackModel = undefined;
63711
63780
  }
63712
- log(`[${HOOK_NAME10}] Assistant response observed; cleared fallback timeout`, { sessionID, model });
63781
+ log(`[${HOOK_NAME11}] Assistant response observed; cleared fallback timeout`, { sessionID, model });
63713
63782
  return;
63714
63783
  }
63715
63784
  if (sessionID && role === "assistant" && error51) {
63716
63785
  sessionAwaitingFallbackResult.delete(sessionID);
63717
63786
  if (sessionRetryInFlight.has(sessionID) && !retrySignal) {
63718
- log(`[${HOOK_NAME10}] message.updated fallback skipped (retry in flight)`, { sessionID });
63787
+ log(`[${HOOK_NAME11}] message.updated fallback skipped (retry in flight)`, { sessionID });
63719
63788
  return;
63720
63789
  }
63721
63790
  if (retrySignal && sessionRetryInFlight.has(sessionID) && timeoutEnabled) {
63722
- log(`[${HOOK_NAME10}] Overriding in-flight retry due to provider auto-retry signal`, {
63791
+ log(`[${HOOK_NAME11}] Overriding in-flight retry due to provider auto-retry signal`, {
63723
63792
  sessionID,
63724
63793
  model
63725
63794
  });
@@ -63727,12 +63796,12 @@ function createMessageUpdateHandler(deps, helpers) {
63727
63796
  sessionRetryInFlight.delete(sessionID);
63728
63797
  }
63729
63798
  if (retrySignal && timeoutEnabled) {
63730
- log(`[${HOOK_NAME10}] Detected provider auto-retry signal`, { sessionID, model });
63799
+ log(`[${HOOK_NAME11}] Detected provider auto-retry signal`, { sessionID, model });
63731
63800
  }
63732
63801
  if (!retrySignal) {
63733
63802
  helpers.clearSessionFallbackTimeout(sessionID);
63734
63803
  }
63735
- log(`[${HOOK_NAME10}] message.updated with assistant error`, {
63804
+ log(`[${HOOK_NAME11}] message.updated with assistant error`, {
63736
63805
  sessionID,
63737
63806
  model,
63738
63807
  statusCode: extractStatusCode(error51, config2.retry_on_errors),
@@ -63740,7 +63809,7 @@ function createMessageUpdateHandler(deps, helpers) {
63740
63809
  errorType: classifyErrorType(error51)
63741
63810
  });
63742
63811
  if (!isRetryableError(error51, config2.retry_on_errors)) {
63743
- log(`[${HOOK_NAME10}] message.updated error not retryable, skipping fallback`, {
63812
+ log(`[${HOOK_NAME11}] message.updated error not retryable, skipping fallback`, {
63744
63813
  sessionID,
63745
63814
  statusCode: extractStatusCode(error51, config2.retry_on_errors),
63746
63815
  errorName: extractErrorName(error51),
@@ -63764,7 +63833,7 @@ function createMessageUpdateHandler(deps, helpers) {
63764
63833
  pluginConfig
63765
63834
  });
63766
63835
  if (!initialModel) {
63767
- log(`[${HOOK_NAME10}] message.updated missing model info, cannot fallback`, {
63836
+ log(`[${HOOK_NAME11}] message.updated missing model info, cannot fallback`, {
63768
63837
  sessionID,
63769
63838
  errorName: extractErrorName(error51),
63770
63839
  errorType: classifyErrorType(error51)
@@ -63778,13 +63847,13 @@ function createMessageUpdateHandler(deps, helpers) {
63778
63847
  sessionLastAccess.set(sessionID, Date.now());
63779
63848
  if (state3.pendingFallbackModel) {
63780
63849
  if (retrySignal && timeoutEnabled) {
63781
- log(`[${HOOK_NAME10}] Clearing pending fallback due to provider auto-retry signal`, {
63850
+ log(`[${HOOK_NAME11}] Clearing pending fallback due to provider auto-retry signal`, {
63782
63851
  sessionID,
63783
63852
  pendingFallbackModel: state3.pendingFallbackModel
63784
63853
  });
63785
63854
  state3.pendingFallbackModel = undefined;
63786
63855
  } else {
63787
- log(`[${HOOK_NAME10}] message.updated fallback skipped (pending fallback in progress)`, {
63856
+ log(`[${HOOK_NAME11}] message.updated fallback skipped (pending fallback in progress)`, {
63788
63857
  sessionID,
63789
63858
  pendingFallbackModel: state3.pendingFallbackModel
63790
63859
  });
@@ -64790,7 +64859,7 @@ function createSessionRecoveryHook(ctx, options) {
64790
64859
  }
64791
64860
  // src/hooks/start-work/start-work-hook.ts
64792
64861
  init_logger();
64793
- var HOOK_NAME11 = "start-work";
64862
+ var HOOK_NAME12 = "start-work";
64794
64863
  var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
64795
64864
  function extractUserRequestPlanName(promptText) {
64796
64865
  const userRequestMatch = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
@@ -64820,7 +64889,7 @@ function createStartWorkHook(ctx) {
64820
64889
  if (!isStartWorkCommand) {
64821
64890
  return;
64822
64891
  }
64823
- log(`[${HOOK_NAME11}] Processing start-work command`, {
64892
+ log(`[${HOOK_NAME12}] Processing start-work command`, {
64824
64893
  sessionID: input.sessionID
64825
64894
  });
64826
64895
  updateSessionAgent(input.sessionID, "architect");
@@ -64830,7 +64899,7 @@ function createStartWorkHook(ctx) {
64830
64899
  let contextInfo = "";
64831
64900
  const explicitPlanName = extractUserRequestPlanName(promptText);
64832
64901
  if (explicitPlanName) {
64833
- log(`[${HOOK_NAME11}] Explicit plan name requested: ${explicitPlanName}`, {
64902
+ log(`[${HOOK_NAME12}] Explicit plan name requested: ${explicitPlanName}`, {
64834
64903
  sessionID: input.sessionID
64835
64904
  });
64836
64905
  const allPlans = findOraclePlans(ctx.directory);
@@ -64971,7 +65040,7 @@ Ask the user which plan to work on. Present the options above and wait for their
64971
65040
  ---
64972
65041
  ${contextInfo}`;
64973
65042
  }
64974
- log(`[${HOOK_NAME11}] Context injected`, {
65043
+ log(`[${HOOK_NAME12}] Context injected`, {
64975
65044
  sessionID: input.sessionID,
64976
65045
  hasExistingState: !!existingState
64977
65046
  });
@@ -64980,16 +65049,16 @@ ${contextInfo}`;
64980
65049
  }
64981
65050
  // src/hooks/stop-continuation-guard/hook.ts
64982
65051
  init_logger();
64983
- var HOOK_NAME12 = "stop-continuation-guard";
65052
+ var HOOK_NAME13 = "stop-continuation-guard";
64984
65053
  function createStopContinuationGuardHook(_ctx, options) {
64985
65054
  const stoppedSessions = new Set;
64986
65055
  const stop = (sessionID) => {
64987
65056
  stoppedSessions.add(sessionID);
64988
- log(`[${HOOK_NAME12}] Continuation stopped for session`, { sessionID });
65057
+ log(`[${HOOK_NAME13}] Continuation stopped for session`, { sessionID });
64989
65058
  if (options?.backgroundManager) {
64990
65059
  const cancelled = options.backgroundManager.cancelAllForSession(sessionID);
64991
65060
  if (cancelled > 0) {
64992
- log(`[${HOOK_NAME12}] Cancelled ${cancelled} background task(s) for session`, { sessionID });
65061
+ log(`[${HOOK_NAME13}] Cancelled ${cancelled} background task(s) for session`, { sessionID });
64993
65062
  }
64994
65063
  }
64995
65064
  };
@@ -64998,7 +65067,7 @@ function createStopContinuationGuardHook(_ctx, options) {
64998
65067
  };
64999
65068
  const clear = (sessionID) => {
65000
65069
  stoppedSessions.delete(sessionID);
65001
- log(`[${HOOK_NAME12}] Continuation guard cleared for session`, { sessionID });
65070
+ log(`[${HOOK_NAME13}] Continuation guard cleared for session`, { sessionID });
65002
65071
  };
65003
65072
  const event = async ({
65004
65073
  event: event2
@@ -65008,7 +65077,7 @@ function createStopContinuationGuardHook(_ctx, options) {
65008
65077
  const sessionInfo = props?.info;
65009
65078
  if (sessionInfo?.id) {
65010
65079
  clear(sessionInfo.id);
65011
- log(`[${HOOK_NAME12}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
65080
+ log(`[${HOOK_NAME13}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
65012
65081
  }
65013
65082
  }
65014
65083
  };
@@ -65017,7 +65086,7 @@ function createStopContinuationGuardHook(_ctx, options) {
65017
65086
  }) => {
65018
65087
  if (sessionID && stoppedSessions.has(sessionID)) {
65019
65088
  clear(sessionID);
65020
- log(`[${HOOK_NAME12}] Cleared stop state on new user message`, { sessionID });
65089
+ log(`[${HOOK_NAME13}] Cleared stop state on new user message`, { sessionID });
65021
65090
  }
65022
65091
  };
65023
65092
  return {
@@ -65032,7 +65101,7 @@ function createStopContinuationGuardHook(_ctx, options) {
65032
65101
  init_logger();
65033
65102
 
65034
65103
  // src/hooks/task-continuation-enforcer/constants.ts
65035
- var HOOK_NAME13 = "task-continuation-enforcer";
65104
+ var HOOK_NAME14 = "task-continuation-enforcer";
65036
65105
  var DEFAULT_SKIP_AGENTS = ["oracle", "compaction"];
65037
65106
  var CONTINUATION_PROMPT2 = `${createSystemDirective(SystemDirectiveTypes.TASK_CONTINUATION)}
65038
65107
 
@@ -65352,12 +65421,12 @@ async function injectContinuation(args) {
65352
65421
  } = args;
65353
65422
  const state3 = sessionStateStore.getExistingState(sessionID);
65354
65423
  if (state3?.isRecovering) {
65355
- log(`[${HOOK_NAME13}] Skipped injection: in recovery`, { sessionID });
65424
+ log(`[${HOOK_NAME14}] Skipped injection: in recovery`, { sessionID });
65356
65425
  return;
65357
65426
  }
65358
65427
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
65359
65428
  if (hasRunningBgTasks) {
65360
- log(`[${HOOK_NAME13}] Skipped injection: background tasks running`, { sessionID });
65429
+ log(`[${HOOK_NAME14}] Skipped injection: background tasks running`, { sessionID });
65361
65430
  return;
65362
65431
  }
65363
65432
  const tasks = [];
@@ -65365,7 +65434,7 @@ async function injectContinuation(args) {
65365
65434
  try {
65366
65435
  const taskDir = getTaskDir({}, ctx.directory);
65367
65436
  if (!existsSync43(taskDir)) {
65368
- log(`[${HOOK_NAME13}] Skipped injection: no task dir`, { sessionID, taskDir });
65437
+ log(`[${HOOK_NAME14}] Skipped injection: no task dir`, { sessionID, taskDir });
65369
65438
  return;
65370
65439
  }
65371
65440
  const files = readdirSync14(taskDir).filter((f) => f.startsWith("T-") && f.endsWith(".json"));
@@ -65376,16 +65445,16 @@ async function injectContinuation(args) {
65376
65445
  }
65377
65446
  total = tasks.length;
65378
65447
  if (total === 0) {
65379
- log(`[${HOOK_NAME13}] Skipped injection: no tasks`, { sessionID });
65448
+ log(`[${HOOK_NAME14}] Skipped injection: no tasks`, { sessionID });
65380
65449
  return;
65381
65450
  }
65382
65451
  } catch (error51) {
65383
- log(`[${HOOK_NAME13}] Failed to fetch tasks`, { sessionID, error: String(error51) });
65452
+ log(`[${HOOK_NAME14}] Failed to fetch tasks`, { sessionID, error: String(error51) });
65384
65453
  return;
65385
65454
  }
65386
65455
  const freshIncompleteCount = getIncompleteTaskCount(tasks);
65387
65456
  if (freshIncompleteCount === 0) {
65388
- log(`[${HOOK_NAME13}] Skipped injection: no incomplete tasks`, { sessionID, total });
65457
+ log(`[${HOOK_NAME14}] Skipped injection: no incomplete tasks`, { sessionID, total });
65389
65458
  return;
65390
65459
  }
65391
65460
  let agentName = resolvedInfo?.agent;
@@ -65408,11 +65477,11 @@ async function injectContinuation(args) {
65408
65477
  tools = tools ?? previousMessage?.tools;
65409
65478
  }
65410
65479
  if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
65411
- log(`[${HOOK_NAME13}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
65480
+ log(`[${HOOK_NAME14}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
65412
65481
  return;
65413
65482
  }
65414
65483
  if (!hasWritePermission(tools)) {
65415
- log(`[${HOOK_NAME13}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
65484
+ log(`[${HOOK_NAME14}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
65416
65485
  return;
65417
65486
  }
65418
65487
  const byId = new Map(tasks.map((t) => [t.id, t]));
@@ -65436,7 +65505,7 @@ ${taskList}`;
65436
65505
  injectionState.inFlight = true;
65437
65506
  }
65438
65507
  try {
65439
- log(`[${HOOK_NAME13}] Injecting continuation`, {
65508
+ log(`[${HOOK_NAME14}] Injecting continuation`, {
65440
65509
  sessionID,
65441
65510
  agent: agentName,
65442
65511
  model,
@@ -65451,14 +65520,14 @@ ${taskList}`;
65451
65520
  },
65452
65521
  query: { directory: ctx.directory }
65453
65522
  });
65454
- log(`[${HOOK_NAME13}] Injection successful`, { sessionID });
65523
+ log(`[${HOOK_NAME14}] Injection successful`, { sessionID });
65455
65524
  if (injectionState) {
65456
65525
  injectionState.inFlight = false;
65457
65526
  injectionState.lastInjectedAt = Date.now();
65458
65527
  injectionState.consecutiveFailures = 0;
65459
65528
  }
65460
65529
  } catch (error51) {
65461
- log(`[${HOOK_NAME13}] Injection failed`, { sessionID, error: String(error51) });
65530
+ log(`[${HOOK_NAME14}] Injection failed`, { sessionID, error: String(error51) });
65462
65531
  if (injectionState) {
65463
65532
  injectionState.inFlight = false;
65464
65533
  injectionState.lastInjectedAt = Date.now();
@@ -65491,7 +65560,10 @@ function startCountdown(args) {
65491
65560
  sessionStateStore
65492
65561
  } = args;
65493
65562
  const state3 = sessionStateStore.getState(sessionID);
65494
- sessionStateStore.cancelCountdown(sessionID);
65563
+ if (state3.countdownTimer || state3.countdownInterval) {
65564
+ log(`[${HOOK_NAME14}] Skipped: countdown already active`, { sessionID });
65565
+ return;
65566
+ }
65495
65567
  let secondsRemaining = COUNTDOWN_SECONDS;
65496
65568
  showCountdownToast(ctx, secondsRemaining, incompleteCount);
65497
65569
  state3.countdownStartedAt = Date.now();
@@ -65512,7 +65584,7 @@ function startCountdown(args) {
65512
65584
  sessionStateStore
65513
65585
  });
65514
65586
  }, COUNTDOWN_SECONDS * 1000);
65515
- log(`[${HOOK_NAME13}] Countdown started`, {
65587
+ log(`[${HOOK_NAME14}] Countdown started`, {
65516
65588
  sessionID,
65517
65589
  seconds: COUNTDOWN_SECONDS,
65518
65590
  incompleteCount
@@ -65529,16 +65601,20 @@ async function handleSessionIdle(args) {
65529
65601
  skipAgents = DEFAULT_SKIP_AGENTS,
65530
65602
  isContinuationStopped
65531
65603
  } = args;
65532
- log(`[${HOOK_NAME13}] session.idle`, { sessionID });
65604
+ log(`[${HOOK_NAME14}] session.idle`, { sessionID });
65533
65605
  const state3 = sessionStateStore.getState(sessionID);
65606
+ if (state3.countdownTimer || state3.countdownInterval) {
65607
+ log(`[${HOOK_NAME14}] Skipped: countdown already active`, { sessionID });
65608
+ return;
65609
+ }
65534
65610
  if (state3.isRecovering) {
65535
- log(`[${HOOK_NAME13}] Skipped: in recovery`, { sessionID });
65611
+ log(`[${HOOK_NAME14}] Skipped: in recovery`, { sessionID });
65536
65612
  return;
65537
65613
  }
65538
65614
  if (state3.abortDetectedAt) {
65539
65615
  const timeSinceAbort = Date.now() - state3.abortDetectedAt;
65540
65616
  if (timeSinceAbort < ABORT_WINDOW_MS) {
65541
- log(`[${HOOK_NAME13}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
65617
+ log(`[${HOOK_NAME14}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
65542
65618
  state3.abortDetectedAt = undefined;
65543
65619
  return;
65544
65620
  }
@@ -65546,7 +65622,7 @@ async function handleSessionIdle(args) {
65546
65622
  }
65547
65623
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
65548
65624
  if (hasRunningBgTasks) {
65549
- log(`[${HOOK_NAME13}] Skipped: background tasks running`, { sessionID });
65625
+ log(`[${HOOK_NAME14}] Skipped: background tasks running`, { sessionID });
65550
65626
  return;
65551
65627
  }
65552
65628
  try {
@@ -65556,18 +65632,18 @@ async function handleSessionIdle(args) {
65556
65632
  });
65557
65633
  const messages = normalizeSDKResponse(messagesResp, []);
65558
65634
  if (isLastAssistantMessageAborted(messages)) {
65559
- log(`[${HOOK_NAME13}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
65635
+ log(`[${HOOK_NAME14}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
65560
65636
  return;
65561
65637
  }
65562
65638
  } catch (error51) {
65563
- log(`[${HOOK_NAME13}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
65639
+ log(`[${HOOK_NAME14}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
65564
65640
  }
65565
65641
  let incompleteCount = 0;
65566
65642
  let total = 0;
65567
65643
  try {
65568
65644
  const taskDir = getTaskDir({}, ctx.directory);
65569
65645
  if (!existsSync44(taskDir)) {
65570
- log(`[${HOOK_NAME13}] No task dir`, { sessionID, taskDir });
65646
+ log(`[${HOOK_NAME14}] No task dir`, { sessionID, taskDir });
65571
65647
  return;
65572
65648
  }
65573
65649
  const files = readdirSync15(taskDir).filter((f) => f.startsWith("T-") && f.endsWith(".json"));
@@ -65579,31 +65655,31 @@ async function handleSessionIdle(args) {
65579
65655
  }
65580
65656
  total = tasks.length;
65581
65657
  if (total === 0) {
65582
- log(`[${HOOK_NAME13}] No tasks`, { sessionID });
65658
+ log(`[${HOOK_NAME14}] No tasks`, { sessionID });
65583
65659
  return;
65584
65660
  }
65585
65661
  incompleteCount = getIncompleteTaskCount(tasks);
65586
65662
  } catch (error51) {
65587
- log(`[${HOOK_NAME13}] Task fetch failed`, { sessionID, error: String(error51) });
65663
+ log(`[${HOOK_NAME14}] Task fetch failed`, { sessionID, error: String(error51) });
65588
65664
  return;
65589
65665
  }
65590
65666
  if (incompleteCount === 0) {
65591
- log(`[${HOOK_NAME13}] All tasks complete`, { sessionID, total });
65667
+ log(`[${HOOK_NAME14}] All tasks complete`, { sessionID, total });
65592
65668
  return;
65593
65669
  }
65594
65670
  if (state3.inFlight) {
65595
- log(`[${HOOK_NAME13}] Skipped: injection in flight`, { sessionID });
65671
+ log(`[${HOOK_NAME14}] Skipped: injection in flight`, { sessionID });
65596
65672
  return;
65597
65673
  }
65598
65674
  if ((state3.consecutiveFailures ?? 0) >= MAX_CONSECUTIVE_FAILURES && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS) {
65599
65675
  state3.consecutiveFailures = 0;
65600
- log(`[${HOOK_NAME13}] Reset consecutive failures after recovery window`, {
65676
+ log(`[${HOOK_NAME14}] Reset consecutive failures after recovery window`, {
65601
65677
  sessionID,
65602
65678
  failureResetWindowMs: FAILURE_RESET_WINDOW_MS
65603
65679
  });
65604
65680
  }
65605
65681
  if ((state3.consecutiveFailures ?? 0) >= MAX_CONSECUTIVE_FAILURES) {
65606
- log(`[${HOOK_NAME13}] Skipped: max consecutive failures reached`, {
65682
+ log(`[${HOOK_NAME14}] Skipped: max consecutive failures reached`, {
65607
65683
  sessionID,
65608
65684
  consecutiveFailures: state3.consecutiveFailures ?? 0,
65609
65685
  maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES
@@ -65612,7 +65688,7 @@ async function handleSessionIdle(args) {
65612
65688
  }
65613
65689
  const effectiveCooldown = CONTINUATION_COOLDOWN_MS2 * 2 ** Math.min(state3.consecutiveFailures ?? 0, 5);
65614
65690
  if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
65615
- log(`[${HOOK_NAME13}] Skipped: cooldown active`, {
65691
+ log(`[${HOOK_NAME14}] Skipped: cooldown active`, {
65616
65692
  sessionID,
65617
65693
  effectiveCooldown,
65618
65694
  consecutiveFailures: state3.consecutiveFailures ?? 0
@@ -65642,20 +65718,20 @@ async function handleSessionIdle(args) {
65642
65718
  }
65643
65719
  }
65644
65720
  } catch (error51) {
65645
- log(`[${HOOK_NAME13}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
65721
+ log(`[${HOOK_NAME14}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
65646
65722
  }
65647
- log(`[${HOOK_NAME13}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
65723
+ log(`[${HOOK_NAME14}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
65648
65724
  const resolvedAgentName = resolvedInfo?.agent;
65649
65725
  if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
65650
- log(`[${HOOK_NAME13}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
65726
+ log(`[${HOOK_NAME14}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
65651
65727
  return;
65652
65728
  }
65653
65729
  if (hasCompactionMessage && !resolvedInfo?.agent) {
65654
- log(`[${HOOK_NAME13}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
65730
+ log(`[${HOOK_NAME14}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
65655
65731
  return;
65656
65732
  }
65657
65733
  if (isContinuationStopped?.(sessionID)) {
65658
- log(`[${HOOK_NAME13}] Skipped: continuation stopped for session`, { sessionID });
65734
+ log(`[${HOOK_NAME14}] Skipped: continuation stopped for session`, { sessionID });
65659
65735
  return;
65660
65736
  }
65661
65737
  startCountdown({
@@ -65685,7 +65761,7 @@ function handleNonIdleEvent(args) {
65685
65761
  if (state3?.countdownStartedAt) {
65686
65762
  const elapsed = Date.now() - state3.countdownStartedAt;
65687
65763
  if (elapsed < COUNTDOWN_GRACE_PERIOD_MS) {
65688
- log(`[${HOOK_NAME13}] Ignoring user message in grace period`, { sessionID, elapsed });
65764
+ log(`[${HOOK_NAME14}] Ignoring user message in grace period`, { sessionID, elapsed });
65689
65765
  return;
65690
65766
  }
65691
65767
  }
@@ -65729,7 +65805,7 @@ function handleNonIdleEvent(args) {
65729
65805
  const sessionInfo = properties?.info;
65730
65806
  if (sessionInfo?.id) {
65731
65807
  sessionStateStore.cleanup(sessionInfo.id);
65732
- log(`[${HOOK_NAME13}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
65808
+ log(`[${HOOK_NAME14}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
65733
65809
  }
65734
65810
  return;
65735
65811
  }
@@ -65754,10 +65830,10 @@ function createTaskContinuationHandler(args) {
65754
65830
  if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
65755
65831
  const state3 = sessionStateStore.getState(sessionID);
65756
65832
  state3.abortDetectedAt = Date.now();
65757
- log(`[${HOOK_NAME13}] Abort detected via session.error`, { sessionID, errorName: error51.name });
65833
+ log(`[${HOOK_NAME14}] Abort detected via session.error`, { sessionID, errorName: error51.name });
65758
65834
  }
65759
65835
  sessionStateStore.cancelCountdown(sessionID);
65760
- log(`[${HOOK_NAME13}] session.error`, { sessionID });
65836
+ log(`[${HOOK_NAME14}] session.error`, { sessionID });
65761
65837
  return;
65762
65838
  }
65763
65839
  if (event.type === "session.idle") {
@@ -65872,13 +65948,13 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
65872
65948
  const state3 = sessionStateStore.getState(sessionID);
65873
65949
  state3.isRecovering = true;
65874
65950
  sessionStateStore.cancelCountdown(sessionID);
65875
- log(`[${HOOK_NAME13}] Session marked as recovering`, { sessionID });
65951
+ log(`[${HOOK_NAME14}] Session marked as recovering`, { sessionID });
65876
65952
  };
65877
65953
  const markRecoveryComplete = (sessionID) => {
65878
65954
  const state3 = sessionStateStore.getExistingState(sessionID);
65879
65955
  if (state3) {
65880
65956
  state3.isRecovering = false;
65881
- log(`[${HOOK_NAME13}] Session recovery complete`, { sessionID });
65957
+ log(`[${HOOK_NAME14}] Session recovery complete`, { sessionID });
65882
65958
  }
65883
65959
  };
65884
65960
  const handler = createTaskContinuationHandler({
@@ -65890,7 +65966,7 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
65890
65966
  });
65891
65967
  const cancelAllCountdowns = () => {
65892
65968
  sessionStateStore.cancelAllCountdowns();
65893
- log(`[${HOOK_NAME13}] All countdowns cancelled`);
65969
+ log(`[${HOOK_NAME14}] All countdowns cancelled`);
65894
65970
  };
65895
65971
  return {
65896
65972
  handler,
@@ -65900,7 +65976,7 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
65900
65976
  };
65901
65977
  }
65902
65978
  // src/hooks/task-edit-guard/constants.ts
65903
- var HOOK_NAME14 = "task-edit-guard";
65979
+ var HOOK_NAME15 = "task-edit-guard";
65904
65980
  var BLOCKED_PATTERNS = [
65905
65981
  /sed\s+.*\.matrixx\/plans/,
65906
65982
  /sed\s+.*\.matrixx\/tasks/,
@@ -65936,7 +66012,7 @@ function createTaskEditGuardHook(ctx) {
65936
66012
  if (!hit)
65937
66013
  return;
65938
66014
  ctx.directory;
65939
- log(`[${HOOK_NAME14}] BLOCKED raw bash edit`, {
66015
+ log(`[${HOOK_NAME15}] BLOCKED raw bash edit`, {
65940
66016
  sessionID: input.sessionID,
65941
66017
  command: cmd.slice(0, 120)
65942
66018
  });
@@ -65945,7 +66021,7 @@ function createTaskEditGuardHook(ctx) {
65945
66021
  };
65946
66022
  }
65947
66023
  // src/hooks/task-notepad/constants.ts
65948
- var HOOK_NAME15 = "task-notepad";
66024
+ var HOOK_NAME16 = "task-notepad";
65949
66025
  var PLAN_DIR_NAME = ".matrixx";
65950
66026
  var PLANS_SUBDIR = "plans";
65951
66027
  var NOTEPAD_DIR_NAME = "notepads";
@@ -65974,7 +66050,7 @@ function createTaskNotepadHook(ctx) {
65974
66050
  const plan = resolveActivePlan(ctx.directory);
65975
66051
  if (inProgress) {
65976
66052
  if (!plan) {
65977
- log(`[${HOOK_NAME15}] No active plan, skipping notepad creation`, {
66053
+ log(`[${HOOK_NAME16}] No active plan, skipping notepad creation`, {
65978
66054
  sessionID: input.sessionID
65979
66055
  });
65980
66056
  } else {
@@ -66001,7 +66077,7 @@ async function fetchTodos(ctx, sessionID) {
66001
66077
  }
66002
66078
  return [];
66003
66079
  } catch (err) {
66004
- log(`[${HOOK_NAME15}] ${TODO_FETCH_ERROR_FRAGMENT}`, {
66080
+ log(`[${HOOK_NAME16}] ${TODO_FETCH_ERROR_FRAGMENT}`, {
66005
66081
  sessionID,
66006
66082
  error: String(err)
66007
66083
  });
@@ -66046,7 +66122,7 @@ function ensureNotepad(directory, planName, todo) {
66046
66122
  mkdirSync16(dirname15(notepadPath), { recursive: true });
66047
66123
  const content = renderNotepad(todo);
66048
66124
  writeFileSync19(notepadPath, content, "utf-8");
66049
- log(`[${HOOK_NAME15}] Created task notepad`, { plan: planName, taskID: todo.id });
66125
+ log(`[${HOOK_NAME16}] Created task notepad`, { plan: planName, taskID: todo.id });
66050
66126
  }
66051
66127
  function appendCompletion(directory, planName, todo) {
66052
66128
  const notepadPath = notepadPathFor(directory, planName, todo.id);
@@ -66062,7 +66138,7 @@ function appendCompletion(directory, planName, todo) {
66062
66138
  - status: completed
66063
66139
  `;
66064
66140
  writeFileSync19(notepadPath, updated, "utf-8");
66065
- log(`[${HOOK_NAME15}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
66141
+ log(`[${HOOK_NAME16}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
66066
66142
  }
66067
66143
  function notepadPathFor(directory, planName, taskID) {
66068
66144
  return join52(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
@@ -66655,7 +66731,7 @@ function createThinkingBlockValidatorHook() {
66655
66731
  init_logger();
66656
66732
 
66657
66733
  // src/hooks/todo-continuation-enforcer/constants.ts
66658
- var HOOK_NAME16 = "todo-continuation-enforcer";
66734
+ var HOOK_NAME17 = "todo-continuation-enforcer";
66659
66735
  var DEFAULT_SKIP_AGENTS2 = ["oracle", "compaction"];
66660
66736
  var CONTINUATION_PROMPT3 = `${createSystemDirective(SystemDirectiveTypes.TODO_CONTINUATION)}
66661
66737
 
@@ -66721,12 +66797,12 @@ async function injectContinuation2(args) {
66721
66797
  } = args;
66722
66798
  const state3 = sessionStateStore.getExistingState(sessionID);
66723
66799
  if (state3?.isRecovering) {
66724
- log(`[${HOOK_NAME16}] Skipped injection: in recovery`, { sessionID });
66800
+ log(`[${HOOK_NAME17}] Skipped injection: in recovery`, { sessionID });
66725
66801
  return;
66726
66802
  }
66727
66803
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
66728
66804
  if (hasRunningBgTasks) {
66729
- log(`[${HOOK_NAME16}] Skipped injection: background tasks running`, { sessionID });
66805
+ log(`[${HOOK_NAME17}] Skipped injection: background tasks running`, { sessionID });
66730
66806
  return;
66731
66807
  }
66732
66808
  let todos = [];
@@ -66734,12 +66810,12 @@ async function injectContinuation2(args) {
66734
66810
  const response = await ctx.client.session.todo({ path: { id: sessionID } });
66735
66811
  todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
66736
66812
  } catch (error51) {
66737
- log(`[${HOOK_NAME16}] Failed to fetch todos`, { sessionID, error: String(error51) });
66813
+ log(`[${HOOK_NAME17}] Failed to fetch todos`, { sessionID, error: String(error51) });
66738
66814
  return;
66739
66815
  }
66740
66816
  const freshIncompleteCount = getIncompleteCount(todos);
66741
66817
  if (freshIncompleteCount === 0) {
66742
- log(`[${HOOK_NAME16}] Skipped injection: no incomplete todos`, { sessionID });
66818
+ log(`[${HOOK_NAME17}] Skipped injection: no incomplete todos`, { sessionID });
66743
66819
  return;
66744
66820
  }
66745
66821
  let agentName = resolvedInfo?.agent;
@@ -66762,11 +66838,11 @@ async function injectContinuation2(args) {
66762
66838
  tools = tools ?? previousMessage?.tools;
66763
66839
  }
66764
66840
  if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
66765
- log(`[${HOOK_NAME16}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
66841
+ log(`[${HOOK_NAME17}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
66766
66842
  return;
66767
66843
  }
66768
66844
  if (!hasWritePermission2(tools)) {
66769
- log(`[${HOOK_NAME16}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
66845
+ log(`[${HOOK_NAME17}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
66770
66846
  return;
66771
66847
  }
66772
66848
  const incompleteTodos = todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled");
@@ -66783,7 +66859,7 @@ ${todoList}`;
66783
66859
  injectionState.inFlight = true;
66784
66860
  }
66785
66861
  try {
66786
- log(`[${HOOK_NAME16}] Injecting continuation`, {
66862
+ log(`[${HOOK_NAME17}] Injecting continuation`, {
66787
66863
  sessionID,
66788
66864
  agent: agentName,
66789
66865
  model,
@@ -66798,14 +66874,14 @@ ${todoList}`;
66798
66874
  },
66799
66875
  query: { directory: ctx.directory }
66800
66876
  });
66801
- log(`[${HOOK_NAME16}] Injection successful`, { sessionID });
66877
+ log(`[${HOOK_NAME17}] Injection successful`, { sessionID });
66802
66878
  if (injectionState) {
66803
66879
  injectionState.inFlight = false;
66804
66880
  injectionState.lastInjectedAt = Date.now();
66805
66881
  injectionState.consecutiveFailures = 0;
66806
66882
  }
66807
66883
  } catch (error51) {
66808
- log(`[${HOOK_NAME16}] Injection failed`, { sessionID, error: String(error51) });
66884
+ log(`[${HOOK_NAME17}] Injection failed`, { sessionID, error: String(error51) });
66809
66885
  if (injectionState) {
66810
66886
  injectionState.inFlight = false;
66811
66887
  injectionState.lastInjectedAt = Date.now();
@@ -66838,7 +66914,10 @@ function startCountdown2(args) {
66838
66914
  sessionStateStore
66839
66915
  } = args;
66840
66916
  const state3 = sessionStateStore.getState(sessionID);
66841
- sessionStateStore.cancelCountdown(sessionID);
66917
+ if (state3.countdownTimer || state3.countdownInterval) {
66918
+ log(`[${HOOK_NAME17}] Skipped: countdown already active`, { sessionID });
66919
+ return;
66920
+ }
66842
66921
  let secondsRemaining = COUNTDOWN_SECONDS2;
66843
66922
  showCountdownToast2(ctx, secondsRemaining, incompleteCount);
66844
66923
  state3.countdownStartedAt = Date.now();
@@ -66859,7 +66938,7 @@ function startCountdown2(args) {
66859
66938
  sessionStateStore
66860
66939
  });
66861
66940
  }, COUNTDOWN_SECONDS2 * 1000);
66862
- log(`[${HOOK_NAME16}] Countdown started`, {
66941
+ log(`[${HOOK_NAME17}] Countdown started`, {
66863
66942
  sessionID,
66864
66943
  seconds: COUNTDOWN_SECONDS2,
66865
66944
  incompleteCount
@@ -66876,16 +66955,20 @@ async function handleSessionIdle2(args) {
66876
66955
  skipAgents = DEFAULT_SKIP_AGENTS2,
66877
66956
  isContinuationStopped
66878
66957
  } = args;
66879
- log(`[${HOOK_NAME16}] session.idle`, { sessionID });
66958
+ log(`[${HOOK_NAME17}] session.idle`, { sessionID });
66880
66959
  const state3 = sessionStateStore.getState(sessionID);
66960
+ if (state3.countdownTimer || state3.countdownInterval) {
66961
+ log(`[${HOOK_NAME17}] Skipped: countdown already active`, { sessionID });
66962
+ return;
66963
+ }
66881
66964
  if (state3.isRecovering) {
66882
- log(`[${HOOK_NAME16}] Skipped: in recovery`, { sessionID });
66965
+ log(`[${HOOK_NAME17}] Skipped: in recovery`, { sessionID });
66883
66966
  return;
66884
66967
  }
66885
66968
  if (state3.abortDetectedAt) {
66886
66969
  const timeSinceAbort = Date.now() - state3.abortDetectedAt;
66887
66970
  if (timeSinceAbort < ABORT_WINDOW_MS2) {
66888
- log(`[${HOOK_NAME16}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
66971
+ log(`[${HOOK_NAME17}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
66889
66972
  state3.abortDetectedAt = undefined;
66890
66973
  return;
66891
66974
  }
@@ -66893,7 +66976,7 @@ async function handleSessionIdle2(args) {
66893
66976
  }
66894
66977
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
66895
66978
  if (hasRunningBgTasks) {
66896
- log(`[${HOOK_NAME16}] Skipped: background tasks running`, { sessionID });
66979
+ log(`[${HOOK_NAME17}] Skipped: background tasks running`, { sessionID });
66897
66980
  return;
66898
66981
  }
66899
66982
  try {
@@ -66903,42 +66986,42 @@ async function handleSessionIdle2(args) {
66903
66986
  });
66904
66987
  const messages = normalizeSDKResponse(messagesResp, []);
66905
66988
  if (isLastAssistantMessageAborted2(messages)) {
66906
- log(`[${HOOK_NAME16}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
66989
+ log(`[${HOOK_NAME17}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
66907
66990
  return;
66908
66991
  }
66909
66992
  } catch (error51) {
66910
- log(`[${HOOK_NAME16}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
66993
+ log(`[${HOOK_NAME17}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
66911
66994
  }
66912
66995
  let todos = [];
66913
66996
  try {
66914
66997
  const response = await ctx.client.session.todo({ path: { id: sessionID } });
66915
66998
  todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
66916
66999
  } catch (error51) {
66917
- log(`[${HOOK_NAME16}] Todo fetch failed`, { sessionID, error: String(error51) });
67000
+ log(`[${HOOK_NAME17}] Todo fetch failed`, { sessionID, error: String(error51) });
66918
67001
  return;
66919
67002
  }
66920
67003
  if (!todos || todos.length === 0) {
66921
- log(`[${HOOK_NAME16}] No todos`, { sessionID });
67004
+ log(`[${HOOK_NAME17}] No todos`, { sessionID });
66922
67005
  return;
66923
67006
  }
66924
67007
  const incompleteCount = getIncompleteCount(todos);
66925
67008
  if (incompleteCount === 0) {
66926
- log(`[${HOOK_NAME16}] All todos complete`, { sessionID, total: todos.length });
67009
+ log(`[${HOOK_NAME17}] All todos complete`, { sessionID, total: todos.length });
66927
67010
  return;
66928
67011
  }
66929
67012
  if (state3.inFlight) {
66930
- log(`[${HOOK_NAME16}] Skipped: injection in flight`, { sessionID });
67013
+ log(`[${HOOK_NAME17}] Skipped: injection in flight`, { sessionID });
66931
67014
  return;
66932
67015
  }
66933
67016
  if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES2 && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS2) {
66934
67017
  state3.consecutiveFailures = 0;
66935
- log(`[${HOOK_NAME16}] Reset consecutive failures after recovery window`, {
67018
+ log(`[${HOOK_NAME17}] Reset consecutive failures after recovery window`, {
66936
67019
  sessionID,
66937
67020
  failureResetWindowMs: FAILURE_RESET_WINDOW_MS2
66938
67021
  });
66939
67022
  }
66940
67023
  if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES2) {
66941
- log(`[${HOOK_NAME16}] Skipped: max consecutive failures reached`, {
67024
+ log(`[${HOOK_NAME17}] Skipped: max consecutive failures reached`, {
66942
67025
  sessionID,
66943
67026
  consecutiveFailures: state3.consecutiveFailures,
66944
67027
  maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES2
@@ -66947,7 +67030,7 @@ async function handleSessionIdle2(args) {
66947
67030
  }
66948
67031
  const effectiveCooldown = CONTINUATION_COOLDOWN_MS3 * 2 ** Math.min(state3.consecutiveFailures, 5);
66949
67032
  if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
66950
- log(`[${HOOK_NAME16}] Skipped: cooldown active`, {
67033
+ log(`[${HOOK_NAME17}] Skipped: cooldown active`, {
66951
67034
  sessionID,
66952
67035
  effectiveCooldown,
66953
67036
  consecutiveFailures: state3.consecutiveFailures
@@ -66977,20 +67060,20 @@ async function handleSessionIdle2(args) {
66977
67060
  }
66978
67061
  }
66979
67062
  } catch (error51) {
66980
- log(`[${HOOK_NAME16}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
67063
+ log(`[${HOOK_NAME17}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
66981
67064
  }
66982
- log(`[${HOOK_NAME16}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
67065
+ log(`[${HOOK_NAME17}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
66983
67066
  const resolvedAgentName = resolvedInfo?.agent;
66984
67067
  if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
66985
- log(`[${HOOK_NAME16}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
67068
+ log(`[${HOOK_NAME17}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
66986
67069
  return;
66987
67070
  }
66988
67071
  if (hasCompactionMessage && !resolvedInfo?.agent) {
66989
- log(`[${HOOK_NAME16}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
67072
+ log(`[${HOOK_NAME17}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
66990
67073
  return;
66991
67074
  }
66992
67075
  if (isContinuationStopped?.(sessionID)) {
66993
- log(`[${HOOK_NAME16}] Skipped: continuation stopped for session`, { sessionID });
67076
+ log(`[${HOOK_NAME17}] Skipped: continuation stopped for session`, { sessionID });
66994
67077
  return;
66995
67078
  }
66996
67079
  startCountdown2({
@@ -67020,7 +67103,7 @@ function handleNonIdleEvent2(args) {
67020
67103
  if (state3?.countdownStartedAt) {
67021
67104
  const elapsed = Date.now() - state3.countdownStartedAt;
67022
67105
  if (elapsed < COUNTDOWN_GRACE_PERIOD_MS2) {
67023
- log(`[${HOOK_NAME16}] Ignoring user message in grace period`, { sessionID, elapsed });
67106
+ log(`[${HOOK_NAME17}] Ignoring user message in grace period`, { sessionID, elapsed });
67024
67107
  return;
67025
67108
  }
67026
67109
  }
@@ -67064,7 +67147,7 @@ function handleNonIdleEvent2(args) {
67064
67147
  const sessionInfo = properties?.info;
67065
67148
  if (sessionInfo?.id) {
67066
67149
  sessionStateStore.cleanup(sessionInfo.id);
67067
- log(`[${HOOK_NAME16}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
67150
+ log(`[${HOOK_NAME17}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
67068
67151
  }
67069
67152
  return;
67070
67153
  }
@@ -67089,10 +67172,10 @@ function createTodoContinuationHandler(args) {
67089
67172
  if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
67090
67173
  const state3 = sessionStateStore.getState(sessionID);
67091
67174
  state3.abortDetectedAt = Date.now();
67092
- log(`[${HOOK_NAME16}] Abort detected via session.error`, { sessionID, errorName: error51.name });
67175
+ log(`[${HOOK_NAME17}] Abort detected via session.error`, { sessionID, errorName: error51.name });
67093
67176
  }
67094
67177
  sessionStateStore.cancelCountdown(sessionID);
67095
- log(`[${HOOK_NAME16}] session.error`, { sessionID });
67178
+ log(`[${HOOK_NAME17}] session.error`, { sessionID });
67096
67179
  return;
67097
67180
  }
67098
67181
  if (event.type === "session.idle") {
@@ -67207,13 +67290,13 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
67207
67290
  const state3 = sessionStateStore.getState(sessionID);
67208
67291
  state3.isRecovering = true;
67209
67292
  sessionStateStore.cancelCountdown(sessionID);
67210
- log(`[${HOOK_NAME16}] Session marked as recovering`, { sessionID });
67293
+ log(`[${HOOK_NAME17}] Session marked as recovering`, { sessionID });
67211
67294
  };
67212
67295
  const markRecoveryComplete = (sessionID) => {
67213
67296
  const state3 = sessionStateStore.getExistingState(sessionID);
67214
67297
  if (state3) {
67215
67298
  state3.isRecovering = false;
67216
- log(`[${HOOK_NAME16}] Session recovery complete`, { sessionID });
67299
+ log(`[${HOOK_NAME17}] Session recovery complete`, { sessionID });
67217
67300
  }
67218
67301
  };
67219
67302
  const handler = createTodoContinuationHandler({
@@ -67225,7 +67308,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
67225
67308
  });
67226
67309
  const cancelAllCountdowns = () => {
67227
67310
  sessionStateStore.cancelAllCountdowns();
67228
- log(`[${HOOK_NAME16}] All countdowns cancelled`);
67311
+ log(`[${HOOK_NAME17}] All countdowns cancelled`);
67229
67312
  };
67230
67313
  return {
67231
67314
  handler,
@@ -67449,7 +67532,7 @@ This is a reminder only. No automatic action was taken.`;
67449
67532
  }
67450
67533
  // src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts
67451
67534
  init_logger();
67452
- var HOOK_NAME17 = "unstable-agent-babysitter";
67535
+ var HOOK_NAME18 = "unstable-agent-babysitter";
67453
67536
  var DEFAULT_TIMEOUT_MS = 120000;
67454
67537
  var COOLDOWN_MS = 5 * 60 * 1000;
67455
67538
  async function resolveMainSessionTarget(ctx, sessionID) {
@@ -67469,7 +67552,7 @@ async function resolveMainSessionTarget(ctx, sessionID) {
67469
67552
  }
67470
67553
  }
67471
67554
  } catch (error51) {
67472
- log(`[${HOOK_NAME17}] Failed to resolve main session agent`, { sessionID, error: String(error51) });
67555
+ log(`[${HOOK_NAME18}] Failed to resolve main session agent`, { sessionID, error: String(error51) });
67473
67556
  }
67474
67557
  return { agent, model };
67475
67558
  }
@@ -67502,7 +67585,7 @@ async function getThinkingSummary(ctx, sessionID) {
67502
67585
  return combined;
67503
67586
  return `${combined.slice(0, THINKING_SUMMARY_MAX_CHARS)}...`;
67504
67587
  } catch (error51) {
67505
- log(`[${HOOK_NAME17}] Failed to fetch thinking summary`, { sessionID, error: String(error51) });
67588
+ log(`[${HOOK_NAME18}] Failed to fetch thinking summary`, { sessionID, error: String(error51) });
67506
67589
  return null;
67507
67590
  }
67508
67591
  }
@@ -67551,9 +67634,9 @@ function createUnstableAgentBabysitterHook(ctx, options) {
67551
67634
  query: { directory: ctx.directory }
67552
67635
  });
67553
67636
  reminderCooldowns.set(task.id, now);
67554
- log(`[${HOOK_NAME17}] Reminder injected`, { taskId: task.id, sessionID: mainSessionID });
67637
+ log(`[${HOOK_NAME18}] Reminder injected`, { taskId: task.id, sessionID: mainSessionID });
67555
67638
  } catch (error51) {
67556
- log(`[${HOOK_NAME17}] Reminder injection failed`, { taskId: task.id, error: String(error51) });
67639
+ log(`[${HOOK_NAME18}] Reminder injection failed`, { taskId: task.id, error: String(error51) });
67557
67640
  }
67558
67641
  }
67559
67642
  };
@@ -92343,6 +92426,7 @@ function createToolGuardHooks(args) {
92343
92426
  const envFileWriteGuard = isHookEnabled("env-file-write-guard") ? safeHook("env-file-write-guard", () => createEnvFileWriteGuardHook(pluginConfig.security?.env_file_guard)) : null;
92344
92427
  const jsonErrorRecovery = isHookEnabled("json-error-recovery") ? safeHook("json-error-recovery", () => createJsonErrorRecoveryHook(ctx)) : null;
92345
92428
  const bashFileReadGuard = isHookEnabled("bash-file-read-guard") ? safeHook("bash-file-read-guard", () => createBashFileReadGuardHook()) : null;
92429
+ const contextModeEnforcer = isHookEnabled("context-mode-enforcer") ? safeHook("context-mode-enforcer", () => createContextModeEnforcerHook(pluginConfig)) : null;
92346
92430
  const readImageResizer = isHookEnabled("read-image-resizer") ? safeHook("read-image-resizer", () => createReadImageResizerHook(ctx)) : null;
92347
92431
  const webfetchRedirectGuard = isHookEnabled("webfetch-redirect-guard") ? safeHook("webfetch-redirect-guard", () => createWebFetchRedirectGuardHook(ctx)) : null;
92348
92432
  const hashlineEditDiffEnhancer = isHookEnabled("hashline-edit-diff-enhancer") ? safeHook("hashline-edit-diff-enhancer", () => createHashlineEditDiffEnhancerHook({ hashline_edit: { enabled: pluginConfig.experimental?.hashline_edit ?? false } })) : null;
@@ -92364,6 +92448,7 @@ function createToolGuardHooks(args) {
92364
92448
  envFileWriteGuard,
92365
92449
  jsonErrorRecovery,
92366
92450
  bashFileReadGuard,
92451
+ contextModeEnforcer,
92367
92452
  readImageResizer,
92368
92453
  webfetchRedirectGuard,
92369
92454
  hashlineEditDiffEnhancer,
@@ -95397,14 +95482,14 @@ function buildCompactContextDisciplineSection(hasContextMode = false) {
95397
95482
  | Docs / Web | ctx_fetch_and_index -> ctx_search |
95398
95483
  | Compression | compress when ctx_stats > 40% or 10+ tool calls |
95399
95484
 
95400
- Edits need prior read for LINE#ID \u2014 read\u2192edit chain exempt. When in doubt, use ctx_*.`;
95485
+ Edits need prior read for LINE#ID \u2014 read\u2192edit chain exempt. MUST use ctx_* when available \u2014 raw grep/read is forbidden for analysis.`;
95401
95486
  }
95402
95487
  function buildExploreDisciplineSection(hasContextMode = false, hasHeadroom = false) {
95403
95488
  if (!hasContextMode && !hasHeadroom)
95404
95489
  return "";
95405
95490
  const parts = [];
95406
95491
  if (hasContextMode) {
95407
- parts.push("Prefer ctx_search for indexed hits \u2192 grep/glob fallback for raw FS; use ctx_batch_execute / ctx_execute for multi-file analysis; use ctx_fetch_and_index for docs/web \u2192 ctx_search.");
95492
+ parts.push("MUST use ctx_search for indexed hits \u2192 grep/glob fallback ONLY when ctx_* unavailable; use ctx_batch_execute / ctx_execute for multi-file analysis; use ctx_fetch_and_index for docs/web \u2192 ctx_search. Raw grep/read/glob for analysis is forbidden when ctx_* is available.");
95408
95493
  }
95409
95494
  if (hasHeadroom) {
95410
95495
  parts.push("Use headroom_retrieve / headroom_search for compressed history \u2014 NEVER re-read full history.");
@@ -102738,6 +102823,12 @@ var BuiltinCommandNameSchema = exports_external.enum([
102738
102823
  var CommentCheckerConfigSchema = exports_external.object({
102739
102824
  custom_prompt: exports_external.string().optional()
102740
102825
  });
102826
+ // src/config/schema/context-mode.ts
102827
+ var ContextModeConfigSchema = exports_external.object({
102828
+ enabled: exports_external.boolean().default(true),
102829
+ enforce: exports_external.boolean().default(false),
102830
+ blocked_tools: exports_external.array(exports_external.string()).default(["read", "grep", "glob"])
102831
+ });
102741
102832
  // src/config/schema/evolution.ts
102742
102833
  var EvolutionWatcherConfigSchema = exports_external.object({
102743
102834
  maxArgChars: exports_external.number().int().min(1).default(4000),
@@ -102887,6 +102978,8 @@ var HookNameSchema = exports_external.enum([
102887
102978
  "evolution-compressor",
102888
102979
  "evolution-quality-gate",
102889
102980
  "evolution-hitl",
102981
+ "context-mode-enforcer",
102982
+ "task-edit-guard",
102890
102983
  "task-edit-guard"
102891
102984
  ]);
102892
102985
  // src/config/schema/matrix-loop.ts
@@ -103076,6 +103169,7 @@ var MatrixxConfigSchema = exports_external.object({
103076
103169
  agent_definitions: AgentDefinitionsConfigSchema.optional(),
103077
103170
  matrixx_self_config: MatrixxSelfConfigSkillConfigSchema.optional(),
103078
103171
  headroom: HeadroomConfigSchema.optional(),
103172
+ context_mode: ContextModeConfigSchema.optional(),
103079
103173
  rtk: RtkConfigSchema.optional(),
103080
103174
  evolution: EvolutionConfigSchema.optional(),
103081
103175
  _migrations: exports_external.array(exports_external.string()).optional()
@@ -103638,22 +103732,22 @@ function createToolExecuteAfterHandler2(args) {
103638
103732
 
103639
103733
  // src/features/assembly-state/manager.ts
103640
103734
  init_logger();
103641
- var HOOK_NAME18 = "assembly-state";
103735
+ var HOOK_NAME19 = "assembly-state";
103642
103736
  var disabledSessions = new Set;
103643
103737
  function isAssemblyDisabled(sessionID) {
103644
103738
  return disabledSessions.has(sessionID);
103645
103739
  }
103646
103740
  function disableAssembly(sessionID) {
103647
103741
  disabledSessions.add(sessionID);
103648
- log(`[${HOOK_NAME18}] Assembly disabled for session`, { sessionID });
103742
+ log(`[${HOOK_NAME19}] Assembly disabled for session`, { sessionID });
103649
103743
  disabledSessions.add(sessionID);
103650
- log(`[${HOOK_NAME18}] Consensus disabled for session`, { sessionID });
103744
+ log(`[${HOOK_NAME19}] Consensus disabled for session`, { sessionID });
103651
103745
  }
103652
103746
  function enableAssembly(sessionID) {
103653
103747
  disabledSessions.delete(sessionID);
103654
- log(`[${HOOK_NAME18}] Assembly enabled for session`, { sessionID });
103748
+ log(`[${HOOK_NAME19}] Assembly enabled for session`, { sessionID });
103655
103749
  disabledSessions.delete(sessionID);
103656
- log(`[${HOOK_NAME18}] Consensus enabled for session`, { sessionID });
103750
+ log(`[${HOOK_NAME19}] Consensus enabled for session`, { sessionID });
103657
103751
  }
103658
103752
  // src/plugin/session-agent-resolver.ts
103659
103753
  async function resolveSessionAgent(client2, sessionId, timeoutMs = 5000) {
@@ -103696,6 +103790,7 @@ function createToolExecuteBeforeHandler2(args) {
103696
103790
  const writeExistingFileGuardHook = hooks2.writeExistingFileGuard?.["tool.execute.before"];
103697
103791
  const tasksTodowriteDisablerHook = hooks2.tasksTodowriteDisabler?.["tool.execute.before"];
103698
103792
  const oracleMdOnlyHook = hooks2.oracleMdOnly?.["tool.execute.before"];
103793
+ const contextModeEnforcerHook = hooks2.contextModeEnforcer?.["tool.execute.before"];
103699
103794
  const nonInteractiveEnvHook = hooks2.nonInteractiveEnv?.["tool.execute.before"];
103700
103795
  const bashFileReadGuardHook = hooks2.bashFileReadGuard?.["tool.execute.before"];
103701
103796
  const questionLabelTruncatorHook = hooks2.questionLabelTruncator?.["tool.execute.before"];
@@ -103715,7 +103810,8 @@ function createToolExecuteBeforeHandler2(args) {
103715
103810
  envFileWriteGuardHook?.(input, output),
103716
103811
  writeExistingFileGuardHook?.(input, output),
103717
103812
  tasksTodowriteDisablerHook?.(input, output),
103718
- oracleMdOnlyHook?.(input, output)
103813
+ oracleMdOnlyHook?.(input, output),
103814
+ contextModeEnforcerHook?.(input, output)
103719
103815
  ]);
103720
103816
  let resolvePromise;
103721
103817
  if (input.tool === "task") {