oh-my-opencode 3.6.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.ja.md +6 -6
  2. package/README.ko.md +6 -6
  3. package/README.md +6 -6
  4. package/README.zh-cn.md +6 -6
  5. package/dist/agents/sisyphus-junior/gpt.d.ts +4 -14
  6. package/dist/cli/index.js +357 -104
  7. package/dist/cli/run/event-handlers.d.ts +1 -0
  8. package/dist/cli/run/opencode-bin-path.d.ts +3 -0
  9. package/dist/cli/run/opencode-binary-resolver.d.ts +5 -0
  10. package/dist/cli/run/session-resolver.d.ts +1 -0
  11. package/dist/cli/run/types.d.ts +29 -0
  12. package/dist/config/schema/browser-automation.d.ts +2 -0
  13. package/dist/config/schema/experimental.d.ts +1 -0
  14. package/dist/config/schema/hooks.d.ts +1 -0
  15. package/dist/config/schema/oh-my-opencode-config.d.ts +3 -0
  16. package/dist/create-hooks.d.ts +3 -0
  17. package/dist/create-managers.d.ts +1 -0
  18. package/dist/features/background-agent/manager.d.ts +3 -0
  19. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  20. package/dist/features/builtin-skills/skills/playwright-cli.d.ts +10 -0
  21. package/dist/features/tmux-subagent/action-executor-core.d.ts +21 -0
  22. package/dist/features/tmux-subagent/action-executor.d.ts +3 -12
  23. package/dist/features/tmux-subagent/grid-planning.d.ts +2 -2
  24. package/dist/features/tmux-subagent/pane-split-availability.d.ts +3 -2
  25. package/dist/features/tmux-subagent/polling-manager.d.ts +1 -0
  26. package/dist/features/tmux-subagent/spawn-target-finder.d.ts +1 -1
  27. package/dist/hooks/context-window-monitor.d.ts +5 -1
  28. package/dist/hooks/directory-agents-injector/hook.d.ts +3 -1
  29. package/dist/hooks/directory-readme-injector/hook.d.ts +3 -1
  30. package/dist/hooks/hashline-read-enhancer/hook.d.ts +18 -0
  31. package/dist/hooks/hashline-read-enhancer/index.d.ts +1 -0
  32. package/dist/hooks/index.d.ts +1 -0
  33. package/dist/hooks/preemptive-compaction.d.ts +4 -1
  34. package/dist/hooks/rules-injector/hook.d.ts +3 -1
  35. package/dist/hooks/think-mode/switcher.d.ts +7 -0
  36. package/dist/hooks/tool-output-truncator.d.ts +3 -0
  37. package/dist/index.js +1758 -830
  38. package/dist/plugin/hooks/create-core-hooks.d.ts +3 -0
  39. package/dist/plugin/hooks/create-session-hooks.d.ts +2 -0
  40. package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +4 -1
  41. package/dist/shared/dynamic-truncator.d.ts +7 -3
  42. package/dist/shared/fallback-model-availability.d.ts +9 -2
  43. package/dist/shared/git-worktree/index.d.ts +2 -0
  44. package/dist/shared/git-worktree/parse-status-porcelain-line.d.ts +6 -0
  45. package/dist/shared/index.d.ts +2 -0
  46. package/dist/shared/model-availability.d.ts +0 -5
  47. package/dist/shared/session-directory-resolver.d.ts +7 -0
  48. package/dist/shared/tmux/tmux-utils/layout.d.ts +2 -2
  49. package/dist/tools/call-omo-agent/tools.d.ts +1 -1
  50. package/dist/tools/delegate-task/sync-prompt-sender.d.ts +7 -1
  51. package/dist/tools/hashline-edit/constants.d.ts +2 -0
  52. package/dist/tools/hashline-edit/edit-operations.d.ts +6 -0
  53. package/dist/tools/hashline-edit/hash-computation.d.ts +3 -0
  54. package/dist/tools/hashline-edit/index.d.ts +7 -0
  55. package/dist/tools/hashline-edit/tools.d.ts +2 -0
  56. package/dist/tools/hashline-edit/types.d.ts +22 -0
  57. package/dist/tools/hashline-edit/validation.d.ts +6 -0
  58. package/dist/tools/index.d.ts +1 -0
  59. package/package.json +8 -8
  60. package/dist/features/background-agent/background-event-handler.d.ts +0 -26
  61. package/dist/features/background-agent/background-manager-shutdown.d.ts +0 -25
  62. package/dist/features/background-agent/notification-tracker.d.ts +0 -6
  63. package/dist/features/background-agent/notify-parent-session.d.ts +0 -10
  64. package/dist/features/background-agent/poll-running-tasks.d.ts +0 -15
  65. package/dist/features/background-agent/process-signal.d.ts +0 -2
  66. package/dist/features/background-agent/session-validator.d.ts +0 -7
  67. package/dist/features/background-agent/spawner/task-factory.d.ts +0 -2
  68. package/dist/features/background-agent/spawner/task-resumer.d.ts +0 -3
  69. package/dist/features/background-agent/spawner/task-starter.d.ts +0 -3
  70. package/dist/features/background-agent/stale-task-pruner.d.ts +0 -15
  71. package/dist/features/background-agent/task-canceller.d.ts +0 -27
  72. package/dist/features/background-agent/task-completer.d.ts +0 -13
  73. package/dist/features/background-agent/task-launch.d.ts +0 -15
  74. package/dist/features/background-agent/task-queries.d.ts +0 -7
  75. package/dist/features/background-agent/task-queue-processor.d.ts +0 -14
  76. package/dist/features/background-agent/task-resumer.d.ts +0 -14
  77. package/dist/features/background-agent/task-starter.d.ts +0 -27
  78. package/dist/features/background-agent/task-tracker.d.ts +0 -18
  79. package/dist/features/tmux-subagent/manager-cleanup.d.ts +0 -12
  80. package/dist/features/tmux-subagent/session-cleaner.d.ts +0 -23
  81. package/dist/features/tmux-subagent/session-spawner.d.ts +0 -34
  82. package/dist/hooks/interactive-bash-session/interactive-bash-session-hook.d.ts +0 -23
  83. package/dist/shared/available-models-fetcher.d.ts +0 -4
  84. package/dist/shared/model-cache-availability.d.ts +0 -2
  85. package/dist/tools/background-task/modules/background-cancel.d.ts +0 -4
  86. package/dist/tools/background-task/modules/background-output.d.ts +0 -3
  87. package/dist/tools/background-task/modules/background-task.d.ts +0 -3
  88. package/dist/tools/background-task/modules/formatters.d.ts +0 -11
  89. package/dist/tools/background-task/modules/message-processing.d.ts +0 -59
  90. package/dist/tools/background-task/modules/utils.d.ts +0 -16
  91. package/dist/tools/call-omo-agent/agent-type-normalizer.d.ts +0 -2
  92. package/dist/tools/call-omo-agent/sync-agent-executor.d.ts +0 -4
  93. package/dist/tools/delegate-task/skill-content-resolver.d.ts +0 -9
package/dist/index.js CHANGED
@@ -15329,9 +15329,12 @@ function normalizeSDKResponse(response, fallback, options) {
15329
15329
  }
15330
15330
 
15331
15331
  // src/shared/dynamic-truncator.ts
15332
- var ANTHROPIC_ACTUAL_LIMIT = process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : 200000;
15332
+ var DEFAULT_ANTHROPIC_ACTUAL_LIMIT = 200000;
15333
15333
  var CHARS_PER_TOKEN_ESTIMATE = 4;
15334
15334
  var DEFAULT_TARGET_MAX_TOKENS = 50000;
15335
+ function getAnthropicActualLimit(modelCacheState) {
15336
+ return (modelCacheState?.anthropicContext1MEnabled ?? false) || process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : DEFAULT_ANTHROPIC_ACTUAL_LIMIT;
15337
+ }
15335
15338
  function estimateTokens(text) {
15336
15339
  return Math.ceil(text.length / CHARS_PER_TOKEN_ESTIMATE);
15337
15340
  }
@@ -15392,7 +15395,7 @@ function truncateToTokenLimit(output, maxTokens, preserveHeaderLines = 3) {
15392
15395
  removedCount
15393
15396
  };
15394
15397
  }
15395
- async function getContextWindowUsage(ctx, sessionID) {
15398
+ async function getContextWindowUsage(ctx, sessionID, modelCacheState) {
15396
15399
  try {
15397
15400
  const response = await ctx.client.session.messages({
15398
15401
  path: { id: sessionID }
@@ -15404,17 +15407,18 @@ async function getContextWindowUsage(ctx, sessionID) {
15404
15407
  const lastAssistant = assistantMessages[assistantMessages.length - 1];
15405
15408
  const lastTokens = lastAssistant.tokens;
15406
15409
  const usedTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0) + (lastTokens?.output ?? 0);
15407
- const remainingTokens = ANTHROPIC_ACTUAL_LIMIT - usedTokens;
15410
+ const anthropicActualLimit = getAnthropicActualLimit(modelCacheState);
15411
+ const remainingTokens = anthropicActualLimit - usedTokens;
15408
15412
  return {
15409
15413
  usedTokens,
15410
15414
  remainingTokens,
15411
- usagePercentage: usedTokens / ANTHROPIC_ACTUAL_LIMIT
15415
+ usagePercentage: usedTokens / anthropicActualLimit
15412
15416
  };
15413
15417
  } catch {
15414
15418
  return null;
15415
15419
  }
15416
15420
  }
15417
- async function dynamicTruncate(ctx, sessionID, output, options = {}) {
15421
+ async function dynamicTruncate(ctx, sessionID, output, options = {}, modelCacheState) {
15418
15422
  if (typeof output !== "string") {
15419
15423
  return { result: String(output ?? ""), truncated: false };
15420
15424
  }
@@ -15422,7 +15426,7 @@ async function dynamicTruncate(ctx, sessionID, output, options = {}) {
15422
15426
  targetMaxTokens = DEFAULT_TARGET_MAX_TOKENS,
15423
15427
  preserveHeaderLines = 3
15424
15428
  } = options;
15425
- const usage = await getContextWindowUsage(ctx, sessionID);
15429
+ const usage = await getContextWindowUsage(ctx, sessionID, modelCacheState);
15426
15430
  if (!usage) {
15427
15431
  return truncateToTokenLimit(output, targetMaxTokens, preserveHeaderLines);
15428
15432
  }
@@ -15435,10 +15439,10 @@ async function dynamicTruncate(ctx, sessionID, output, options = {}) {
15435
15439
  }
15436
15440
  return truncateToTokenLimit(output, maxOutputTokens, preserveHeaderLines);
15437
15441
  }
15438
- function createDynamicTruncator(ctx) {
15442
+ function createDynamicTruncator(ctx, modelCacheState) {
15439
15443
  return {
15440
- truncate: (sessionID, output, options) => dynamicTruncate(ctx, sessionID, output, options),
15441
- getUsage: (sessionID) => getContextWindowUsage(ctx, sessionID),
15444
+ truncate: (sessionID, output, options) => dynamicTruncate(ctx, sessionID, output, options, modelCacheState),
15445
+ getUsage: (sessionID) => getContextWindowUsage(ctx, sessionID, modelCacheState),
15442
15446
  truncateSync: (output, maxTokens, preserveHeaderLines) => truncateToTokenLimit(output, maxTokens, preserveHeaderLines)
15443
15447
  };
15444
15448
  }
@@ -17434,7 +17438,7 @@ init_logger();
17434
17438
  import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
17435
17439
  import { join as join10 } from "path";
17436
17440
  function normalizeModelName(name) {
17437
- return name.toLowerCase().replace(/claude-(opus|sonnet|haiku)-4-5/g, "claude-$1-4.5").replace(/claude-(opus|sonnet|haiku)-4\.5/g, "claude-$1-4.5");
17441
+ return name.toLowerCase().replace(/claude-(opus|sonnet|haiku)-(\d+)[.-](\d+)/g, "claude-$1-$2.$3");
17438
17442
  }
17439
17443
  function fuzzyMatchModel(target, available, providers) {
17440
17444
  log("[fuzzyMatchModel] called", { target, availableCount: available.size, providers });
@@ -17459,6 +17463,7 @@ function fuzzyMatchModel(target, available, providers) {
17459
17463
  const matches = candidates.filter((model) => normalizeModelName(model).includes(targetNormalized));
17460
17464
  log("[fuzzyMatchModel] substring matches", { targetNormalized, matchCount: matches.length, matches });
17461
17465
  if (matches.length === 0) {
17466
+ log("[fuzzyMatchModel] WARNING: no match found", { target, availableCount: available.size, providers });
17462
17467
  return null;
17463
17468
  }
17464
17469
  const exactMatch = matches.find((model) => normalizeModelName(model) === targetNormalized);
@@ -17620,55 +17625,6 @@ async function fetchAvailableModels(client, options) {
17620
17625
  }
17621
17626
  return modelSet;
17622
17627
  }
17623
- function isAnyFallbackModelAvailable(fallbackChain, availableModels) {
17624
- if (availableModels.size > 0) {
17625
- for (const entry of fallbackChain) {
17626
- const hasAvailableProvider = entry.providers.some((provider) => {
17627
- return fuzzyMatchModel(entry.model, availableModels, [provider]) !== null;
17628
- });
17629
- if (hasAvailableProvider) {
17630
- return true;
17631
- }
17632
- }
17633
- }
17634
- const connectedProviders = readConnectedProvidersCache();
17635
- if (connectedProviders) {
17636
- const connectedSet = new Set(connectedProviders);
17637
- for (const entry of fallbackChain) {
17638
- if (entry.providers.some((p) => connectedSet.has(p))) {
17639
- log("[isAnyFallbackModelAvailable] model not in available set, but provider is connected", {
17640
- model: entry.model,
17641
- availableCount: availableModels.size
17642
- });
17643
- return true;
17644
- }
17645
- }
17646
- }
17647
- return false;
17648
- }
17649
- function isAnyProviderConnected(providers, availableModels) {
17650
- if (availableModels.size > 0) {
17651
- const providerSet = new Set(providers);
17652
- for (const model of availableModels) {
17653
- const [provider] = model.split("/");
17654
- if (providerSet.has(provider)) {
17655
- log("[isAnyProviderConnected] found model from required provider", { provider, model });
17656
- return true;
17657
- }
17658
- }
17659
- }
17660
- const connectedProviders = readConnectedProvidersCache();
17661
- if (connectedProviders) {
17662
- const connectedSet = new Set(connectedProviders);
17663
- for (const provider of providers) {
17664
- if (connectedSet.has(provider)) {
17665
- log("[isAnyProviderConnected] provider connected via cache", { provider });
17666
- return true;
17667
- }
17668
- }
17669
- }
17670
- return false;
17671
- }
17672
17628
  function isModelCacheAvailable() {
17673
17629
  if (hasProviderModelsCache()) {
17674
17630
  return true;
@@ -17816,6 +17772,139 @@ function normalizeModel2(model) {
17816
17772
  function resolveModel(input) {
17817
17773
  return normalizeModel2(input.userModel) ?? normalizeModel2(input.inheritedModel) ?? input.systemDefault;
17818
17774
  }
17775
+ // src/shared/fallback-model-availability.ts
17776
+ init_logger();
17777
+
17778
+ // src/shared/model-name-matcher.ts
17779
+ init_logger();
17780
+ function normalizeModelName2(name) {
17781
+ return name.toLowerCase().replace(/claude-(opus|sonnet|haiku)-(\d+)[.-](\d+)/g, "claude-$1-$2.$3");
17782
+ }
17783
+ function fuzzyMatchModel2(target, available, providers) {
17784
+ log("[fuzzyMatchModel] called", { target, availableCount: available.size, providers });
17785
+ if (available.size === 0) {
17786
+ log("[fuzzyMatchModel] empty available set");
17787
+ return null;
17788
+ }
17789
+ const targetNormalized = normalizeModelName2(target);
17790
+ let candidates = Array.from(available);
17791
+ if (providers && providers.length > 0) {
17792
+ const providerSet = new Set(providers);
17793
+ candidates = candidates.filter((model) => {
17794
+ const [provider] = model.split("/");
17795
+ return providerSet.has(provider);
17796
+ });
17797
+ log("[fuzzyMatchModel] filtered by providers", {
17798
+ candidateCount: candidates.length,
17799
+ candidates: candidates.slice(0, 10)
17800
+ });
17801
+ }
17802
+ if (candidates.length === 0) {
17803
+ log("[fuzzyMatchModel] no candidates after filter");
17804
+ return null;
17805
+ }
17806
+ const matches = candidates.filter((model) => normalizeModelName2(model).includes(targetNormalized));
17807
+ log("[fuzzyMatchModel] substring matches", {
17808
+ targetNormalized,
17809
+ matchCount: matches.length,
17810
+ matches
17811
+ });
17812
+ if (matches.length === 0) {
17813
+ return null;
17814
+ }
17815
+ const exactMatch = matches.find((model) => normalizeModelName2(model) === targetNormalized);
17816
+ if (exactMatch) {
17817
+ log("[fuzzyMatchModel] exact match found", { exactMatch });
17818
+ return exactMatch;
17819
+ }
17820
+ const exactModelIdMatches = matches.filter((model) => {
17821
+ const modelId = model.split("/").slice(1).join("/");
17822
+ return normalizeModelName2(modelId) === targetNormalized;
17823
+ });
17824
+ if (exactModelIdMatches.length > 0) {
17825
+ const result2 = exactModelIdMatches.reduce((shortest, current) => current.length < shortest.length ? current : shortest);
17826
+ log("[fuzzyMatchModel] exact model ID match found", {
17827
+ result: result2,
17828
+ candidateCount: exactModelIdMatches.length
17829
+ });
17830
+ return result2;
17831
+ }
17832
+ const result = matches.reduce((shortest, current) => current.length < shortest.length ? current : shortest);
17833
+ log("[fuzzyMatchModel] shortest match", { result });
17834
+ return result;
17835
+ }
17836
+
17837
+ // src/shared/fallback-model-availability.ts
17838
+ function resolveFirstAvailableFallback(fallbackChain, availableModels) {
17839
+ for (const entry of fallbackChain) {
17840
+ for (const provider of entry.providers) {
17841
+ const matchedModel = fuzzyMatchModel2(entry.model, availableModels, [provider]);
17842
+ log("[resolveFirstAvailableFallback] attempt", {
17843
+ provider,
17844
+ requestedModel: entry.model,
17845
+ resolvedModel: matchedModel
17846
+ });
17847
+ if (matchedModel !== null) {
17848
+ log("[resolveFirstAvailableFallback] resolved", {
17849
+ provider,
17850
+ requestedModel: entry.model,
17851
+ resolvedModel: matchedModel
17852
+ });
17853
+ return { provider, model: matchedModel };
17854
+ }
17855
+ }
17856
+ }
17857
+ log("[resolveFirstAvailableFallback] WARNING: no fallback model resolved", {
17858
+ chain: fallbackChain.map((entry) => ({
17859
+ model: entry.model,
17860
+ providers: entry.providers
17861
+ })),
17862
+ availableCount: availableModels.size
17863
+ });
17864
+ return null;
17865
+ }
17866
+ function isAnyFallbackModelAvailable(fallbackChain, availableModels) {
17867
+ if (resolveFirstAvailableFallback(fallbackChain, availableModels) !== null) {
17868
+ return true;
17869
+ }
17870
+ const connectedProviders = readConnectedProvidersCache();
17871
+ if (connectedProviders) {
17872
+ const connectedSet = new Set(connectedProviders);
17873
+ for (const entry of fallbackChain) {
17874
+ if (entry.providers.some((p) => connectedSet.has(p))) {
17875
+ log("[isAnyFallbackModelAvailable] WARNING: No fuzzy match found for any model in fallback chain, but provider is connected. Agent may fail at runtime.", { chain: fallbackChain.map((entryItem) => entryItem.model), availableCount: availableModels.size });
17876
+ return true;
17877
+ }
17878
+ }
17879
+ }
17880
+ return false;
17881
+ }
17882
+ function isAnyProviderConnected(providers, availableModels) {
17883
+ if (availableModels.size > 0) {
17884
+ const providerSet = new Set(providers);
17885
+ for (const model of availableModels) {
17886
+ const [provider] = model.split("/");
17887
+ if (providerSet.has(provider)) {
17888
+ log("[isAnyProviderConnected] found model from required provider", {
17889
+ provider,
17890
+ model
17891
+ });
17892
+ return true;
17893
+ }
17894
+ }
17895
+ }
17896
+ const connectedProviders = readConnectedProvidersCache();
17897
+ if (connectedProviders) {
17898
+ const connectedSet = new Set(connectedProviders);
17899
+ for (const provider of providers) {
17900
+ if (connectedSet.has(provider)) {
17901
+ log("[isAnyProviderConnected] provider connected via cache", { provider });
17902
+ return true;
17903
+ }
17904
+ }
17905
+ }
17906
+ return false;
17907
+ }
17819
17908
  // src/features/hook-message-injector/injector.ts
17820
17909
  import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync, writeFileSync as writeFileSync3 } from "fs";
17821
17910
  import { join as join11 } from "path";
@@ -18303,13 +18392,29 @@ async function replaceTmuxPane(paneId, sessionId, description, config, serverUrl
18303
18392
  }
18304
18393
  // src/shared/tmux/tmux-utils/layout.ts
18305
18394
  var {spawn: spawn8 } = globalThis.Bun;
18306
- async function enforceMainPaneWidth(mainPaneId, windowWidth) {
18395
+ async function applyLayout(layout, mainPaneSize) {
18396
+ const tmux = await getTmuxPath();
18397
+ if (!tmux)
18398
+ return;
18399
+ const layoutProc = spawn8([tmux, "select-layout", layout], {
18400
+ stdout: "ignore",
18401
+ stderr: "ignore"
18402
+ });
18403
+ await layoutProc.exited;
18404
+ if (layout.startsWith("main-")) {
18405
+ const dimension = layout === "main-horizontal" ? "main-pane-height" : "main-pane-width";
18406
+ const sizeProc = spawn8([tmux, "set-window-option", dimension, `${mainPaneSize}%`], { stdout: "ignore", stderr: "ignore" });
18407
+ await sizeProc.exited;
18408
+ }
18409
+ }
18410
+ async function enforceMainPaneWidth(mainPaneId, windowWidth, mainPaneSize) {
18307
18411
  const { log: log2 } = await Promise.resolve().then(() => (init_logger(), exports_logger));
18308
18412
  const tmux = await getTmuxPath();
18309
18413
  if (!tmux)
18310
18414
  return;
18311
18415
  const dividerWidth = 1;
18312
- const mainWidth = Math.floor((windowWidth - dividerWidth) / 2);
18416
+ const boundedMainPaneSize = Math.max(20, Math.min(80, mainPaneSize));
18417
+ const mainWidth = Math.floor((windowWidth - dividerWidth) * boundedMainPaneSize / 100);
18313
18418
  const proc = spawn8([tmux, "resize-pane", "-t", mainPaneId, "-x", String(mainWidth)], {
18314
18419
  stdout: "ignore",
18315
18420
  stderr: "ignore"
@@ -18318,7 +18423,8 @@ async function enforceMainPaneWidth(mainPaneId, windowWidth) {
18318
18423
  log2("[enforceMainPaneWidth] main pane resized", {
18319
18424
  mainPaneId,
18320
18425
  mainWidth,
18321
- windowWidth
18426
+ windowWidth,
18427
+ mainPaneSize: boundedMainPaneSize
18322
18428
  });
18323
18429
  }
18324
18430
  // src/shared/model-suggestion-retry.ts
@@ -18668,6 +18774,26 @@ async function deletePart(client, sessionID, messageID, partID) {
18668
18774
  return false;
18669
18775
  }
18670
18776
  }
18777
+ // src/shared/git-worktree/parse-status-porcelain-line.ts
18778
+ function toGitFileStatus(statusToken) {
18779
+ if (statusToken === "A" || statusToken === "??")
18780
+ return "added";
18781
+ if (statusToken === "D")
18782
+ return "deleted";
18783
+ return "modified";
18784
+ }
18785
+ function parseGitStatusPorcelainLine(line) {
18786
+ if (!line)
18787
+ return null;
18788
+ const statusToken = line.substring(0, 2).trim();
18789
+ const filePath = line.substring(3);
18790
+ if (!filePath)
18791
+ return null;
18792
+ return {
18793
+ filePath,
18794
+ status: toGitFileStatus(statusToken)
18795
+ };
18796
+ }
18671
18797
  // src/shared/git-worktree/parse-status-porcelain.ts
18672
18798
  function parseGitStatusPorcelain(output) {
18673
18799
  const map2 = new Map;
@@ -18675,19 +18801,10 @@ function parseGitStatusPorcelain(output) {
18675
18801
  return map2;
18676
18802
  for (const line of output.split(`
18677
18803
  `)) {
18678
- if (!line)
18679
- continue;
18680
- const status = line.substring(0, 2).trim();
18681
- const filePath = line.substring(3);
18682
- if (!filePath)
18804
+ const parsed = parseGitStatusPorcelainLine(line);
18805
+ if (!parsed)
18683
18806
  continue;
18684
- if (status === "A" || status === "??") {
18685
- map2.set(filePath, "added");
18686
- } else if (status === "D") {
18687
- map2.set(filePath, "deleted");
18688
- } else {
18689
- map2.set(filePath, "modified");
18690
- }
18807
+ map2.set(parsed.filePath, parsed.status);
18691
18808
  }
18692
18809
  return map2;
18693
18810
  }
@@ -19372,14 +19489,20 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
19372
19489
  }
19373
19490
  // src/hooks/context-window-monitor.ts
19374
19491
  var ANTHROPIC_DISPLAY_LIMIT = 1e6;
19375
- var ANTHROPIC_ACTUAL_LIMIT2 = process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : 200000;
19492
+ var DEFAULT_ANTHROPIC_ACTUAL_LIMIT2 = 200000;
19376
19493
  var CONTEXT_WARNING_THRESHOLD = 0.7;
19494
+ function getAnthropicActualLimit2(modelCacheState) {
19495
+ return (modelCacheState?.anthropicContext1MEnabled ?? false) || process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : DEFAULT_ANTHROPIC_ACTUAL_LIMIT2;
19496
+ }
19377
19497
  var CONTEXT_REMINDER = `${createSystemDirective(SystemDirectiveTypes.CONTEXT_WINDOW_MONITOR)}
19378
19498
 
19379
19499
  You are using Anthropic Claude with 1M context window.
19380
19500
  You have plenty of context remaining - do NOT rush or skip tasks.
19381
19501
  Complete your work thoroughly and methodically.`;
19382
- function createContextWindowMonitorHook(_ctx) {
19502
+ function isAnthropicProvider(providerID) {
19503
+ return providerID === "anthropic" || providerID === "google-vertex-anthropic";
19504
+ }
19505
+ function createContextWindowMonitorHook(_ctx, modelCacheState) {
19383
19506
  const remindedSessions = new Set;
19384
19507
  const tokenCache = new Map;
19385
19508
  const toolExecuteAfter = async (input, output) => {
@@ -19389,11 +19512,11 @@ function createContextWindowMonitorHook(_ctx) {
19389
19512
  const cached = tokenCache.get(sessionID);
19390
19513
  if (!cached)
19391
19514
  return;
19392
- if (cached.providerID !== "anthropic")
19515
+ if (!isAnthropicProvider(cached.providerID))
19393
19516
  return;
19394
19517
  const lastTokens = cached.tokens;
19395
19518
  const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0);
19396
- const actualUsagePercentage = totalInputTokens / ANTHROPIC_ACTUAL_LIMIT2;
19519
+ const actualUsagePercentage = totalInputTokens / getAnthropicActualLimit2(modelCacheState);
19397
19520
  if (actualUsagePercentage < CONTEXT_WARNING_THRESHOLD)
19398
19521
  return;
19399
19522
  remindedSessions.add(sessionID);
@@ -33307,6 +33430,7 @@ function isCliPathUsable(cliPath) {
33307
33430
  var pendingCalls = new Map;
33308
33431
  var PENDING_CALL_TTL = 60000;
33309
33432
  var cleanupIntervalStarted = false;
33433
+ var cleanupInterval;
33310
33434
  function cleanupOldPendingCalls() {
33311
33435
  const now = Date.now();
33312
33436
  for (const [callID, call] of pendingCalls) {
@@ -33319,7 +33443,10 @@ function startPendingCallCleanup() {
33319
33443
  if (cleanupIntervalStarted)
33320
33444
  return;
33321
33445
  cleanupIntervalStarted = true;
33322
- setInterval(cleanupOldPendingCalls, 1e4);
33446
+ cleanupInterval = setInterval(cleanupOldPendingCalls, 1e4);
33447
+ if (typeof cleanupInterval === "object" && "unref" in cleanupInterval) {
33448
+ cleanupInterval.unref();
33449
+ }
33323
33450
  }
33324
33451
  function registerPendingCall(callID, pendingCall) {
33325
33452
  pendingCalls.set(callID, pendingCall);
@@ -33476,7 +33603,7 @@ var TOOL_SPECIFIC_MAX_TOKENS = {
33476
33603
  WebFetch: WEBFETCH_MAX_TOKENS
33477
33604
  };
33478
33605
  function createToolOutputTruncatorHook(ctx, options) {
33479
- const truncator = createDynamicTruncator(ctx);
33606
+ const truncator = createDynamicTruncator(ctx, options?.modelCacheState);
33480
33607
  const truncateAll = options?.experimental?.truncate_all_tool_outputs ?? false;
33481
33608
  const toolExecuteAfter = async (input, output) => {
33482
33609
  if (!truncateAll && !TRUNCATABLE_TOOLS.includes(input.tool))
@@ -33632,9 +33759,9 @@ ${result}${truncationNotice}`;
33632
33759
  }
33633
33760
 
33634
33761
  // src/hooks/directory-agents-injector/hook.ts
33635
- function createDirectoryAgentsInjectorHook(ctx) {
33762
+ function createDirectoryAgentsInjectorHook(ctx, modelCacheState) {
33636
33763
  const sessionCaches = new Map;
33637
- const truncator = createDynamicTruncator(ctx);
33764
+ const truncator = createDynamicTruncator(ctx, modelCacheState);
33638
33765
  const toolExecuteAfter = async (input, output) => {
33639
33766
  const toolName = input.tool.toLowerCase();
33640
33767
  if (toolName === "read") {
@@ -33760,9 +33887,9 @@ ${result}${truncationNotice}`;
33760
33887
  }
33761
33888
 
33762
33889
  // src/hooks/directory-readme-injector/hook.ts
33763
- function createDirectoryReadmeInjectorHook(ctx) {
33890
+ function createDirectoryReadmeInjectorHook(ctx, modelCacheState) {
33764
33891
  const sessionCaches = new Map;
33765
- const truncator = createDynamicTruncator(ctx);
33892
+ const truncator = createDynamicTruncator(ctx, modelCacheState);
33766
33893
  const toolExecuteAfter = async (input, output) => {
33767
33894
  const toolName = input.tool.toLowerCase();
33768
33895
  if (toolName === "read") {
@@ -35389,6 +35516,13 @@ var THINKING_CONFIGS = {
35389
35516
  },
35390
35517
  maxTokens: 128000
35391
35518
  },
35519
+ "google-vertex-anthropic": {
35520
+ thinking: {
35521
+ type: "enabled",
35522
+ budgetTokens: 64000
35523
+ },
35524
+ maxTokens: 128000
35525
+ },
35392
35526
  "amazon-bedrock": {
35393
35527
  reasoningConfig: {
35394
35528
  type: "enabled",
@@ -35431,6 +35565,7 @@ var THINKING_CONFIGS = {
35431
35565
  };
35432
35566
  var THINKING_CAPABLE_MODELS = {
35433
35567
  anthropic: ["claude-sonnet-4", "claude-opus-4", "claude-3"],
35568
+ "google-vertex-anthropic": ["claude-sonnet-4", "claude-opus-4", "claude-3"],
35434
35569
  "amazon-bedrock": ["claude", "anthropic"],
35435
35570
  google: ["gemini-2", "gemini-3"],
35436
35571
  "google-vertex": ["gemini-2", "gemini-3"],
@@ -36426,7 +36561,7 @@ function getToolInput(sessionId, toolName, invocationId) {
36426
36561
  return null;
36427
36562
  return entry.toolInput;
36428
36563
  }
36429
- var cleanupInterval = setInterval(() => {
36564
+ var cleanupInterval2 = setInterval(() => {
36430
36565
  const now = Date.now();
36431
36566
  for (const [key, entry] of cache.entries()) {
36432
36567
  if (now - entry.timestamp > CACHE_TTL) {
@@ -36434,8 +36569,8 @@ var cleanupInterval = setInterval(() => {
36434
36569
  }
36435
36570
  }
36436
36571
  }, CACHE_TTL);
36437
- if (typeof cleanupInterval === "object" && "unref" in cleanupInterval) {
36438
- cleanupInterval.unref();
36572
+ if (typeof cleanupInterval2 === "object" && "unref" in cleanupInterval2) {
36573
+ cleanupInterval2.unref();
36439
36574
  }
36440
36575
 
36441
36576
  // src/hooks/claude-code-hooks/handlers/tool-execute-after-handler.ts
@@ -37195,8 +37330,8 @@ ${result}${truncationNotice}`;
37195
37330
 
37196
37331
  // src/hooks/rules-injector/hook.ts
37197
37332
  var TRACKED_TOOLS = ["read", "write", "edit", "multiedit"];
37198
- function createRulesInjectorHook(ctx) {
37199
- const truncator = createDynamicTruncator(ctx);
37333
+ function createRulesInjectorHook(ctx, modelCacheState) {
37334
+ const truncator = createDynamicTruncator(ctx, modelCacheState);
37200
37335
  const { getSessionCache: getSessionCache3, clearSessionCache } = createSessionCacheStore();
37201
37336
  const { processFilePathForInjection } = createRuleInjectionProcessor({
37202
37337
  workspaceDirectory: ctx.directory,
@@ -37848,6 +37983,7 @@ async function showLocalDevToast(ctx, version2, isSisyphusEnabled) {
37848
37983
  // src/hooks/auto-update-checker/hook.ts
37849
37984
  function createAutoUpdateCheckerHook(ctx, options = {}) {
37850
37985
  const { showStartupToast = true, isSisyphusEnabled = false, autoUpdate = true } = options;
37986
+ const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true";
37851
37987
  const getToastMessage = (isUpdate, latestVersion) => {
37852
37988
  if (isSisyphusEnabled) {
37853
37989
  return isUpdate ? `Sisyphus on steroids is steering OpenCode.
@@ -37861,6 +37997,8 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
37861
37997
  event: ({ event }) => {
37862
37998
  if (event.type !== "session.created")
37863
37999
  return;
38000
+ if (isCliRunMode)
38001
+ return;
37864
38002
  if (hasChecked)
37865
38003
  return;
37866
38004
  const props = event.properties;
@@ -42046,6 +42184,265 @@ agent-browser trace stop trace.zip # Stop and save trace
42046
42184
  Install: \`bun add -g agent-browser && agent-browser install\`. Run \`agent-browser --help\` for all commands. Repo: https://github.com/vercel-labs/agent-browser`,
42047
42185
  allowedTools: ["Bash(agent-browser:*)"]
42048
42186
  };
42187
+ // src/features/builtin-skills/skills/playwright-cli.ts
42188
+ var playwrightCliSkill = {
42189
+ name: "playwright",
42190
+ description: "MUST USE for any browser-related tasks. Browser automation via playwright-cli - verification, browsing, information gathering, web scraping, testing, screenshots, and all browser interactions.",
42191
+ template: `# Browser Automation with playwright-cli
42192
+
42193
+ ## Quick start
42194
+
42195
+ \`\`\`bash
42196
+ # open new browser
42197
+ playwright-cli open
42198
+ # navigate to a page
42199
+ playwright-cli goto https://playwright.dev
42200
+ # interact with the page using refs from the snapshot
42201
+ playwright-cli click e15
42202
+ playwright-cli type "page.click"
42203
+ playwright-cli press Enter
42204
+ # take a screenshot
42205
+ playwright-cli screenshot
42206
+ # close the browser
42207
+ playwright-cli close
42208
+ \`\`\`
42209
+
42210
+ ## Commands
42211
+
42212
+ ### Core
42213
+
42214
+ \`\`\`bash
42215
+ playwright-cli open
42216
+ # open and navigate right away
42217
+ playwright-cli open https://example.com/
42218
+ playwright-cli goto https://playwright.dev
42219
+ playwright-cli type "search query"
42220
+ playwright-cli click e3
42221
+ playwright-cli dblclick e7
42222
+ playwright-cli fill e5 "user@example.com"
42223
+ playwright-cli drag e2 e8
42224
+ playwright-cli hover e4
42225
+ playwright-cli select e9 "option-value"
42226
+ playwright-cli upload ./document.pdf
42227
+ playwright-cli check e12
42228
+ playwright-cli uncheck e12
42229
+ playwright-cli snapshot
42230
+ playwright-cli snapshot --filename=after-click.yaml
42231
+ playwright-cli eval "document.title"
42232
+ playwright-cli eval "el => el.textContent" e5
42233
+ playwright-cli dialog-accept
42234
+ playwright-cli dialog-accept "confirmation text"
42235
+ playwright-cli dialog-dismiss
42236
+ playwright-cli resize 1920 1080
42237
+ playwright-cli close
42238
+ \`\`\`
42239
+
42240
+ ### Navigation
42241
+
42242
+ \`\`\`bash
42243
+ playwright-cli go-back
42244
+ playwright-cli go-forward
42245
+ playwright-cli reload
42246
+ \`\`\`
42247
+
42248
+ ### Keyboard
42249
+
42250
+ \`\`\`bash
42251
+ playwright-cli press Enter
42252
+ playwright-cli press ArrowDown
42253
+ playwright-cli keydown Shift
42254
+ playwright-cli keyup Shift
42255
+ \`\`\`
42256
+
42257
+ ### Mouse
42258
+
42259
+ \`\`\`bash
42260
+ playwright-cli mousemove 150 300
42261
+ playwright-cli mousedown
42262
+ playwright-cli mousedown right
42263
+ playwright-cli mouseup
42264
+ playwright-cli mouseup right
42265
+ playwright-cli mousewheel 0 100
42266
+ \`\`\`
42267
+
42268
+ ### Save as
42269
+
42270
+ \`\`\`bash
42271
+ playwright-cli screenshot
42272
+ playwright-cli screenshot e5
42273
+ playwright-cli screenshot --filename=page.png
42274
+ playwright-cli pdf --filename=page.pdf
42275
+ \`\`\`
42276
+
42277
+ ### Tabs
42278
+
42279
+ \`\`\`bash
42280
+ playwright-cli tab-list
42281
+ playwright-cli tab-new
42282
+ playwright-cli tab-new https://example.com/page
42283
+ playwright-cli tab-close
42284
+ playwright-cli tab-close 2
42285
+ playwright-cli tab-select 0
42286
+ \`\`\`
42287
+
42288
+ ### Storage
42289
+
42290
+ \`\`\`bash
42291
+ playwright-cli state-save
42292
+ playwright-cli state-save auth.json
42293
+ playwright-cli state-load auth.json
42294
+
42295
+ # Cookies
42296
+ playwright-cli cookie-list
42297
+ playwright-cli cookie-list --domain=example.com
42298
+ playwright-cli cookie-get session_id
42299
+ playwright-cli cookie-set session_id abc123
42300
+ playwright-cli cookie-set session_id abc123 --domain=example.com --httpOnly --secure
42301
+ playwright-cli cookie-delete session_id
42302
+ playwright-cli cookie-clear
42303
+
42304
+ # LocalStorage
42305
+ playwright-cli localstorage-list
42306
+ playwright-cli localstorage-get theme
42307
+ playwright-cli localstorage-set theme dark
42308
+ playwright-cli localstorage-delete theme
42309
+ playwright-cli localstorage-clear
42310
+
42311
+ # SessionStorage
42312
+ playwright-cli sessionstorage-list
42313
+ playwright-cli sessionstorage-get step
42314
+ playwright-cli sessionstorage-set step 3
42315
+ playwright-cli sessionstorage-delete step
42316
+ playwright-cli sessionstorage-clear
42317
+ \`\`\`
42318
+
42319
+ ### Network
42320
+
42321
+ \`\`\`bash
42322
+ playwright-cli route "**/*.jpg" --status=404
42323
+ playwright-cli route "https://api.example.com/**" --body='{"mock": true}'
42324
+ playwright-cli route-list
42325
+ playwright-cli unroute "**/*.jpg"
42326
+ playwright-cli unroute
42327
+ \`\`\`
42328
+
42329
+ ### DevTools
42330
+
42331
+ \`\`\`bash
42332
+ playwright-cli console
42333
+ playwright-cli console warning
42334
+ playwright-cli network
42335
+ playwright-cli run-code "async page => await page.context().grantPermissions(['geolocation'])"
42336
+ playwright-cli tracing-start
42337
+ playwright-cli tracing-stop
42338
+ playwright-cli video-start
42339
+ playwright-cli video-stop video.webm
42340
+ \`\`\`
42341
+
42342
+ ### Install
42343
+
42344
+ \`\`\`bash
42345
+ playwright-cli install --skills
42346
+ playwright-cli install-browser
42347
+ \`\`\`
42348
+
42349
+ ### Configuration
42350
+ \`\`\`bash
42351
+ # Use specific browser when creating session
42352
+ playwright-cli open --browser=chrome
42353
+ playwright-cli open --browser=firefox
42354
+ playwright-cli open --browser=webkit
42355
+ playwright-cli open --browser=msedge
42356
+ # Connect to browser via extension
42357
+ playwright-cli open --extension
42358
+
42359
+ # Use persistent profile (by default profile is in-memory)
42360
+ playwright-cli open --persistent
42361
+ # Use persistent profile with custom directory
42362
+ playwright-cli open --profile=/path/to/profile
42363
+
42364
+ # Start with config file
42365
+ playwright-cli open --config=my-config.json
42366
+
42367
+ # Close the browser
42368
+ playwright-cli close
42369
+ # Delete user data for the default session
42370
+ playwright-cli delete-data
42371
+ \`\`\`
42372
+
42373
+ ### Browser Sessions
42374
+
42375
+ \`\`\`bash
42376
+ # create new browser session named "mysession" with persistent profile
42377
+ playwright-cli -s=mysession open example.com --persistent
42378
+ # same with manually specified profile directory (use when requested explicitly)
42379
+ playwright-cli -s=mysession open example.com --profile=/path/to/profile
42380
+ playwright-cli -s=mysession click e6
42381
+ playwright-cli -s=mysession close # stop a named browser
42382
+ playwright-cli -s=mysession delete-data # delete user data for persistent session
42383
+
42384
+ playwright-cli list
42385
+ # Close all browsers
42386
+ playwright-cli close-all
42387
+ # Forcefully kill all browser processes
42388
+ playwright-cli kill-all
42389
+ \`\`\`
42390
+
42391
+ ## Example: Form submission
42392
+
42393
+ \`\`\`bash
42394
+ playwright-cli open https://example.com/form
42395
+ playwright-cli snapshot
42396
+
42397
+ playwright-cli fill e1 "user@example.com"
42398
+ playwright-cli fill e2 "password123"
42399
+ playwright-cli click e3
42400
+ playwright-cli snapshot
42401
+ playwright-cli close
42402
+ \`\`\`
42403
+
42404
+ ## Example: Multi-tab workflow
42405
+
42406
+ \`\`\`bash
42407
+ playwright-cli open https://example.com
42408
+ playwright-cli tab-new https://example.com/other
42409
+ playwright-cli tab-list
42410
+ playwright-cli tab-select 0
42411
+ playwright-cli snapshot
42412
+ playwright-cli close
42413
+ \`\`\`
42414
+
42415
+ ## Example: Debugging with DevTools
42416
+
42417
+ \`\`\`bash
42418
+ playwright-cli open https://example.com
42419
+ playwright-cli click e4
42420
+ playwright-cli fill e7 "test"
42421
+ playwright-cli console
42422
+ playwright-cli network
42423
+ playwright-cli close
42424
+ \`\`\`
42425
+
42426
+ \`\`\`bash
42427
+ playwright-cli open https://example.com
42428
+ playwright-cli tracing-start
42429
+ playwright-cli click e4
42430
+ playwright-cli fill e7 "test"
42431
+ playwright-cli tracing-stop
42432
+ playwright-cli close
42433
+ \`\`\`
42434
+
42435
+ ## Specific tasks
42436
+
42437
+ * **Request mocking** [references/request-mocking.md](references/request-mocking.md)
42438
+ * **Running Playwright code** [references/running-code.md](references/running-code.md)
42439
+ * **Browser session management** [references/session-management.md](references/session-management.md)
42440
+ * **Storage state (cookies, localStorage)** [references/storage-state.md](references/storage-state.md)
42441
+ * **Test generation** [references/test-generation.md](references/test-generation.md)
42442
+ * **Tracing** [references/tracing.md](references/tracing.md)
42443
+ * **Video recording** [references/video-recording.md](references/video-recording.md)`,
42444
+ allowedTools: ["Bash(playwright-cli:*)"]
42445
+ };
42049
42446
  // src/features/builtin-skills/skills/frontend-ui-ux.ts
42050
42447
  var frontendUiUxSkill = {
42051
42448
  name: "frontend-ui-ux",
@@ -43455,7 +43852,14 @@ EOF
43455
43852
  // src/features/builtin-skills/skills.ts
43456
43853
  function createBuiltinSkills(options = {}) {
43457
43854
  const { browserProvider = "playwright", disabledSkills } = options;
43458
- const browserSkill = browserProvider === "agent-browser" ? agentBrowserSkill : playwrightSkill;
43855
+ let browserSkill;
43856
+ if (browserProvider === "agent-browser") {
43857
+ browserSkill = agentBrowserSkill;
43858
+ } else if (browserProvider === "playwright-cli") {
43859
+ browserSkill = playwrightCliSkill;
43860
+ } else {
43861
+ browserSkill = playwrightSkill;
43862
+ }
43459
43863
  const skills = [browserSkill, frontendUiUxSkill, gitMasterSkill, devBrowserSkill];
43460
43864
  if (!disabledSkills) {
43461
43865
  return skills;
@@ -45835,9 +46239,14 @@ function createUnstableAgentBabysitterHook(ctx, options) {
45835
46239
  // src/hooks/preemptive-compaction.ts
45836
46240
  init_logger();
45837
46241
  var DEFAULT_ACTUAL_LIMIT = 200000;
45838
- var ANTHROPIC_ACTUAL_LIMIT3 = process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : DEFAULT_ACTUAL_LIMIT;
46242
+ function getAnthropicActualLimit3(modelCacheState) {
46243
+ return (modelCacheState?.anthropicContext1MEnabled ?? false) || process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : DEFAULT_ACTUAL_LIMIT;
46244
+ }
45839
46245
  var PREEMPTIVE_COMPACTION_THRESHOLD = 0.78;
45840
- function createPreemptiveCompactionHook(ctx) {
46246
+ function isAnthropicProvider2(providerID) {
46247
+ return providerID === "anthropic" || providerID === "google-vertex-anthropic";
46248
+ }
46249
+ function createPreemptiveCompactionHook(ctx, modelCacheState) {
45841
46250
  const compactionInProgress = new Set;
45842
46251
  const compactedSessions = new Set;
45843
46252
  const tokenCache = new Map;
@@ -45848,7 +46257,7 @@ function createPreemptiveCompactionHook(ctx) {
45848
46257
  const cached2 = tokenCache.get(sessionID);
45849
46258
  if (!cached2)
45850
46259
  return;
45851
- const actualLimit = cached2.providerID === "anthropic" ? ANTHROPIC_ACTUAL_LIMIT3 : DEFAULT_ACTUAL_LIMIT;
46260
+ const actualLimit = isAnthropicProvider2(cached2.providerID) ? getAnthropicActualLimit3(modelCacheState) : DEFAULT_ACTUAL_LIMIT;
45852
46261
  const lastTokens = cached2.tokens;
45853
46262
  const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0);
45854
46263
  const usageRatio = totalInputTokens / actualLimit;
@@ -45982,13 +46391,332 @@ function createWriteExistingFileGuardHook(ctx) {
45982
46391
  }
45983
46392
  };
45984
46393
  }
46394
+ // src/tools/hashline-edit/constants.ts
46395
+ var HASH_DICT = [
46396
+ "00",
46397
+ "01",
46398
+ "02",
46399
+ "03",
46400
+ "04",
46401
+ "05",
46402
+ "06",
46403
+ "07",
46404
+ "08",
46405
+ "09",
46406
+ "0a",
46407
+ "0b",
46408
+ "0c",
46409
+ "0d",
46410
+ "0e",
46411
+ "0f",
46412
+ "10",
46413
+ "11",
46414
+ "12",
46415
+ "13",
46416
+ "14",
46417
+ "15",
46418
+ "16",
46419
+ "17",
46420
+ "18",
46421
+ "19",
46422
+ "1a",
46423
+ "1b",
46424
+ "1c",
46425
+ "1d",
46426
+ "1e",
46427
+ "1f",
46428
+ "20",
46429
+ "21",
46430
+ "22",
46431
+ "23",
46432
+ "24",
46433
+ "25",
46434
+ "26",
46435
+ "27",
46436
+ "28",
46437
+ "29",
46438
+ "2a",
46439
+ "2b",
46440
+ "2c",
46441
+ "2d",
46442
+ "2e",
46443
+ "2f",
46444
+ "30",
46445
+ "31",
46446
+ "32",
46447
+ "33",
46448
+ "34",
46449
+ "35",
46450
+ "36",
46451
+ "37",
46452
+ "38",
46453
+ "39",
46454
+ "3a",
46455
+ "3b",
46456
+ "3c",
46457
+ "3d",
46458
+ "3e",
46459
+ "3f",
46460
+ "40",
46461
+ "41",
46462
+ "42",
46463
+ "43",
46464
+ "44",
46465
+ "45",
46466
+ "46",
46467
+ "47",
46468
+ "48",
46469
+ "49",
46470
+ "4a",
46471
+ "4b",
46472
+ "4c",
46473
+ "4d",
46474
+ "4e",
46475
+ "4f",
46476
+ "50",
46477
+ "51",
46478
+ "52",
46479
+ "53",
46480
+ "54",
46481
+ "55",
46482
+ "56",
46483
+ "57",
46484
+ "58",
46485
+ "59",
46486
+ "5a",
46487
+ "5b",
46488
+ "5c",
46489
+ "5d",
46490
+ "5e",
46491
+ "5f",
46492
+ "60",
46493
+ "61",
46494
+ "62",
46495
+ "63",
46496
+ "64",
46497
+ "65",
46498
+ "66",
46499
+ "67",
46500
+ "68",
46501
+ "69",
46502
+ "6a",
46503
+ "6b",
46504
+ "6c",
46505
+ "6d",
46506
+ "6e",
46507
+ "6f",
46508
+ "70",
46509
+ "71",
46510
+ "72",
46511
+ "73",
46512
+ "74",
46513
+ "75",
46514
+ "76",
46515
+ "77",
46516
+ "78",
46517
+ "79",
46518
+ "7a",
46519
+ "7b",
46520
+ "7c",
46521
+ "7d",
46522
+ "7e",
46523
+ "7f",
46524
+ "80",
46525
+ "81",
46526
+ "82",
46527
+ "83",
46528
+ "84",
46529
+ "85",
46530
+ "86",
46531
+ "87",
46532
+ "88",
46533
+ "89",
46534
+ "8a",
46535
+ "8b",
46536
+ "8c",
46537
+ "8d",
46538
+ "8e",
46539
+ "8f",
46540
+ "90",
46541
+ "91",
46542
+ "92",
46543
+ "93",
46544
+ "94",
46545
+ "95",
46546
+ "96",
46547
+ "97",
46548
+ "98",
46549
+ "99",
46550
+ "9a",
46551
+ "9b",
46552
+ "9c",
46553
+ "9d",
46554
+ "9e",
46555
+ "9f",
46556
+ "a0",
46557
+ "a1",
46558
+ "a2",
46559
+ "a3",
46560
+ "a4",
46561
+ "a5",
46562
+ "a6",
46563
+ "a7",
46564
+ "a8",
46565
+ "a9",
46566
+ "aa",
46567
+ "ab",
46568
+ "ac",
46569
+ "ad",
46570
+ "ae",
46571
+ "af",
46572
+ "b0",
46573
+ "b1",
46574
+ "b2",
46575
+ "b3",
46576
+ "b4",
46577
+ "b5",
46578
+ "b6",
46579
+ "b7",
46580
+ "b8",
46581
+ "b9",
46582
+ "ba",
46583
+ "bb",
46584
+ "bc",
46585
+ "bd",
46586
+ "be",
46587
+ "bf",
46588
+ "c0",
46589
+ "c1",
46590
+ "c2",
46591
+ "c3",
46592
+ "c4",
46593
+ "c5",
46594
+ "c6",
46595
+ "c7",
46596
+ "c8",
46597
+ "c9",
46598
+ "ca",
46599
+ "cb",
46600
+ "cc",
46601
+ "cd",
46602
+ "ce",
46603
+ "cf",
46604
+ "d0",
46605
+ "d1",
46606
+ "d2",
46607
+ "d3",
46608
+ "d4",
46609
+ "d5",
46610
+ "d6",
46611
+ "d7",
46612
+ "d8",
46613
+ "d9",
46614
+ "da",
46615
+ "db",
46616
+ "dc",
46617
+ "dd",
46618
+ "de",
46619
+ "df",
46620
+ "e0",
46621
+ "e1",
46622
+ "e2",
46623
+ "e3",
46624
+ "e4",
46625
+ "e5",
46626
+ "e6",
46627
+ "e7",
46628
+ "e8",
46629
+ "e9",
46630
+ "ea",
46631
+ "eb",
46632
+ "ec",
46633
+ "ed",
46634
+ "ee",
46635
+ "ef",
46636
+ "f0",
46637
+ "f1",
46638
+ "f2",
46639
+ "f3",
46640
+ "f4",
46641
+ "f5",
46642
+ "f6",
46643
+ "f7",
46644
+ "f8",
46645
+ "f9",
46646
+ "fa",
46647
+ "fb",
46648
+ "fc",
46649
+ "fd",
46650
+ "fe",
46651
+ "ff"
46652
+ ];
46653
+
46654
+ // src/tools/hashline-edit/hash-computation.ts
46655
+ function computeLineHash(_lineNumber, content) {
46656
+ const stripped = content.replace(/\s+/g, "");
46657
+ const hash2 = Bun.hash.xxHash32(stripped);
46658
+ const index = hash2 % 256;
46659
+ return HASH_DICT[index];
46660
+ }
46661
+
46662
+ // src/hooks/hashline-read-enhancer/hook.ts
46663
+ var READ_LINE_PATTERN = /^(\d+): (.*)$/;
46664
+ function isReadTool(toolName) {
46665
+ return toolName.toLowerCase() === "read";
46666
+ }
46667
+ function shouldProcess(config2) {
46668
+ return config2.hashline_edit?.enabled ?? false;
46669
+ }
46670
+ function isTextFile(output) {
46671
+ const firstLine = output.split(`
46672
+ `)[0] ?? "";
46673
+ return READ_LINE_PATTERN.test(firstLine);
46674
+ }
46675
+ function transformLine(line) {
46676
+ const match = READ_LINE_PATTERN.exec(line);
46677
+ if (!match) {
46678
+ return line;
46679
+ }
46680
+ const lineNumber = parseInt(match[1], 10);
46681
+ const content = match[2];
46682
+ const hash2 = computeLineHash(lineNumber, content);
46683
+ return `${lineNumber}:${hash2}|${content}`;
46684
+ }
46685
+ function transformOutput(output) {
46686
+ if (!output) {
46687
+ return output;
46688
+ }
46689
+ if (!isTextFile(output)) {
46690
+ return output;
46691
+ }
46692
+ const lines = output.split(`
46693
+ `);
46694
+ return lines.map(transformLine).join(`
46695
+ `);
46696
+ }
46697
+ function createHashlineReadEnhancerHook(_ctx, config2) {
46698
+ return {
46699
+ "tool.execute.after": async (input, output) => {
46700
+ if (!isReadTool(input.tool)) {
46701
+ return;
46702
+ }
46703
+ if (typeof output.output !== "string") {
46704
+ return;
46705
+ }
46706
+ if (!shouldProcess(config2)) {
46707
+ return;
46708
+ }
46709
+ output.output = transformOutput(output.output);
46710
+ }
46711
+ };
46712
+ }
45985
46713
  // src/hooks/anthropic-effort/hook.ts
45986
46714
  var OPUS_4_6_PATTERN = /claude-opus-4[-.]6/i;
45987
46715
  function normalizeModelID2(modelID) {
45988
46716
  return modelID.replace(/\.(\d+)/g, "-$1");
45989
46717
  }
45990
46718
  function isClaudeProvider(providerID, modelID) {
45991
- if (["anthropic", "opencode"].includes(providerID))
46719
+ if (["anthropic", "google-vertex-anthropic", "opencode"].includes(providerID))
45992
46720
  return true;
45993
46721
  if (providerID === "github-copilot" && modelID.toLowerCase().includes("claude"))
45994
46722
  return true;
@@ -47987,10 +48715,11 @@ function createSgResultFromStdout(stdout) {
47987
48715
 
47988
48716
  // src/tools/ast-grep/cli.ts
47989
48717
  async function runSg(options) {
48718
+ const shouldSeparateWritePass = !!(options.rewrite && options.updateAll);
47990
48719
  const args = ["run", "-p", options.pattern, "--lang", options.lang, "--json=compact"];
47991
48720
  if (options.rewrite) {
47992
48721
  args.push("-r", options.rewrite);
47993
- if (options.updateAll) {
48722
+ if (options.updateAll && !shouldSeparateWritePass) {
47994
48723
  args.push("--update-all");
47995
48724
  }
47996
48725
  }
@@ -48083,7 +48812,26 @@ async function runSg(options) {
48083
48812
  }
48084
48813
  return { matches: [], totalMatches: 0, truncated: false };
48085
48814
  }
48086
- return createSgResultFromStdout(stdout);
48815
+ const jsonResult = createSgResultFromStdout(stdout);
48816
+ if (shouldSeparateWritePass && jsonResult.matches.length > 0) {
48817
+ const writeArgs = args.filter((a) => a !== "--json=compact");
48818
+ writeArgs.push("--update-all");
48819
+ const writeProc = spawn10([cliPath, ...writeArgs], {
48820
+ stdout: "pipe",
48821
+ stderr: "pipe"
48822
+ });
48823
+ try {
48824
+ const writeOutput = await collectProcessOutputWithTimeout(writeProc, timeout);
48825
+ if (writeOutput.exitCode !== 0) {
48826
+ const errorDetail = writeOutput.stderr.trim() || `ast-grep exited with code ${writeOutput.exitCode}`;
48827
+ return { ...jsonResult, error: `Replace failed: ${errorDetail}` };
48828
+ }
48829
+ } catch (error45) {
48830
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
48831
+ return { ...jsonResult, error: `Replace failed: ${errorMessage}` };
48832
+ }
48833
+ }
48834
+ return jsonResult;
48087
48835
  }
48088
48836
 
48089
48837
  // src/tools/ast-grep/result-formatter.ts
@@ -51027,7 +51775,7 @@ session_id: ${sessionID}
51027
51775
  }
51028
51776
 
51029
51777
  // src/tools/call-omo-agent/tools.ts
51030
- function createCallOmoAgent(ctx, backgroundManager) {
51778
+ function createCallOmoAgent(ctx, backgroundManager, disabledAgents = []) {
51031
51779
  const agentDescriptions = ALLOWED_AGENTS.map((name) => `- ${name}: Specialized agent for ${name} tasks`).join(`
51032
51780
  `);
51033
51781
  const description = CALL_OMO_AGENT_DESCRIPTION.replace("{agents}", agentDescriptions);
@@ -51048,6 +51796,9 @@ function createCallOmoAgent(ctx, backgroundManager) {
51048
51796
  }
51049
51797
  const normalizedAgent = args.subagent_type.toLowerCase();
51050
51798
  args = { ...args, subagent_type: normalizedAgent };
51799
+ if (disabledAgents.some((disabled) => disabled.toLowerCase() === normalizedAgent)) {
51800
+ return `Error: Agent "${normalizedAgent}" is disabled via disabled_agents configuration. Remove it from disabled_agents in your oh-my-opencode.json to use it.`;
51801
+ }
51051
51802
  if (args.run_in_background) {
51052
51803
  if (args.session_id) {
51053
51804
  return `Error: session_id is not supported in background mode. Use run_in_background=false to continue an existing session.`;
@@ -52240,28 +52991,49 @@ async function createSyncSession(client2, input) {
52240
52991
 
52241
52992
  // src/tools/delegate-task/sync-prompt-sender.ts
52242
52993
  init_constants();
52243
- async function sendSyncPrompt(client2, input) {
52994
+ var sendSyncPromptDeps = {
52995
+ promptWithModelSuggestionRetry,
52996
+ promptSyncWithModelSuggestionRetry
52997
+ };
52998
+ function isOracleAgent(agentToUse) {
52999
+ return agentToUse.toLowerCase() === "oracle";
53000
+ }
53001
+ function isUnexpectedEofError(error45) {
53002
+ const message = error45 instanceof Error ? error45.message : String(error45);
53003
+ const lowered = message.toLowerCase();
53004
+ return lowered.includes("unexpected eof") || lowered.includes("json parse error");
53005
+ }
53006
+ async function sendSyncPrompt(client2, input, deps = sendSyncPromptDeps) {
53007
+ const allowTask = isPlanFamily(input.agentToUse);
53008
+ const tools = {
53009
+ task: allowTask,
53010
+ call_omo_agent: true,
53011
+ question: false,
53012
+ ...getAgentToolRestrictions(input.agentToUse)
53013
+ };
53014
+ setSessionTools(input.sessionID, tools);
53015
+ const promptArgs = {
53016
+ path: { id: input.sessionID },
53017
+ body: {
53018
+ agent: input.agentToUse,
53019
+ system: input.systemContent,
53020
+ tools,
53021
+ parts: [{ type: "text", text: input.args.prompt }],
53022
+ ...input.categoryModel ? { model: { providerID: input.categoryModel.providerID, modelID: input.categoryModel.modelID } } : {},
53023
+ ...input.categoryModel?.variant ? { variant: input.categoryModel.variant } : {}
53024
+ }
53025
+ };
52244
53026
  try {
52245
- const allowTask = isPlanFamily(input.agentToUse);
52246
- const tools = {
52247
- task: allowTask,
52248
- call_omo_agent: true,
52249
- question: false,
52250
- ...getAgentToolRestrictions(input.agentToUse)
52251
- };
52252
- setSessionTools(input.sessionID, tools);
52253
- await promptWithModelSuggestionRetry(client2, {
52254
- path: { id: input.sessionID },
52255
- body: {
52256
- agent: input.agentToUse,
52257
- system: input.systemContent,
52258
- tools,
52259
- parts: [{ type: "text", text: input.args.prompt }],
52260
- ...input.categoryModel ? { model: { providerID: input.categoryModel.providerID, modelID: input.categoryModel.modelID } } : {},
52261
- ...input.categoryModel?.variant ? { variant: input.categoryModel.variant } : {}
52262
- }
52263
- });
53027
+ await deps.promptWithModelSuggestionRetry(client2, promptArgs);
52264
53028
  } catch (promptError) {
53029
+ if (isOracleAgent(input.agentToUse) && isUnexpectedEofError(promptError)) {
53030
+ try {
53031
+ await deps.promptSyncWithModelSuggestionRetry(client2, promptArgs);
53032
+ return null;
53033
+ } catch (oracleRetryError) {
53034
+ promptError = oracleRetryError;
53035
+ }
53036
+ }
52265
53037
  if (input.toastManager && input.taskId !== undefined) {
52266
53038
  input.toastManager.removeTask(input.taskId);
52267
53039
  }
@@ -52689,6 +53461,7 @@ Available categories: ${categoryNames.join(", ")}`
52689
53461
  }
52690
53462
  // src/tools/delegate-task/subagent-resolver.ts
52691
53463
  init_constants();
53464
+ init_logger();
52692
53465
  async function resolveSubagentExecution(args, executorCtx, parentAgent, categoryExamples) {
52693
53466
  const { client: client2, agentOverrides } = executorCtx;
52694
53467
  if (!args.subagent_type?.trim()) {
@@ -52764,7 +53537,19 @@ Create the work plan directly - that's your job as the planning agent.`
52764
53537
  if (!categoryModel && matchedAgent.model) {
52765
53538
  categoryModel = matchedAgent.model;
52766
53539
  }
52767
- } catch {}
53540
+ } catch (error45) {
53541
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
53542
+ log("[delegate-task] Failed to resolve subagent execution", {
53543
+ requestedAgent: agentToUse,
53544
+ parentAgent,
53545
+ error: errorMessage
53546
+ });
53547
+ return {
53548
+ agentToUse: "",
53549
+ categoryModel: undefined,
53550
+ error: `Failed to delegate to agent "${agentToUse}": ${errorMessage}`
53551
+ };
53552
+ }
52768
53553
  return { agentToUse, categoryModel };
52769
53554
  }
52770
53555
  // src/tools/delegate-task/tools.ts
@@ -53492,6 +54277,223 @@ async function handleUpdate(args, config3, ctx, context) {
53492
54277
  return JSON.stringify({ error: "internal_error" });
53493
54278
  }
53494
54279
  }
54280
+ // src/tools/hashline-edit/validation.ts
54281
+ function parseLineRef(ref) {
54282
+ const match = ref.match(/^(\d+):([0-9a-f]{2})$/);
54283
+ if (!match) {
54284
+ throw new Error(`Invalid line reference format: "${ref}". Expected format: "LINE:HASH" (e.g., "42:a3")`);
54285
+ }
54286
+ return {
54287
+ line: Number.parseInt(match[1], 10),
54288
+ hash: match[2]
54289
+ };
54290
+ }
54291
+ function validateLineRef(lines, ref) {
54292
+ const { line, hash: hash2 } = parseLineRef(ref);
54293
+ if (line < 1 || line > lines.length) {
54294
+ throw new Error(`Line number ${line} out of bounds. File has ${lines.length} lines.`);
54295
+ }
54296
+ const content = lines[line - 1];
54297
+ const currentHash = computeLineHash(line, content);
54298
+ if (currentHash !== hash2) {
54299
+ throw new Error(`Hash mismatch at line ${line}. Expected hash: ${hash2}, current hash: ${currentHash}. ` + `Line content may have changed. Current content: "${content}"`);
54300
+ }
54301
+ }
54302
+ // src/tools/hashline-edit/edit-operations.ts
54303
+ function unescapeNewlines(text) {
54304
+ return text.replace(/\\n/g, `
54305
+ `);
54306
+ }
54307
+ function getEditLineNumber(edit) {
54308
+ switch (edit.type) {
54309
+ case "set_line":
54310
+ return parseLineRef(edit.line).line;
54311
+ case "replace_lines":
54312
+ return parseLineRef(edit.end_line).line;
54313
+ case "insert_after":
54314
+ return parseLineRef(edit.line).line;
54315
+ case "replace":
54316
+ return Number.NEGATIVE_INFINITY;
54317
+ default:
54318
+ return Number.POSITIVE_INFINITY;
54319
+ }
54320
+ }
54321
+ function applyHashlineEdits(content, edits) {
54322
+ if (edits.length === 0) {
54323
+ return content;
54324
+ }
54325
+ const sortedEdits = [...edits].sort((a, b) => getEditLineNumber(b) - getEditLineNumber(a));
54326
+ let result = content;
54327
+ let lines = result.split(`
54328
+ `);
54329
+ for (const edit of sortedEdits) {
54330
+ switch (edit.type) {
54331
+ case "set_line": {
54332
+ validateLineRef(lines, edit.line);
54333
+ const { line } = parseLineRef(edit.line);
54334
+ lines[line - 1] = unescapeNewlines(edit.text);
54335
+ break;
54336
+ }
54337
+ case "replace_lines": {
54338
+ validateLineRef(lines, edit.start_line);
54339
+ validateLineRef(lines, edit.end_line);
54340
+ const { line: startLine } = parseLineRef(edit.start_line);
54341
+ const { line: endLine } = parseLineRef(edit.end_line);
54342
+ if (startLine > endLine) {
54343
+ throw new Error(`Invalid range: start line ${startLine} cannot be greater than end line ${endLine}`);
54344
+ }
54345
+ const newLines = unescapeNewlines(edit.text).split(`
54346
+ `);
54347
+ lines.splice(startLine - 1, endLine - startLine + 1, ...newLines);
54348
+ break;
54349
+ }
54350
+ case "insert_after": {
54351
+ validateLineRef(lines, edit.line);
54352
+ const { line } = parseLineRef(edit.line);
54353
+ const newLines = unescapeNewlines(edit.text).split(`
54354
+ `);
54355
+ lines.splice(line, 0, ...newLines);
54356
+ break;
54357
+ }
54358
+ case "replace": {
54359
+ result = lines.join(`
54360
+ `);
54361
+ if (!result.includes(edit.old_text)) {
54362
+ throw new Error(`Text not found: "${edit.old_text}"`);
54363
+ }
54364
+ result = result.replaceAll(edit.old_text, unescapeNewlines(edit.new_text));
54365
+ lines = result.split(`
54366
+ `);
54367
+ break;
54368
+ }
54369
+ }
54370
+ }
54371
+ return lines.join(`
54372
+ `);
54373
+ }
54374
+ // src/tools/hashline-edit/tools.ts
54375
+ function generateDiff(oldContent, newContent, filePath) {
54376
+ const oldLines = oldContent.split(`
54377
+ `);
54378
+ const newLines = newContent.split(`
54379
+ `);
54380
+ let diff = `--- ${filePath}
54381
+ +++ ${filePath}
54382
+ `;
54383
+ const maxLines = Math.max(oldLines.length, newLines.length);
54384
+ for (let i2 = 0;i2 < maxLines; i2++) {
54385
+ const oldLine = oldLines[i2] ?? "";
54386
+ const newLine = newLines[i2] ?? "";
54387
+ const lineNum = i2 + 1;
54388
+ const hash2 = computeLineHash(lineNum, newLine);
54389
+ if (i2 >= oldLines.length) {
54390
+ diff += `+ ${lineNum}:${hash2}|${newLine}
54391
+ `;
54392
+ } else if (i2 >= newLines.length) {
54393
+ diff += `- ${lineNum}: |${oldLine}
54394
+ `;
54395
+ } else if (oldLine !== newLine) {
54396
+ diff += `- ${lineNum}: |${oldLine}
54397
+ `;
54398
+ diff += `+ ${lineNum}:${hash2}|${newLine}
54399
+ `;
54400
+ }
54401
+ }
54402
+ return diff;
54403
+ }
54404
+ function createHashlineEditTool() {
54405
+ return tool({
54406
+ description: `Edit files using LINE:HASH format for precise, safe modifications.
54407
+
54408
+ LINE:HASH FORMAT:
54409
+ Each line reference must be in "LINE:HASH" format where:
54410
+ - LINE: 1-based line number
54411
+ - HASH: First 2 characters of xxHash32 hash of line content (computed with computeLineHash)
54412
+ - Example: "5:a3|const x = 1" means line 5 with hash "a3"
54413
+
54414
+ GETTING HASHES:
54415
+ Use the read tool - it returns lines in "LINE:HASH|content" format.
54416
+
54417
+ FOUR OPERATION TYPES:
54418
+
54419
+ 1. set_line: Replace a single line
54420
+ { "type": "set_line", "line": "5:a3", "text": "const y = 2" }
54421
+
54422
+ 2. replace_lines: Replace a range of lines
54423
+ { "type": "replace_lines", "start_line": "5:a3", "end_line": "7:b2", "text": "new
54424
+ content" }
54425
+
54426
+ 3. insert_after: Insert lines after a specific line
54427
+ { "type": "insert_after", "line": "5:a3", "text": "console.log('hi')" }
54428
+
54429
+ 4. replace: Simple text replacement (no hash validation)
54430
+ { "type": "replace", "old_text": "foo", "new_text": "bar" }
54431
+
54432
+ HASH MISMATCH HANDLING:
54433
+ If the hash doesn't match the current content, the edit fails with a hash mismatch error. This prevents editing stale content.
54434
+
54435
+ BOTTOM-UP APPLICATION:
54436
+ Edits are applied from bottom to top (highest line numbers first) to preserve line number references.
54437
+
54438
+ ESCAPING:
54439
+ Use \\n in text to represent literal newlines.`,
54440
+ args: {
54441
+ path: tool.schema.string().describe("Absolute path to the file to edit"),
54442
+ edits: tool.schema.array(tool.schema.union([
54443
+ tool.schema.object({
54444
+ type: tool.schema.literal("set_line"),
54445
+ line: tool.schema.string().describe("Line reference in LINE:HASH format"),
54446
+ text: tool.schema.string().describe("New content for the line")
54447
+ }),
54448
+ tool.schema.object({
54449
+ type: tool.schema.literal("replace_lines"),
54450
+ start_line: tool.schema.string().describe("Start line in LINE:HASH format"),
54451
+ end_line: tool.schema.string().describe("End line in LINE:HASH format"),
54452
+ text: tool.schema.string().describe("New content to replace the range")
54453
+ }),
54454
+ tool.schema.object({
54455
+ type: tool.schema.literal("insert_after"),
54456
+ line: tool.schema.string().describe("Line reference in LINE:HASH format"),
54457
+ text: tool.schema.string().describe("Content to insert after the line")
54458
+ }),
54459
+ tool.schema.object({
54460
+ type: tool.schema.literal("replace"),
54461
+ old_text: tool.schema.string().describe("Text to find"),
54462
+ new_text: tool.schema.string().describe("Replacement text")
54463
+ })
54464
+ ])).describe("Array of edit operations to apply")
54465
+ },
54466
+ execute: async (args) => {
54467
+ try {
54468
+ const { path: filePath, edits } = args;
54469
+ if (!filePath) {
54470
+ return "Error: path parameter is required";
54471
+ }
54472
+ if (!edits || !Array.isArray(edits) || edits.length === 0) {
54473
+ return "Error: edits parameter must be a non-empty array";
54474
+ }
54475
+ const file2 = Bun.file(filePath);
54476
+ const exists = await file2.exists();
54477
+ if (!exists) {
54478
+ return `Error: File not found: ${filePath}`;
54479
+ }
54480
+ const oldContent = await file2.text();
54481
+ const newContent = applyHashlineEdits(oldContent, edits);
54482
+ await Bun.write(filePath, newContent);
54483
+ const diff = generateDiff(oldContent, newContent, filePath);
54484
+ return `Successfully applied ${edits.length} edit(s) to ${filePath}
54485
+
54486
+ ${diff}`;
54487
+ } catch (error45) {
54488
+ const message = error45 instanceof Error ? error45.message : String(error45);
54489
+ if (message.includes("hash")) {
54490
+ return `Error: Hash mismatch - ${message}`;
54491
+ }
54492
+ return `Error: ${message}`;
54493
+ }
54494
+ }
54495
+ });
54496
+ }
53495
54497
  // src/tools/index.ts
53496
54498
  function createBackgroundTools(manager, client2) {
53497
54499
  const outputManager = manager;
@@ -53512,10 +54514,10 @@ var builtinTools = {
53512
54514
 
53513
54515
  // src/plugin/hooks/create-session-hooks.ts
53514
54516
  function createSessionHooks(args) {
53515
- const { ctx, pluginConfig, isHookEnabled, safeHookEnabled } = args;
54517
+ const { ctx, pluginConfig, modelCacheState, isHookEnabled, safeHookEnabled } = args;
53516
54518
  const safeHook = (hookName, factory) => safeCreateHook(hookName, factory, { enabled: safeHookEnabled });
53517
- const contextWindowMonitor = isHookEnabled("context-window-monitor") ? safeHook("context-window-monitor", () => createContextWindowMonitorHook(ctx)) : null;
53518
- const preemptiveCompaction = isHookEnabled("preemptive-compaction") && pluginConfig.experimental?.preemptive_compaction ? safeHook("preemptive-compaction", () => createPreemptiveCompactionHook(ctx)) : null;
54519
+ const contextWindowMonitor = isHookEnabled("context-window-monitor") ? safeHook("context-window-monitor", () => createContextWindowMonitorHook(ctx, modelCacheState)) : null;
54520
+ const preemptiveCompaction = isHookEnabled("preemptive-compaction") && pluginConfig.experimental?.preemptive_compaction ? safeHook("preemptive-compaction", () => createPreemptiveCompactionHook(ctx, modelCacheState)) : null;
53519
54521
  const sessionRecovery = isHookEnabled("session-recovery") ? safeHook("session-recovery", () => createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental })) : null;
53520
54522
  let sessionNotification = null;
53521
54523
  if (isHookEnabled("session-notification")) {
@@ -53574,10 +54576,13 @@ function createSessionHooks(args) {
53574
54576
 
53575
54577
  // src/plugin/hooks/create-tool-guard-hooks.ts
53576
54578
  function createToolGuardHooks(args) {
53577
- const { ctx, pluginConfig, isHookEnabled, safeHookEnabled } = args;
54579
+ const { ctx, pluginConfig, modelCacheState, isHookEnabled, safeHookEnabled } = args;
53578
54580
  const safeHook = (hookName, factory) => safeCreateHook(hookName, factory, { enabled: safeHookEnabled });
53579
54581
  const commentChecker = isHookEnabled("comment-checker") ? safeHook("comment-checker", () => createCommentCheckerHooks(pluginConfig.comment_checker)) : null;
53580
- const toolOutputTruncator = isHookEnabled("tool-output-truncator") ? safeHook("tool-output-truncator", () => createToolOutputTruncatorHook(ctx, { experimental: pluginConfig.experimental })) : null;
54582
+ const toolOutputTruncator = isHookEnabled("tool-output-truncator") ? safeHook("tool-output-truncator", () => createToolOutputTruncatorHook(ctx, {
54583
+ modelCacheState,
54584
+ experimental: pluginConfig.experimental
54585
+ })) : null;
53581
54586
  let directoryAgentsInjector = null;
53582
54587
  if (isHookEnabled("directory-agents-injector")) {
53583
54588
  const currentVersion = getOpenCodeVersion();
@@ -53588,14 +54593,15 @@ function createToolGuardHooks(args) {
53588
54593
  nativeVersion: OPENCODE_NATIVE_AGENTS_INJECTION_VERSION
53589
54594
  });
53590
54595
  } else {
53591
- directoryAgentsInjector = safeHook("directory-agents-injector", () => createDirectoryAgentsInjectorHook(ctx));
54596
+ directoryAgentsInjector = safeHook("directory-agents-injector", () => createDirectoryAgentsInjectorHook(ctx, modelCacheState));
53592
54597
  }
53593
54598
  }
53594
- const directoryReadmeInjector = isHookEnabled("directory-readme-injector") ? safeHook("directory-readme-injector", () => createDirectoryReadmeInjectorHook(ctx)) : null;
54599
+ const directoryReadmeInjector = isHookEnabled("directory-readme-injector") ? safeHook("directory-readme-injector", () => createDirectoryReadmeInjectorHook(ctx, modelCacheState)) : null;
53595
54600
  const emptyTaskResponseDetector = isHookEnabled("empty-task-response-detector") ? safeHook("empty-task-response-detector", () => createEmptyTaskResponseDetectorHook(ctx)) : null;
53596
- const rulesInjector = isHookEnabled("rules-injector") ? safeHook("rules-injector", () => createRulesInjectorHook(ctx)) : null;
54601
+ const rulesInjector = isHookEnabled("rules-injector") ? safeHook("rules-injector", () => createRulesInjectorHook(ctx, modelCacheState)) : null;
53597
54602
  const tasksTodowriteDisabler = isHookEnabled("tasks-todowrite-disabler") ? safeHook("tasks-todowrite-disabler", () => createTasksTodowriteDisablerHook({ experimental: pluginConfig.experimental })) : null;
53598
54603
  const writeExistingFileGuard = isHookEnabled("write-existing-file-guard") ? safeHook("write-existing-file-guard", () => createWriteExistingFileGuardHook(ctx)) : null;
54604
+ const hashlineReadEnhancer = isHookEnabled("hashline-read-enhancer") ? safeHook("hashline-read-enhancer", () => createHashlineReadEnhancerHook(ctx, { hashline_edit: { enabled: pluginConfig.experimental?.hashline_edit ?? false } })) : null;
53599
54605
  return {
53600
54606
  commentChecker,
53601
54607
  toolOutputTruncator,
@@ -53604,7 +54610,8 @@ function createToolGuardHooks(args) {
53604
54610
  emptyTaskResponseDetector,
53605
54611
  rulesInjector,
53606
54612
  tasksTodowriteDisabler,
53607
- writeExistingFileGuard
54613
+ writeExistingFileGuard,
54614
+ hashlineReadEnhancer
53608
54615
  };
53609
54616
  }
53610
54617
 
@@ -53770,16 +54777,18 @@ function createTransformHooks(args) {
53770
54777
 
53771
54778
  // src/plugin/hooks/create-core-hooks.ts
53772
54779
  function createCoreHooks(args) {
53773
- const { ctx, pluginConfig, isHookEnabled, safeHookEnabled } = args;
54780
+ const { ctx, pluginConfig, modelCacheState, isHookEnabled, safeHookEnabled } = args;
53774
54781
  const session = createSessionHooks({
53775
54782
  ctx,
53776
54783
  pluginConfig,
54784
+ modelCacheState,
53777
54785
  isHookEnabled,
53778
54786
  safeHookEnabled
53779
54787
  });
53780
54788
  const tool3 = createToolGuardHooks({
53781
54789
  ctx,
53782
54790
  pluginConfig,
54791
+ modelCacheState,
53783
54792
  isHookEnabled,
53784
54793
  safeHookEnabled
53785
54794
  });
@@ -53897,6 +54906,7 @@ function createHooks(args) {
53897
54906
  const {
53898
54907
  ctx,
53899
54908
  pluginConfig,
54909
+ modelCacheState,
53900
54910
  backgroundManager,
53901
54911
  isHookEnabled,
53902
54912
  safeHookEnabled,
@@ -53906,6 +54916,7 @@ function createHooks(args) {
53906
54916
  const core3 = createCoreHooks({
53907
54917
  ctx,
53908
54918
  pluginConfig,
54919
+ modelCacheState,
53909
54920
  isHookEnabled,
53910
54921
  safeHookEnabled
53911
54922
  });
@@ -54095,7 +55106,7 @@ var POLLING_INTERVAL_MS = 3000;
54095
55106
  var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
54096
55107
 
54097
55108
  // src/features/background-agent/manager.ts
54098
- import { existsSync as existsSync61, readdirSync as readdirSync18 } from "fs";
55109
+ import { existsSync as existsSync61, readFileSync as readFileSync40, readdirSync as readdirSync18 } from "fs";
54099
55110
  import { join as join72 } from "path";
54100
55111
 
54101
55112
  class BackgroundManager {
@@ -54108,6 +55119,7 @@ class BackgroundManager {
54108
55119
  client;
54109
55120
  directory;
54110
55121
  pollingInterval;
55122
+ pollingInFlight = false;
54111
55123
  concurrencyManager;
54112
55124
  shutdownTriggered = false;
54113
55125
  config;
@@ -54119,6 +55131,7 @@ class BackgroundManager {
54119
55131
  completionTimers = new Map;
54120
55132
  idleDeferralTimers = new Map;
54121
55133
  notificationQueueByParent = new Map;
55134
+ enableParentSessionNotifications;
54122
55135
  taskHistory = new TaskHistory;
54123
55136
  constructor(ctx, config3, options) {
54124
55137
  this.tasks = new Map;
@@ -54131,6 +55144,7 @@ class BackgroundManager {
54131
55144
  this.tmuxEnabled = options?.tmuxConfig?.enabled ?? false;
54132
55145
  this.onSubagentSessionCreated = options?.onSubagentSessionCreated;
54133
55146
  this.onShutdown = options?.onShutdown;
55147
+ this.enableParentSessionNotifications = options?.enableParentSessionNotifications ?? true;
54134
55148
  this.registerProcessCleanup();
54135
55149
  }
54136
55150
  async launch(input) {
@@ -54965,13 +55979,12 @@ class BackgroundManager {
54965
55979
  } else {
54966
55980
  allComplete = true;
54967
55981
  }
55982
+ const completedTasks = allComplete ? Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending") : [];
54968
55983
  const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED";
54969
55984
  const errorInfo = task.error ? `
54970
55985
  **Error:** ${task.error}` : "";
54971
55986
  let notification;
54972
- let completedTasks = [];
54973
55987
  if (allComplete) {
54974
- completedTasks = Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending");
54975
55988
  const completedTasksText = completedTasks.map((t) => `- \`${t.id}\`: ${t.description}`).join(`
54976
55989
  `);
54977
55990
  notification = `<system-reminder>
@@ -54997,59 +56010,69 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
54997
56010
  }
54998
56011
  let agent = task.parentAgent;
54999
56012
  let model;
55000
- try {
55001
- const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionID } });
55002
- const messages = normalizeSDKResponse(messagesResp, []);
55003
- for (let i2 = messages.length - 1;i2 >= 0; i2--) {
55004
- const info = messages[i2].info;
55005
- if (info?.agent || info?.model || info?.modelID && info?.providerID) {
55006
- agent = info.agent ?? task.parentAgent;
55007
- model = info.model ?? (info.providerID && info.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined);
55008
- break;
56013
+ if (this.enableParentSessionNotifications) {
56014
+ try {
56015
+ const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionID } });
56016
+ const messages = normalizeSDKResponse(messagesResp, []);
56017
+ for (let i2 = messages.length - 1;i2 >= 0; i2--) {
56018
+ const info = messages[i2].info;
56019
+ if (isCompactionAgent(info?.agent)) {
56020
+ continue;
56021
+ }
56022
+ if (info?.agent || info?.model || info?.modelID && info?.providerID) {
56023
+ agent = info.agent ?? task.parentAgent;
56024
+ model = info.model ?? (info.providerID && info.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined);
56025
+ break;
56026
+ }
55009
56027
  }
55010
- }
55011
- } catch (error45) {
55012
- if (this.isAbortedSessionError(error45)) {
55013
- log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
55014
- taskId: task.id,
55015
- parentSessionID: task.parentSessionID
55016
- });
55017
- }
55018
- const messageDir = getMessageDir2(task.parentSessionID);
55019
- const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
55020
- agent = currentMessage?.agent ?? task.parentAgent;
55021
- model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
55022
- }
55023
- log("[background-agent] notifyParentSession context:", {
55024
- taskId: task.id,
55025
- resolvedAgent: agent,
55026
- resolvedModel: model
55027
- });
55028
- try {
55029
- await this.client.session.promptAsync({
55030
- path: { id: task.parentSessionID },
55031
- body: {
55032
- noReply: !allComplete,
55033
- ...agent !== undefined ? { agent } : {},
55034
- ...model !== undefined ? { model } : {},
55035
- ...task.parentTools ? { tools: task.parentTools } : {},
55036
- parts: [{ type: "text", text: notification }]
56028
+ } catch (error45) {
56029
+ if (this.isAbortedSessionError(error45)) {
56030
+ log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
56031
+ taskId: task.id,
56032
+ parentSessionID: task.parentSessionID
56033
+ });
55037
56034
  }
55038
- });
55039
- log("[background-agent] Sent notification to parent session:", {
56035
+ const messageDir = getMessageDir2(task.parentSessionID);
56036
+ const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir) : null;
56037
+ agent = currentMessage?.agent ?? task.parentAgent;
56038
+ model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
56039
+ }
56040
+ log("[background-agent] notifyParentSession context:", {
55040
56041
  taskId: task.id,
55041
- allComplete,
55042
- noReply: !allComplete
56042
+ resolvedAgent: agent,
56043
+ resolvedModel: model
55043
56044
  });
55044
- } catch (error45) {
55045
- if (this.isAbortedSessionError(error45)) {
55046
- log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
56045
+ try {
56046
+ await this.client.session.promptAsync({
56047
+ path: { id: task.parentSessionID },
56048
+ body: {
56049
+ noReply: !allComplete,
56050
+ ...agent !== undefined ? { agent } : {},
56051
+ ...model !== undefined ? { model } : {},
56052
+ ...task.parentTools ? { tools: task.parentTools } : {},
56053
+ parts: [{ type: "text", text: notification }]
56054
+ }
56055
+ });
56056
+ log("[background-agent] Sent notification to parent session:", {
55047
56057
  taskId: task.id,
55048
- parentSessionID: task.parentSessionID
56058
+ allComplete,
56059
+ noReply: !allComplete
55049
56060
  });
55050
- } else {
55051
- log("[background-agent] Failed to send notification:", error45);
56061
+ } catch (error45) {
56062
+ if (this.isAbortedSessionError(error45)) {
56063
+ log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
56064
+ taskId: task.id,
56065
+ parentSessionID: task.parentSessionID
56066
+ });
56067
+ } else {
56068
+ log("[background-agent] Failed to send notification:", error45);
56069
+ }
55052
56070
  }
56071
+ } else {
56072
+ log("[background-agent] Parent session notifications disabled, skipping prompt injection:", {
56073
+ taskId: task.id,
56074
+ parentSessionID: task.parentSessionID
56075
+ });
55053
56076
  }
55054
56077
  if (allComplete) {
55055
56078
  for (const completedTask of completedTasks) {
@@ -55253,46 +56276,53 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
55253
56276
  }
55254
56277
  }
55255
56278
  async pollRunningTasks() {
55256
- this.pruneStaleTasksAndNotifications();
55257
- const statusResult = await this.client.session.status();
55258
- const allStatuses = normalizeSDKResponse(statusResult, {});
55259
- await this.checkAndInterruptStaleTasks(allStatuses);
55260
- for (const task of this.tasks.values()) {
55261
- if (task.status !== "running")
55262
- continue;
55263
- const sessionID = task.sessionID;
55264
- if (!sessionID)
55265
- continue;
55266
- try {
55267
- const sessionStatus = allStatuses[sessionID];
55268
- if (sessionStatus?.type === "idle") {
55269
- const hasValidOutput = await this.validateSessionHasOutput(sessionID);
55270
- if (!hasValidOutput) {
55271
- log("[background-agent] Polling idle but no valid output yet, waiting:", task.id);
55272
- continue;
55273
- }
55274
- if (task.status !== "running")
55275
- continue;
55276
- const hasIncompleteTodos2 = await this.checkSessionTodos(sessionID);
55277
- if (hasIncompleteTodos2) {
55278
- log("[background-agent] Task has incomplete todos via polling, waiting:", task.id);
56279
+ if (this.pollingInFlight)
56280
+ return;
56281
+ this.pollingInFlight = true;
56282
+ try {
56283
+ this.pruneStaleTasksAndNotifications();
56284
+ const statusResult = await this.client.session.status();
56285
+ const allStatuses = normalizeSDKResponse(statusResult, {});
56286
+ await this.checkAndInterruptStaleTasks(allStatuses);
56287
+ for (const task of this.tasks.values()) {
56288
+ if (task.status !== "running")
56289
+ continue;
56290
+ const sessionID = task.sessionID;
56291
+ if (!sessionID)
56292
+ continue;
56293
+ try {
56294
+ const sessionStatus = allStatuses[sessionID];
56295
+ if (sessionStatus?.type === "idle") {
56296
+ const hasValidOutput = await this.validateSessionHasOutput(sessionID);
56297
+ if (!hasValidOutput) {
56298
+ log("[background-agent] Polling idle but no valid output yet, waiting:", task.id);
56299
+ continue;
56300
+ }
56301
+ if (task.status !== "running")
56302
+ continue;
56303
+ const hasIncompleteTodos2 = await this.checkSessionTodos(sessionID);
56304
+ if (hasIncompleteTodos2) {
56305
+ log("[background-agent] Task has incomplete todos via polling, waiting:", task.id);
56306
+ continue;
56307
+ }
56308
+ await this.tryCompleteTask(task, "polling (idle status)");
55279
56309
  continue;
55280
56310
  }
55281
- await this.tryCompleteTask(task, "polling (idle status)");
55282
- continue;
56311
+ log("[background-agent] Session still running, relying on event-based progress:", {
56312
+ taskId: task.id,
56313
+ sessionID,
56314
+ sessionStatus: sessionStatus?.type ?? "not_in_status",
56315
+ toolCalls: task.progress?.toolCalls ?? 0
56316
+ });
56317
+ } catch (error45) {
56318
+ log("[background-agent] Poll error for task:", { taskId: task.id, error: error45 });
55283
56319
  }
55284
- log("[background-agent] Session still running, relying on event-based progress:", {
55285
- taskId: task.id,
55286
- sessionID,
55287
- sessionStatus: sessionStatus?.type ?? "not_in_status",
55288
- toolCalls: task.progress?.toolCalls ?? 0
55289
- });
55290
- } catch (error45) {
55291
- log("[background-agent] Poll error for task:", { taskId: task.id, error: error45 });
55292
56320
  }
55293
- }
55294
- if (!this.hasRunningTasks()) {
55295
- this.stopPolling();
56321
+ if (!this.hasRunningTasks()) {
56322
+ this.stopPolling();
56323
+ }
56324
+ } finally {
56325
+ this.pollingInFlight = false;
55296
56326
  }
55297
56327
  }
55298
56328
  shutdown() {
@@ -55378,6 +56408,47 @@ function getMessageDir2(sessionID) {
55378
56408
  }
55379
56409
  return null;
55380
56410
  }
56411
+ function isCompactionAgent(agent) {
56412
+ return agent?.trim().toLowerCase() === "compaction";
56413
+ }
56414
+ function hasFullAgentAndModel(message) {
56415
+ return !!message.agent && !isCompactionAgent(message.agent) && !!message.model?.providerID && !!message.model?.modelID;
56416
+ }
56417
+ function hasPartialAgentOrModel(message) {
56418
+ const hasAgent = !!message.agent && !isCompactionAgent(message.agent);
56419
+ const hasModel = !!message.model?.providerID && !!message.model?.modelID;
56420
+ return hasAgent || hasModel;
56421
+ }
56422
+ function findNearestMessageExcludingCompaction(messageDir) {
56423
+ try {
56424
+ const files = readdirSync18(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
56425
+ for (const file2 of files) {
56426
+ try {
56427
+ const content = readFileSync40(join72(messageDir, file2), "utf-8");
56428
+ const parsed = JSON.parse(content);
56429
+ if (hasFullAgentAndModel(parsed)) {
56430
+ return parsed;
56431
+ }
56432
+ } catch {
56433
+ continue;
56434
+ }
56435
+ }
56436
+ for (const file2 of files) {
56437
+ try {
56438
+ const content = readFileSync40(join72(messageDir, file2), "utf-8");
56439
+ const parsed = JSON.parse(content);
56440
+ if (hasPartialAgentOrModel(parsed)) {
56441
+ return parsed;
56442
+ }
56443
+ } catch {
56444
+ continue;
56445
+ }
56446
+ }
56447
+ } catch {
56448
+ return null;
56449
+ }
56450
+ return null;
56451
+ }
55381
56452
  // src/features/background-agent/state.ts
55382
56453
  class TaskStateManager {
55383
56454
  tasks = new Map;
@@ -59363,7 +60434,7 @@ class StreamableHTTPClientTransport {
59363
60434
  }
59364
60435
 
59365
60436
  // src/features/mcp-oauth/storage.ts
59366
- import { chmodSync as chmodSync2, existsSync as existsSync62, mkdirSync as mkdirSync14, readFileSync as readFileSync40, unlinkSync as unlinkSync10, writeFileSync as writeFileSync19 } from "fs";
60437
+ import { chmodSync as chmodSync2, existsSync as existsSync62, mkdirSync as mkdirSync14, readFileSync as readFileSync41, unlinkSync as unlinkSync10, writeFileSync as writeFileSync19 } from "fs";
59367
60438
  import { dirname as dirname20, join as join73 } from "path";
59368
60439
  var STORAGE_FILE_NAME = "mcp-oauth.json";
59369
60440
  function getMcpOauthStoragePath() {
@@ -59408,7 +60479,7 @@ function readStore() {
59408
60479
  return null;
59409
60480
  }
59410
60481
  try {
59411
- const content = readFileSync40(filePath, "utf-8");
60482
+ const content = readFileSync41(filePath, "utf-8");
59412
60483
  return JSON.parse(content);
59413
60484
  } catch {
59414
60485
  return null;
@@ -60538,14 +61609,14 @@ var MIN_SPLIT_WIDTH = 2 * MIN_PANE_WIDTH + DIVIDER_SIZE;
60538
61609
  var MIN_SPLIT_HEIGHT = 2 * MIN_PANE_HEIGHT + DIVIDER_SIZE;
60539
61610
 
60540
61611
  // src/features/tmux-subagent/grid-planning.ts
60541
- function calculateCapacity(windowWidth, windowHeight, minPaneWidth = MIN_PANE_WIDTH) {
60542
- const availableWidth = Math.floor(windowWidth * (1 - MAIN_PANE_RATIO));
61612
+ function calculateCapacity(windowWidth, windowHeight, minPaneWidth = MIN_PANE_WIDTH, mainPaneWidth) {
61613
+ const availableWidth = typeof mainPaneWidth === "number" ? Math.max(0, windowWidth - mainPaneWidth - DIVIDER_SIZE) : Math.floor(windowWidth * (1 - MAIN_PANE_RATIO));
60543
61614
  const cols = Math.min(MAX_GRID_SIZE, Math.max(0, Math.floor((availableWidth + DIVIDER_SIZE) / (minPaneWidth + DIVIDER_SIZE))));
60544
61615
  const rows = Math.min(MAX_GRID_SIZE, Math.max(0, Math.floor((windowHeight + DIVIDER_SIZE) / (MIN_PANE_HEIGHT + DIVIDER_SIZE))));
60545
61616
  return { cols, rows, total: cols * rows };
60546
61617
  }
60547
- function computeGridPlan(windowWidth, windowHeight, paneCount) {
60548
- const capacity = calculateCapacity(windowWidth, windowHeight);
61618
+ function computeGridPlan(windowWidth, windowHeight, paneCount, mainPaneWidth, minPaneWidth) {
61619
+ const capacity = calculateCapacity(windowWidth, windowHeight, minPaneWidth ?? MIN_PANE_WIDTH, mainPaneWidth);
60549
61620
  const { cols: maxCols, rows: maxRows } = capacity;
60550
61621
  if (maxCols === 0 || maxRows === 0 || paneCount === 0) {
60551
61622
  return { cols: 1, rows: 1, slotWidth: 0, slotHeight: 0 };
@@ -60565,7 +61636,7 @@ function computeGridPlan(windowWidth, windowHeight, paneCount) {
60565
61636
  }
60566
61637
  }
60567
61638
  }
60568
- const availableWidth = Math.floor(windowWidth * (1 - MAIN_PANE_RATIO));
61639
+ const availableWidth = typeof mainPaneWidth === "number" ? Math.max(0, windowWidth - mainPaneWidth - DIVIDER_SIZE) : Math.floor(windowWidth * (1 - MAIN_PANE_RATIO));
60569
61640
  const slotWidth = Math.floor(availableWidth / bestCols);
60570
61641
  const slotHeight = Math.floor(windowHeight / bestRows);
60571
61642
  return { cols: bestCols, rows: bestRows, slotWidth, slotHeight };
@@ -60610,8 +61681,8 @@ function canSplitPane(pane, direction, minPaneWidth = MIN_PANE_WIDTH) {
60610
61681
  }
60611
61682
  return pane.height >= MIN_SPLIT_HEIGHT;
60612
61683
  }
60613
- function getBestSplitDirection(pane) {
60614
- const canH = pane.width >= MIN_SPLIT_WIDTH;
61684
+ function getBestSplitDirection(pane, minPaneWidth = MIN_PANE_WIDTH) {
61685
+ const canH = pane.width >= minSplitWidthFor(minPaneWidth);
60615
61686
  const canV = pane.height >= MIN_SPLIT_HEIGHT;
60616
61687
  if (!canH && !canV)
60617
61688
  return null;
@@ -60640,38 +61711,38 @@ function findFirstEmptySlot(occupancy, plan) {
60640
61711
  }
60641
61712
  return { row: plan.rows - 1, col: plan.cols - 1 };
60642
61713
  }
60643
- function findSplittableTarget(state3, _preferredDirection) {
61714
+ function findSplittableTarget(state3, minPaneWidth, _preferredDirection) {
60644
61715
  if (!state3.mainPane)
60645
61716
  return null;
60646
61717
  const existingCount = state3.agentPanes.length;
60647
61718
  if (existingCount === 0) {
60648
61719
  const virtualMainPane = { ...state3.mainPane, width: state3.windowWidth };
60649
- if (canSplitPane(virtualMainPane, "-h")) {
61720
+ if (canSplitPane(virtualMainPane, "-h", minPaneWidth)) {
60650
61721
  return { targetPaneId: state3.mainPane.paneId, splitDirection: "-h" };
60651
61722
  }
60652
61723
  return null;
60653
61724
  }
60654
- const plan = computeGridPlan(state3.windowWidth, state3.windowHeight, existingCount + 1);
60655
- const mainPaneWidth = Math.floor(state3.windowWidth * MAIN_PANE_RATIO);
61725
+ const plan = computeGridPlan(state3.windowWidth, state3.windowHeight, existingCount + 1, state3.mainPane.width, minPaneWidth);
61726
+ const mainPaneWidth = state3.mainPane.width;
60656
61727
  const occupancy = buildOccupancy(state3.agentPanes, plan, mainPaneWidth);
60657
61728
  const targetSlot = findFirstEmptySlot(occupancy, plan);
60658
61729
  const leftPane = occupancy.get(`${targetSlot.row}:${targetSlot.col - 1}`);
60659
- if (leftPane && canSplitPane(leftPane, "-h")) {
61730
+ if (leftPane && canSplitPane(leftPane, "-h", minPaneWidth)) {
60660
61731
  return { targetPaneId: leftPane.paneId, splitDirection: "-h" };
60661
61732
  }
60662
61733
  const abovePane = occupancy.get(`${targetSlot.row - 1}:${targetSlot.col}`);
60663
- if (abovePane && canSplitPane(abovePane, "-v")) {
61734
+ if (abovePane && canSplitPane(abovePane, "-v", minPaneWidth)) {
60664
61735
  return { targetPaneId: abovePane.paneId, splitDirection: "-v" };
60665
61736
  }
60666
- const splittablePanes = state3.agentPanes.map((pane) => ({ pane, direction: getBestSplitDirection(pane) })).filter((item) => item.direction !== null).sort((a, b) => b.pane.width * b.pane.height - a.pane.width * a.pane.height);
61737
+ const splittablePanes = state3.agentPanes.map((pane) => ({ pane, direction: getBestSplitDirection(pane, minPaneWidth) })).filter((item) => item.direction !== null).sort((a, b) => b.pane.width * b.pane.height - a.pane.width * a.pane.height);
60667
61738
  const best = splittablePanes[0];
60668
61739
  if (best) {
60669
61740
  return { targetPaneId: best.pane.paneId, splitDirection: best.direction };
60670
61741
  }
60671
61742
  return null;
60672
61743
  }
60673
- function findSpawnTarget(state3) {
60674
- return findSplittableTarget(state3);
61744
+ function findSpawnTarget(state3, minPaneWidth = MIN_PANE_WIDTH) {
61745
+ return findSplittableTarget(state3, minPaneWidth);
60675
61746
  }
60676
61747
  // src/features/tmux-subagent/oldest-agent-pane.ts
60677
61748
  function findOldestAgentPane(agentPanes, sessionMappings) {
@@ -60699,7 +61770,7 @@ function decideSpawnActions(state3, sessionId, description, config3, sessionMapp
60699
61770
  return { canSpawn: false, actions: [], reason: "no main pane found" };
60700
61771
  }
60701
61772
  const minPaneWidth = config3.agentPaneWidth;
60702
- const agentAreaWidth = Math.floor(state3.windowWidth * (1 - MAIN_PANE_RATIO));
61773
+ const agentAreaWidth = Math.max(0, state3.windowWidth - state3.mainPane.width - DIVIDER_SIZE);
60703
61774
  const currentCount = state3.agentPanes.length;
60704
61775
  if (agentAreaWidth < minPaneWidth) {
60705
61776
  return {
@@ -60729,7 +61800,7 @@ function decideSpawnActions(state3, sessionId, description, config3, sessionMapp
60729
61800
  return { canSpawn: false, actions: [], reason: "mainPane too small to split" };
60730
61801
  }
60731
61802
  if (isSplittableAtCount(agentAreaWidth, currentCount, minPaneWidth)) {
60732
- const spawnTarget = findSpawnTarget(state3);
61803
+ const spawnTarget = findSpawnTarget(state3, minPaneWidth);
60733
61804
  if (spawnTarget) {
60734
61805
  return {
60735
61806
  canSpawn: true,
@@ -60751,19 +61822,14 @@ function decideSpawnActions(state3, sessionId, description, config3, sessionMapp
60751
61822
  canSpawn: true,
60752
61823
  actions: [
60753
61824
  {
60754
- type: "close",
61825
+ type: "replace",
60755
61826
  paneId: oldestPane.paneId,
60756
- sessionId: oldestMapping?.sessionId || ""
60757
- },
60758
- {
60759
- type: "spawn",
60760
- sessionId,
60761
- description,
60762
- targetPaneId: state3.mainPane.paneId,
60763
- splitDirection: "-h"
61827
+ oldSessionId: oldestMapping?.sessionId || "",
61828
+ newSessionId: sessionId,
61829
+ description
60764
61830
  }
60765
61831
  ],
60766
- reason: "closed 1 pane to make room for split"
61832
+ reason: "replaced oldest pane to avoid split churn"
60767
61833
  };
60768
61834
  }
60769
61835
  if (oldestPane) {
@@ -60792,36 +61858,48 @@ function decideCloseAction(state3, sessionId, sessionMappings) {
60792
61858
  return null;
60793
61859
  return { type: "close", paneId: mapping.paneId, sessionId };
60794
61860
  }
60795
- // src/features/tmux-subagent/action-executor.ts
60796
- async function enforceMainPane(windowState) {
61861
+ // src/features/tmux-subagent/action-executor-core.ts
61862
+ async function enforceMainPane(windowState, config3, deps) {
60797
61863
  if (!windowState.mainPane)
60798
61864
  return;
60799
- await enforceMainPaneWidth(windowState.mainPane.paneId, windowState.windowWidth);
61865
+ await deps.enforceMainPaneWidth(windowState.mainPane.paneId, windowState.windowWidth, config3.main_pane_size);
60800
61866
  }
60801
- async function executeAction(action, ctx) {
61867
+ async function executeActionWithDeps(action, ctx, deps) {
60802
61868
  if (action.type === "close") {
60803
- const success2 = await closeTmuxPane(action.paneId);
61869
+ const success2 = await deps.closeTmuxPane(action.paneId);
60804
61870
  if (success2) {
60805
- await enforceMainPane(ctx.windowState);
61871
+ await enforceMainPane(ctx.windowState, ctx.config, deps);
60806
61872
  }
60807
61873
  return { success: success2 };
60808
61874
  }
60809
61875
  if (action.type === "replace") {
60810
- const result2 = await replaceTmuxPane(action.paneId, action.newSessionId, action.description, ctx.config, ctx.serverUrl);
61876
+ const result2 = await deps.replaceTmuxPane(action.paneId, action.newSessionId, action.description, ctx.config, ctx.serverUrl);
60811
61877
  return {
60812
61878
  success: result2.success,
60813
61879
  paneId: result2.paneId
60814
61880
  };
60815
61881
  }
60816
- const result = await spawnTmuxPane(action.sessionId, action.description, ctx.config, ctx.serverUrl, action.targetPaneId, action.splitDirection);
61882
+ const result = await deps.spawnTmuxPane(action.sessionId, action.description, ctx.config, ctx.serverUrl, action.targetPaneId, action.splitDirection);
60817
61883
  if (result.success) {
60818
- await enforceMainPane(ctx.windowState);
61884
+ await enforceMainPane(ctx.windowState, ctx.config, deps);
60819
61885
  }
60820
61886
  return {
60821
61887
  success: result.success,
60822
61888
  paneId: result.paneId
60823
61889
  };
60824
61890
  }
61891
+
61892
+ // src/features/tmux-subagent/action-executor.ts
61893
+ var DEFAULT_DEPS = {
61894
+ spawnTmuxPane,
61895
+ closeTmuxPane,
61896
+ replaceTmuxPane,
61897
+ applyLayout,
61898
+ enforceMainPaneWidth
61899
+ };
61900
+ async function executeAction(action, ctx) {
61901
+ return executeActionWithDeps(action, ctx, DEFAULT_DEPS);
61902
+ }
60825
61903
  async function executeActions(actions, ctx) {
60826
61904
  const results = [];
60827
61905
  let spawnedPaneId;
@@ -60850,6 +61928,7 @@ class TmuxPollingManager {
60850
61928
  sessions;
60851
61929
  closeSessionById;
60852
61930
  pollInterval;
61931
+ pollingInFlight = false;
60853
61932
  constructor(client2, sessions, closeSessionById) {
60854
61933
  this.client = client2;
60855
61934
  this.sessions = sessions;
@@ -60869,11 +61948,14 @@ class TmuxPollingManager {
60869
61948
  }
60870
61949
  }
60871
61950
  async pollSessions() {
60872
- if (this.sessions.size === 0) {
60873
- this.stopPolling();
61951
+ if (this.pollingInFlight)
60874
61952
  return;
60875
- }
61953
+ this.pollingInFlight = true;
60876
61954
  try {
61955
+ if (this.sessions.size === 0) {
61956
+ this.stopPolling();
61957
+ return;
61958
+ }
60877
61959
  const statusResult = await this.client.session.status({ path: undefined });
60878
61960
  const allStatuses = normalizeSDKResponse(statusResult, {});
60879
61961
  log("[tmux-session-manager] pollSessions", {
@@ -60950,6 +62032,8 @@ class TmuxPollingManager {
60950
62032
  }
60951
62033
  } catch (err) {
60952
62034
  log("[tmux-session-manager] poll error", { error: String(err) });
62035
+ } finally {
62036
+ this.pollingInFlight = false;
60953
62037
  }
60954
62038
  }
60955
62039
  }
@@ -61101,10 +62185,12 @@ class TmuxSessionManager {
61101
62185
  if (result.success && result.spawnedPaneId) {
61102
62186
  const sessionReady = await this.waitForSessionReady(sessionId);
61103
62187
  if (!sessionReady) {
61104
- log("[tmux-session-manager] session not ready after timeout, tracking anyway", {
62188
+ log("[tmux-session-manager] session not ready after timeout, closing spawned pane", {
61105
62189
  sessionId,
61106
62190
  paneId: result.spawnedPaneId
61107
62191
  });
62192
+ await executeAction({ type: "close", paneId: result.spawnedPaneId, sessionId }, { config: this.tmuxConfig, serverUrl: this.serverUrl, windowState: state3 });
62193
+ return;
61108
62194
  }
61109
62195
  const now = Date.now();
61110
62196
  this.sessions.set(sessionId, {
@@ -61248,17 +62334,15 @@ function buildToolSelectionTable(agents, tools = [], _skills = []) {
61248
62334
  "### Tool & Agent Selection:",
61249
62335
  ""
61250
62336
  ];
61251
- rows.push("| Resource | Cost | When to Use |");
61252
- rows.push("|----------|------|-------------|");
61253
62337
  if (tools.length > 0) {
61254
62338
  const toolsDisplay = formatToolsForPrompt(tools);
61255
- rows.push(`| ${toolsDisplay} | FREE | Not Complex, Scope Clear, No Implicit Assumptions |`);
62339
+ rows.push(`- ${toolsDisplay} \u2014 **FREE** \u2014 Not Complex, Scope Clear, No Implicit Assumptions`);
61256
62340
  }
61257
62341
  const costOrder = { FREE: 0, CHEAP: 1, EXPENSIVE: 2 };
61258
62342
  const sortedAgents = [...agents].filter((a) => a.metadata.category !== "utility").sort((a, b) => costOrder[a.metadata.cost] - costOrder[b.metadata.cost]);
61259
62343
  for (const agent of sortedAgents) {
61260
62344
  const shortDesc = agent.description.split(".")[0] || agent.description;
61261
- rows.push(`| \`${agent.name}\` agent | ${agent.metadata.cost} | ${shortDesc} |`);
62345
+ rows.push(`- \`${agent.name}\` agent \u2014 **${agent.metadata.cost}** \u2014 ${shortDesc}`);
61262
62346
  }
61263
62347
  rows.push("");
61264
62348
  rows.push("**Default flow**: explore/librarian (background) + tools \u2192 oracle (if required)");
@@ -61275,11 +62359,12 @@ function buildExploreSection(agents) {
61275
62359
 
61276
62360
  Use it as a **peer tool**, not a fallback. Fire liberally.
61277
62361
 
61278
- | Use Direct Tools | Use Explore Agent |
61279
- |------------------|-------------------|
61280
- ${avoidWhen.map((w) => `| ${w} | |`).join(`
62362
+ **Use Direct Tools when:**
62363
+ ${avoidWhen.map((w) => `- ${w}`).join(`
61281
62364
  `)}
61282
- ${useWhen.map((w) => `| | ${w} |`).join(`
62365
+
62366
+ **Use Explore Agent when:**
62367
+ ${useWhen.map((w) => `- ${w}`).join(`
61283
62368
  `)}`;
61284
62369
  }
61285
62370
  function buildLibrarianSection(agents) {
@@ -61291,14 +62376,8 @@ function buildLibrarianSection(agents) {
61291
62376
 
61292
62377
  Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved.
61293
62378
 
61294
- | Contextual Grep (Internal) | Reference Grep (External) |
61295
- |----------------------------|---------------------------|
61296
- | Search OUR codebase | Search EXTERNAL resources |
61297
- | Find patterns in THIS repo | Find examples in OTHER repos |
61298
- | How does our code work? | How does this library work? |
61299
- | Project-specific logic | Official API documentation |
61300
- | | Library best practices & quirks |
61301
- | | OSS implementation examples |
62379
+ **Contextual Grep (Internal)** \u2014 search OUR codebase, find patterns in THIS repo, project-specific logic.
62380
+ **Reference Grep (External)** \u2014 search EXTERNAL resources, official API docs, library best practices, OSS implementation examples.
61302
62381
 
61303
62382
  **Trigger phrases** (fire librarian immediately):
61304
62383
  ${useWhen.map((w) => `- "${w}"`).join(`
@@ -61307,13 +62386,11 @@ ${useWhen.map((w) => `- "${w}"`).join(`
61307
62386
  function buildDelegationTable(agents) {
61308
62387
  const rows = [
61309
62388
  "### Delegation Table:",
61310
- "",
61311
- "| Domain | Delegate To | Trigger |",
61312
- "|--------|-------------|---------|"
62389
+ ""
61313
62390
  ];
61314
62391
  for (const agent of agents) {
61315
62392
  for (const trigger of agent.metadata.triggers) {
61316
- rows.push(`| ${trigger.domain} | \`${agent.name}\` | ${trigger.trigger} |`);
62393
+ rows.push(`- **${trigger.domain}** \u2192 \`${agent.name}\` \u2014 ${trigger.trigger}`);
61317
62394
  }
61318
62395
  }
61319
62396
  return rows.join(`
@@ -61327,8 +62404,6 @@ function formatCustomSkillsBlock(customRows, customSkills, headerLevel = "####")
61327
62404
  **The user has installed these custom skills. They MUST be evaluated for EVERY delegation.**
61328
62405
  Subagents are STATELESS \u2014 they lose all custom knowledge unless you pass these skills via \`load_skills\`.
61329
62406
 
61330
- | Skill | Expertise Domain | Source |
61331
- |-------|------------------|--------|
61332
62407
  ${customRows.join(`
61333
62408
  `)}
61334
62409
 
@@ -61340,26 +62415,24 @@ function buildCategorySkillsDelegationGuide(categories, skills) {
61340
62415
  return "";
61341
62416
  const categoryRows = categories.map((c) => {
61342
62417
  const desc = c.description || c.name;
61343
- return `| \`${c.name}\` | ${desc} |`;
62418
+ return `- \`${c.name}\` \u2014 ${desc}`;
61344
62419
  });
61345
62420
  const builtinSkills = skills.filter((s) => s.location === "plugin");
61346
62421
  const customSkills = skills.filter((s) => s.location !== "plugin");
61347
62422
  const builtinRows = builtinSkills.map((s) => {
61348
62423
  const desc = truncateDescription(s.description);
61349
- return `| \`${s.name}\` | ${desc} |`;
62424
+ return `- \`${s.name}\` \u2014 ${desc}`;
61350
62425
  });
61351
62426
  const customRows = customSkills.map((s) => {
61352
62427
  const desc = truncateDescription(s.description);
61353
62428
  const source = s.location === "project" ? "project" : "user";
61354
- return `| \`${s.name}\` | ${desc} | ${source} |`;
62429
+ return `- \`${s.name}\` (${source}) \u2014 ${desc}`;
61355
62430
  });
61356
62431
  const customSkillBlock = formatCustomSkillsBlock(customRows, customSkills);
61357
62432
  let skillsSection;
61358
62433
  if (customSkills.length > 0 && builtinSkills.length > 0) {
61359
62434
  skillsSection = `#### Built-in Skills
61360
62435
 
61361
- | Skill | Expertise Domain |
61362
- |-------|------------------|
61363
62436
  ${builtinRows.join(`
61364
62437
  `)}
61365
62438
 
@@ -61371,8 +62444,6 @@ ${customSkillBlock}`;
61371
62444
 
61372
62445
  Skills inject specialized instructions into the subagent. Read the description to understand when each skill applies.
61373
62446
 
61374
- | Skill | Expertise Domain |
61375
- |-------|------------------|
61376
62447
  ${builtinRows.join(`
61377
62448
  `)}`;
61378
62449
  }
@@ -61384,8 +62455,6 @@ ${builtinRows.join(`
61384
62455
 
61385
62456
  Each category is configured with a model optimized for that domain. Read the description to understand when to use it.
61386
62457
 
61387
- | Category | Domain / Best For |
61388
- |----------|-------------------|
61389
62458
  ${categoryRows.join(`
61390
62459
  `)}
61391
62460
 
@@ -61456,11 +62525,9 @@ function buildOracleSection(agents) {
61456
62525
 
61457
62526
  Oracle is a read-only, expensive, high-quality reasoning model for debugging and architecture. Consultation only.
61458
62527
 
61459
- ### WHEN to Consult:
62528
+ ### WHEN to Consult (Oracle FIRST, then implement):
61460
62529
 
61461
- | Trigger | Action |
61462
- |---------|--------|
61463
- ${useWhen.map((w) => `| ${w} | Oracle FIRST, then implement |`).join(`
62530
+ ${useWhen.map((w) => `- ${w}`).join(`
61464
62531
  `)}
61465
62532
 
61466
62533
  ### WHEN NOT to Consult:
@@ -61472,34 +62539,43 @@ ${avoidWhen.map((w) => `- ${w}`).join(`
61472
62539
  Briefly announce "Consulting Oracle for [reason]" before invocation.
61473
62540
 
61474
62541
  **Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
62542
+
62543
+ ### Oracle Background Task Policy:
62544
+
62545
+ **You MUST collect Oracle results before your final answer. No exceptions.**
62546
+
62547
+ - Oracle may take several minutes. This is normal and expected.
62548
+ - When Oracle is running and you finish your own exploration/analysis, your next action is \`background_output(task_id="...")\` on Oracle \u2014 NOT delivering a final answer.
62549
+ - Oracle catches blind spots you cannot see \u2014 its value is HIGHEST when you think you don't need it.
62550
+ - **NEVER** cancel Oracle. **NEVER** use \`background_cancel(all=true)\` when Oracle is running. Cancel disposable tasks (explore, librarian) individually by taskId instead.
61475
62551
  </Oracle_Usage>`;
61476
62552
  }
61477
62553
  function buildHardBlocksSection() {
61478
62554
  const blocks = [
61479
- "| Type error suppression (`as any`, `@ts-ignore`) | Never |",
61480
- "| Commit without explicit request | Never |",
61481
- "| Speculate about unread code | Never |",
61482
- "| Leave code in broken state after failures | Never |"
62555
+ "- Type error suppression (`as any`, `@ts-ignore`) \u2014 **Never**",
62556
+ "- Commit without explicit request \u2014 **Never**",
62557
+ "- Speculate about unread code \u2014 **Never**",
62558
+ "- Leave code in broken state after failures \u2014 **Never**",
62559
+ "- `background_cancel(all=true)` when Oracle is running \u2014 **Never.** Cancel tasks individually by taskId.",
62560
+ "- Delivering final answer before collecting Oracle result \u2014 **Never.** Always `background_output` Oracle first."
61483
62561
  ];
61484
62562
  return `## Hard Blocks (NEVER violate)
61485
62563
 
61486
- | Constraint | No Exceptions |
61487
- |------------|---------------|
61488
62564
  ${blocks.join(`
61489
62565
  `)}`;
61490
62566
  }
61491
62567
  function buildAntiPatternsSection() {
61492
62568
  const patterns = [
61493
- "| **Type Safety** | `as any`, `@ts-ignore`, `@ts-expect-error` |",
61494
- "| **Error Handling** | Empty catch blocks `catch(e) {}` |",
61495
- '| **Testing** | Deleting failing tests to "pass" |',
61496
- "| **Search** | Firing agents for single-line typos or obvious syntax errors |",
61497
- "| **Debugging** | Shotgun debugging, random changes |"
62569
+ "- **Type Safety**: `as any`, `@ts-ignore`, `@ts-expect-error`",
62570
+ "- **Error Handling**: Empty catch blocks `catch(e) {}`",
62571
+ '- **Testing**: Deleting failing tests to "pass"',
62572
+ "- **Search**: Firing agents for single-line typos or obvious syntax errors",
62573
+ "- **Debugging**: Shotgun debugging, random changes",
62574
+ "- **Background Tasks**: `background_cancel(all=true)` \u2014 always cancel individually by taskId",
62575
+ "- **Oracle**: Skipping Oracle results when Oracle was launched \u2014 ALWAYS collect via `background_output`"
61498
62576
  ];
61499
62577
  return `## Anti-Patterns (BLOCKING violations)
61500
62578
 
61501
- | Category | Forbidden |
61502
- |----------|-----------|
61503
62579
  ${patterns.join(`
61504
62580
  `)}`;
61505
62581
  }
@@ -61515,12 +62591,10 @@ function buildTaskManagementSection(useTaskSystem) {
61515
62591
 
61516
62592
  ### When to Create Tasks (MANDATORY)
61517
62593
 
61518
- | Trigger | Action |
61519
- |---------|--------|
61520
- | Multi-step task (2+ steps) | ALWAYS \`TaskCreate\` first |
61521
- | Uncertain scope | ALWAYS (tasks clarify thinking) |
61522
- | User request with multiple items | ALWAYS |
61523
- | Complex single task | \`TaskCreate\` to break down |
62594
+ - Multi-step task (2+ steps) \u2192 ALWAYS \`TaskCreate\` first
62595
+ - Uncertain scope \u2192 ALWAYS (tasks clarify thinking)
62596
+ - User request with multiple items \u2192 ALWAYS
62597
+ - Complex single task \u2192 \`TaskCreate\` to break down
61524
62598
 
61525
62599
  ### Workflow (NON-NEGOTIABLE)
61526
62600
 
@@ -61539,12 +62613,10 @@ function buildTaskManagementSection(useTaskSystem) {
61539
62613
 
61540
62614
  ### Anti-Patterns (BLOCKING)
61541
62615
 
61542
- | Violation | Why It's Bad |
61543
- |-----------|--------------|
61544
- | Skipping tasks on multi-step tasks | User has no visibility, steps get forgotten |
61545
- | Batch-completing multiple tasks | Defeats real-time tracking purpose |
61546
- | Proceeding without marking in_progress | No indication of what you're working on |
61547
- | Finishing without completing tasks | Task appears incomplete to user |
62616
+ - Skipping tasks on multi-step tasks \u2014 user has no visibility, steps get forgotten
62617
+ - Batch-completing multiple tasks \u2014 defeats real-time tracking purpose
62618
+ - Proceeding without marking in_progress \u2014 no indication of what you're working on
62619
+ - Finishing without completing tasks \u2014 task appears incomplete to user
61548
62620
 
61549
62621
  **FAILURE TO USE TASKS ON NON-TRIVIAL TASKS = INCOMPLETE WORK.**
61550
62622
 
@@ -61572,12 +62644,10 @@ Should I proceed with [recommendation], or would you prefer differently?
61572
62644
 
61573
62645
  ### When to Create Todos (MANDATORY)
61574
62646
 
61575
- | Trigger | Action |
61576
- |---------|--------|
61577
- | Multi-step task (2+ steps) | ALWAYS create todos first |
61578
- | Uncertain scope | ALWAYS (todos clarify thinking) |
61579
- | User request with multiple items | ALWAYS |
61580
- | Complex single task | Create todos to break down |
62647
+ - Multi-step task (2+ steps) \u2192 ALWAYS create todos first
62648
+ - Uncertain scope \u2192 ALWAYS (todos clarify thinking)
62649
+ - User request with multiple items \u2192 ALWAYS
62650
+ - Complex single task \u2192 Create todos to break down
61581
62651
 
61582
62652
  ### Workflow (NON-NEGOTIABLE)
61583
62653
 
@@ -61596,12 +62666,10 @@ Should I proceed with [recommendation], or would you prefer differently?
61596
62666
 
61597
62667
  ### Anti-Patterns (BLOCKING)
61598
62668
 
61599
- | Violation | Why It's Bad |
61600
- |-----------|--------------|
61601
- | Skipping todos on multi-step tasks | User has no visibility, steps get forgotten |
61602
- | Batch-completing multiple todos | Defeats real-time tracking purpose |
61603
- | Proceeding without marking in_progress | No indication of what you're working on |
61604
- | Finishing without completing todos | Task appears incomplete to user |
62669
+ - Skipping todos on multi-step tasks \u2014 user has no visibility, steps get forgotten
62670
+ - Batch-completing multiple todos \u2014 defeats real-time tracking purpose
62671
+ - Proceeding without marking in_progress \u2014 no indication of what you're working on
62672
+ - Finishing without completing todos \u2014 task appears incomplete to user
61605
62673
 
61606
62674
  **FAILURE TO USE TODOS ON NON-TRIVIAL TASKS = INCOMPLETE WORK.**
61607
62675
 
@@ -61660,23 +62728,19 @@ ${keyTriggers}
61660
62728
 
61661
62729
  ### Step 1: Classify Request Type
61662
62730
 
61663
- | Type | Signal | Action |
61664
- |------|--------|--------|
61665
- | **Trivial** | Single file, known location, direct answer | Direct tools only (UNLESS Key Trigger applies) |
61666
- | **Explicit** | Specific file/line, clear command | Execute directly |
61667
- | **Exploratory** | "How does X work?", "Find Y" | Fire explore (1-3) + tools in parallel |
61668
- | **Open-ended** | "Improve", "Refactor", "Add feature" | Assess codebase first |
61669
- | **Ambiguous** | Unclear scope, multiple interpretations | Ask ONE clarifying question |
62731
+ - **Trivial** (single file, known location, direct answer) \u2192 Direct tools only (UNLESS Key Trigger applies)
62732
+ - **Explicit** (specific file/line, clear command) \u2192 Execute directly
62733
+ - **Exploratory** ("How does X work?", "Find Y") \u2192 Fire explore (1-3) + tools in parallel
62734
+ - **Open-ended** ("Improve", "Refactor", "Add feature") \u2192 Assess codebase first
62735
+ - **Ambiguous** (unclear scope, multiple interpretations) \u2192 Ask ONE clarifying question
61670
62736
 
61671
62737
  ### Step 2: Check for Ambiguity
61672
62738
 
61673
- | Situation | Action |
61674
- |-----------|--------|
61675
- | Single valid interpretation | Proceed |
61676
- | Multiple interpretations, similar effort | Proceed with reasonable default, note assumption |
61677
- | Multiple interpretations, 2x+ effort difference | **MUST ask** |
61678
- | Missing critical info (file, error, context) | **MUST ask** |
61679
- | User's design seems flawed or suboptimal | **MUST raise concern** before implementing |
62739
+ - Single valid interpretation \u2192 Proceed
62740
+ - Multiple interpretations, similar effort \u2192 Proceed with reasonable default, note assumption
62741
+ - Multiple interpretations, 2x+ effort difference \u2192 **MUST ask**
62742
+ - Missing critical info (file, error, context) \u2192 **MUST ask**
62743
+ - User's design seems flawed or suboptimal \u2192 **MUST raise concern** before implementing
61680
62744
 
61681
62745
  ### Step 3: Validate Before Acting
61682
62746
 
@@ -61719,12 +62783,10 @@ Before following existing patterns, assess whether they're worth following.
61719
62783
 
61720
62784
  ### State Classification:
61721
62785
 
61722
- | State | Signals | Your Behavior |
61723
- |-------|---------|---------------|
61724
- | **Disciplined** | Consistent patterns, configs present, tests exist | Follow existing style strictly |
61725
- | **Transitional** | Mixed patterns, some structure | Ask: "I see X and Y patterns. Which to follow?" |
61726
- | **Legacy/Chaotic** | No consistency, outdated patterns | Propose: "No clear conventions. I suggest [X]. OK?" |
61727
- | **Greenfield** | New/empty project | Apply modern best practices |
62786
+ - **Disciplined** (consistent patterns, configs present, tests exist) \u2192 Follow existing style strictly
62787
+ - **Transitional** (mixed patterns, some structure) \u2192 Ask: "I see X and Y patterns. Which to follow?"
62788
+ - **Legacy/Chaotic** (no consistency, outdated patterns) \u2192 Propose: "No clear conventions. I suggest [X]. OK?"
62789
+ - **Greenfield** (new/empty project) \u2192 Apply modern best practices
61728
62790
 
61729
62791
  IMPORTANT: If codebase appears undisciplined, verify before assuming:
61730
62792
  - Different patterns may serve different purposes (intentional)
@@ -61770,7 +62832,9 @@ result = task(..., run_in_background=false) // Never wait synchronously for exp
61770
62832
  1. Launch parallel agents \u2192 receive task_ids
61771
62833
  2. Continue immediate work
61772
62834
  3. When results needed: \`background_output(task_id="...")\`
61773
- 4. BEFORE final answer: \`background_cancel(all=true)\`
62835
+ 4. Before final answer, cancel DISPOSABLE tasks (explore, librarian) individually: \`background_cancel(taskId="bg_explore_xxx")\`, \`background_cancel(taskId="bg_librarian_xxx")\`
62836
+ 5. **NEVER cancel Oracle.** ALWAYS collect Oracle result via \`background_output(task_id="bg_oracle_xxx")\` before answering \u2014 even if you already have enough context.
62837
+ 6. **NEVER use \`background_cancel(all=true)\`** \u2014 it kills Oracle. Cancel each disposable task by its specific taskId.
61774
62838
 
61775
62839
  ### Search Stop Conditions
61776
62840
 
@@ -61822,12 +62886,10 @@ AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING:
61822
62886
  Every \`task()\` output includes a session_id. **USE IT.**
61823
62887
 
61824
62888
  **ALWAYS continue when:**
61825
- | Scenario | Action |
61826
- |----------|--------|
61827
- | Task failed/incomplete | \`session_id="{session_id}", prompt="Fix: {specific error}"\` |
61828
- | Follow-up question on result | \`session_id="{session_id}", prompt="Also: {question}"\` |
61829
- | Multi-turn with same agent | \`session_id="{session_id}"\` - NEVER start fresh |
61830
- | Verification failed | \`session_id="{session_id}", prompt="Failed verification: {error}. Fix."\` |
62889
+ - Task failed/incomplete \u2192 \`session_id="{session_id}", prompt="Fix: {specific error}"\`
62890
+ - Follow-up question on result \u2192 \`session_id="{session_id}", prompt="Also: {question}"\`
62891
+ - Multi-turn with same agent \u2192 \`session_id="{session_id}"\` - NEVER start fresh
62892
+ - Verification failed \u2192 \`session_id="{session_id}", prompt="Failed verification: {error}. Fix."\`
61831
62893
 
61832
62894
  **Why session_id is CRITICAL:**
61833
62895
  - Subagent has FULL conversation context preserved
@@ -61864,12 +62926,10 @@ If project has build/test commands, run them at task completion.
61864
62926
 
61865
62927
  ### Evidence Requirements (task NOT complete without these):
61866
62928
 
61867
- | Action | Required Evidence |
61868
- |--------|-------------------|
61869
- | File edit | \`lsp_diagnostics\` clean on changed files |
61870
- | Build command | Exit code 0 |
61871
- | Test run | Pass (or explicit note of pre-existing failures) |
61872
- | Delegation | Agent result received and verified |
62929
+ - **File edit** \u2192 \`lsp_diagnostics\` clean on changed files
62930
+ - **Build command** \u2192 Exit code 0
62931
+ - **Test run** \u2192 Pass (or explicit note of pre-existing failures)
62932
+ - **Delegation** \u2192 Agent result received and verified
61873
62933
 
61874
62934
  **NO EVIDENCE = NOT COMPLETE.**
61875
62935
 
@@ -61909,8 +62969,9 @@ If verification fails:
61909
62969
  3. Report: "Done. Note: found N pre-existing lint errors unrelated to my changes."
61910
62970
 
61911
62971
  ### Before Delivering Final Answer:
61912
- - Cancel ALL running background tasks: \`background_cancel(all=true)\`
61913
- - This conserves resources and ensures clean workflow completion
62972
+ - Cancel DISPOSABLE background tasks (explore, librarian) individually via \`background_cancel(taskId="...")\`
62973
+ - **NEVER use \`background_cancel(all=true)\`.** Always cancel individually by taskId.
62974
+ - **Always wait for Oracle**: When Oracle is running and you have gathered enough context from your own exploration, your next action is \`background_output\` on Oracle \u2014 NOT delivering a final answer. Oracle's value is highest when you think you don't need it.
61914
62975
  </Behavior_Instructions>
61915
62976
 
61916
62977
  ${oracleSection}
@@ -64152,15 +65213,15 @@ function buildTodoDisciplineSection(useTaskSystem) {
64152
65213
 
64153
65214
  | Trigger | Action |
64154
65215
  |---------|--------|
64155
- | 2+ step task | \`TaskCreate\` FIRST, atomic breakdown |
64156
- | Uncertain scope | \`TaskCreate\` to clarify thinking |
65216
+ | 2+ step task | \`task_create\` FIRST, atomic breakdown |
65217
+ | Uncertain scope | \`task_create\` to clarify thinking |
64157
65218
  | Complex single task | Break down into trackable steps |
64158
65219
 
64159
65220
  ### Workflow (STRICT)
64160
65221
 
64161
- 1. **On task start**: \`TaskCreate\` with atomic steps\u2014no announcements, just create
64162
- 2. **Before each step**: \`TaskUpdate(status="in_progress")\` (ONE at a time)
64163
- 3. **After each step**: \`TaskUpdate(status="completed")\` IMMEDIATELY (NEVER batch)
65222
+ 1. **On task start**: \`task_create\` with atomic steps\u2014no announcements, just create
65223
+ 2. **Before each step**: \`task_update(status="in_progress")\` (ONE at a time)
65224
+ 3. **After each step**: \`task_update(status="completed")\` IMMEDIATELY (NEVER batch)
64164
65225
  4. **Scope changes**: Update tasks BEFORE proceeding
64165
65226
 
64166
65227
  ### Why This Matters
@@ -64229,54 +65290,36 @@ function buildHephaestusPrompt(availableAgents = [], availableTools = [], availa
64229
65290
  const todoDiscipline = buildTodoDisciplineSection(useTaskSystem);
64230
65291
  return `You are Hephaestus, an autonomous deep worker for software engineering.
64231
65292
 
64232
- ## Reasoning Configuration (ROUTER NUDGE - GPT 5.2)
64233
-
64234
- Engage MEDIUM reasoning effort for all code modifications and architectural decisions.
64235
- Prioritize logical consistency, codebase pattern matching, and thorough verification over response speed.
64236
- For complex multi-file refactoring or debugging: escalate to HIGH reasoning effort.
64237
-
64238
- ## Identity & Expertise
65293
+ ## Identity
64239
65294
 
64240
- You operate as a **Senior Staff Engineer** with deep expertise in:
64241
- - Repository-scale architecture comprehension
64242
- - Autonomous problem decomposition and execution
64243
- - Multi-file refactoring with full context awareness
64244
- - Pattern recognition across large codebases
65295
+ You operate as a **Senior Staff Engineer**. You do not guess. You verify. You do not stop early. You complete.
64245
65296
 
64246
- You do not guess. You verify. You do not stop early. You complete.
65297
+ **You must keep going until the task is completely resolved, before ending your turn.** Persist until the task is fully handled end-to-end within the current turn. Persevere even when tool calls fail. Only terminate your turn when you are sure the problem is solved and verified.
64247
65298
 
64248
- ## Core Principle (HIGHEST PRIORITY)
65299
+ When blocked: try a different approach \u2192 decompose the problem \u2192 challenge assumptions \u2192 explore how others solved it.
65300
+ Asking the user is the LAST resort after exhausting creative alternatives.
64249
65301
 
64250
- **KEEP GOING. SOLVE PROBLEMS. ASK ONLY WHEN TRULY IMPOSSIBLE.**
65302
+ ### Do NOT Ask \u2014 Just Do
64251
65303
 
64252
- When blocked:
64253
- 1. Try a different approach (there's always another way)
64254
- 2. Decompose the problem into smaller pieces
64255
- 3. Challenge your assumptions
64256
- 4. Explore how others solved similar problems
65304
+ **FORBIDDEN:**
65305
+ - "Should I proceed with X?" \u2192 JUST DO IT.
65306
+ - "Do you want me to run tests?" \u2192 RUN THEM.
65307
+ - "I noticed Y, should I fix it?" \u2192 FIX IT OR NOTE IN FINAL MESSAGE.
65308
+ - Stopping after partial implementation \u2192 100% OR NOTHING.
64257
65309
 
64258
- Asking the user is the LAST resort after exhausting creative alternatives.
64259
- Your job is to SOLVE problems, not report them.
65310
+ **CORRECT:**
65311
+ - Keep going until COMPLETELY done
65312
+ - Run verification (lint, tests, build) WITHOUT asking
65313
+ - Make decisions. Course-correct only on CONCRETE failure
65314
+ - Note assumptions in final message, not as questions mid-work
65315
+ - Need context? Fire explore/librarian in background IMMEDIATELY \u2014 keep working while they search
64260
65316
 
64261
- ## Hard Constraints (MUST READ FIRST - GPT 5.2 Constraint-First)
65317
+ ## Hard Constraints
64262
65318
 
64263
65319
  ${hardBlocks}
64264
65320
 
64265
65321
  ${antiPatterns}
64266
65322
 
64267
- ## Success Criteria (COMPLETION DEFINITION)
64268
-
64269
- A task is COMPLETE when ALL of the following are TRUE:
64270
- 1. All requested functionality implemented exactly as specified
64271
- 2. \`lsp_diagnostics\` returns zero errors on ALL modified files
64272
- 3. Build command exits with code 0 (if applicable)
64273
- 4. Tests pass (or pre-existing failures documented)
64274
- 5. No temporary/debug code remains
64275
- 6. Code matches existing codebase patterns (verified via exploration)
64276
- 7. Evidence provided for each verification step
64277
-
64278
- **If ANY criterion is unmet, the task is NOT complete.**
64279
-
64280
65323
  ## Phase 0 - Intent Gate (EVERY task)
64281
65324
 
64282
65325
  ${keyTriggers}
@@ -64291,80 +65334,46 @@ ${keyTriggers}
64291
65334
  | **Open-ended** | "Improve", "Refactor", "Add feature" | Full Execution Loop required |
64292
65335
  | **Ambiguous** | Unclear scope, multiple interpretations | Ask ONE clarifying question |
64293
65336
 
64294
- ### Step 2: Handle Ambiguity WITHOUT Questions (GPT 5.2 CRITICAL)
64295
-
64296
- **NEVER ask clarifying questions unless the user explicitly asks you to.**
64297
-
64298
- **Default: EXPLORE FIRST. Questions are the LAST resort.**
65337
+ ### Step 2: Ambiguity Protocol (EXPLORE FIRST \u2014 NEVER ask before exploring)
64299
65338
 
64300
65339
  | Situation | Action |
64301
65340
  |-----------|--------|
64302
65341
  | Single valid interpretation | Proceed immediately |
64303
- | Missing info that MIGHT exist | **EXPLORE FIRST** - use tools (gh, git, grep, explore agents) to find it |
65342
+ | Missing info that MIGHT exist | **EXPLORE FIRST** \u2014 use tools (gh, git, grep, explore agents) to find it |
64304
65343
  | Multiple plausible interpretations | Cover ALL likely intents comprehensively, don't ask |
64305
- | Info not findable after exploration | State your best-guess interpretation, proceed with it |
64306
65344
  | Truly impossible to proceed | Ask ONE precise question (LAST RESORT) |
64307
65345
 
64308
- **EXPLORE-FIRST Protocol:**
64309
- \`\`\`
64310
- // WRONG: Ask immediately
64311
- User: "Fix the PR review comments"
64312
- Agent: "What's the PR number?" // BAD - didn't even try to find it
64313
-
64314
- // CORRECT: Explore first
64315
- User: "Fix the PR review comments"
64316
- Agent: *runs gh pr list, gh pr view, searches recent commits*
64317
- *finds the PR, reads comments, proceeds to fix*
64318
- // Only asks if truly cannot find after exhaustive search
64319
- \`\`\`
65346
+ **Exploration Hierarchy (MANDATORY before any question):**
65347
+ 1. Direct tools: \`gh pr list\`, \`git log\`, \`grep\`, \`rg\`, file reads
65348
+ 2. Explore agents: Fire 2-3 parallel background searches
65349
+ 3. Librarian agents: Check docs, GitHub, external sources
65350
+ 4. Context inference: Educated guess from surrounding context
65351
+ 5. LAST RESORT: Ask ONE precise question (only if 1-4 all failed)
64320
65352
 
64321
- **When ambiguous, cover multiple intents:**
64322
- \`\`\`
64323
- // If query has 2-3 plausible meanings:
64324
- // DON'T ask "Did you mean A or B?"
64325
- // DO provide comprehensive coverage of most likely intent
64326
- // DO note: "I interpreted this as X. If you meant Y, let me know."
64327
- \`\`\`
65353
+ If you notice a potential issue \u2014 fix it or note it in final message. Don't ask for permission.
64328
65354
 
64329
65355
  ### Step 3: Validate Before Acting
64330
65356
 
64331
- **Delegation Check (MANDATORY before acting directly):**
64332
- 0. Find relevant skills that you can load, and load them IMMEDIATELY.
65357
+ **Assumptions Check:**
65358
+ - Do I have any implicit assumptions that might affect the outcome?
65359
+ - Is the search scope clear?
65360
+
65361
+ **Delegation Check (MANDATORY):**
65362
+ 0. Find relevant skills to load \u2014 load them IMMEDIATELY.
64333
65363
  1. Is there a specialized agent that perfectly matches this request?
64334
- 2. If not, is there a \`task\` category that best describes this task? What skills are available to equip the agent with?
64335
- - MUST FIND skills to use: \`task(load_skills=[{skill1}, ...])\`
65364
+ 2. If not, what \`task\` category + skills to equip? \u2192 \`task(load_skills=[{skill1}, ...])\`
64336
65365
  3. Can I do it myself for the best result, FOR SURE?
64337
65366
 
64338
65367
  **Default Bias: DELEGATE for complex tasks. Work yourself ONLY when trivial.**
64339
65368
 
64340
- ### Judicious Initiative (CRITICAL)
64341
-
64342
- **Use good judgment. EXPLORE before asking. Deliver results, not questions.**
64343
-
64344
- **Core Principles:**
64345
- - Make reasonable decisions without asking
64346
- - When info is missing: SEARCH FOR IT using tools before asking
64347
- - Trust your technical judgment for implementation details
64348
- - Note assumptions in final message, not as questions mid-work
64349
-
64350
- **Exploration Hierarchy (MANDATORY before any question):**
64351
- 1. **Direct tools**: \`gh pr list\`, \`git log\`, \`grep\`, \`rg\`, file reads
64352
- 2. **Explore agents**: Fire 2-3 parallel background searches
64353
- 3. **Librarian agents**: Check docs, GitHub, external sources
64354
- 4. **Context inference**: Use surrounding context to make educated guess
64355
- 5. **LAST RESORT**: Ask ONE precise question (only if 1-4 all failed)
64356
-
64357
- **If you notice a potential issue:**
64358
- \`\`\`
64359
- // DON'T DO THIS:
64360
- "I notice X might cause Y. Should I proceed?"
65369
+ ### When to Challenge the User
64361
65370
 
64362
- // DO THIS INSTEAD:
64363
- *Proceed with implementation*
64364
- *In final message:* "Note: I noticed X. I handled it by doing Z to avoid Y."
64365
- \`\`\`
65371
+ If you observe:
65372
+ - A design decision that will cause obvious problems
65373
+ - An approach that contradicts established patterns in the codebase
65374
+ - A request that seems to misunderstand how the existing code works
64366
65375
 
64367
- **Only stop for TRUE blockers** (mutually exclusive requirements, impossible constraints).
65376
+ Note the concern and your alternative clearly, then proceed with the best approach. If the risk is major, flag it before implementing.
64368
65377
 
64369
65378
  ---
64370
65379
 
@@ -64376,35 +65385,40 @@ ${exploreSection}
64376
65385
 
64377
65386
  ${librarianSection}
64378
65387
 
64379
- ### Parallel Execution (DEFAULT behavior - NON-NEGOTIABLE)
65388
+ ### Parallel Execution & Tool Usage (DEFAULT \u2014 NON-NEGOTIABLE)
64380
65389
 
64381
- **Explore/Librarian = Grep, not consultants. ALWAYS run them in parallel as background tasks.**
65390
+ **Parallelize EVERYTHING. Independent reads, searches, and agents run SIMULTANEOUSLY.**
64382
65391
 
64383
- \`\`\`typescript
64384
- // CORRECT: Always background, always parallel
64385
- // Prompt structure (each field should be substantive, not a single sentence):
64386
- // [CONTEXT]: What task I'm working on, which files/modules are involved, and what approach I'm taking
64387
- // [GOAL]: The specific outcome I need \u2014 what decision or action the results will unblock
64388
- // [DOWNSTREAM]: How I will use the results \u2014 what I'll build/decide based on what's found
64389
- // [REQUEST]: Concrete search instructions \u2014 what to find, what format to return, and what to SKIP
65392
+ <tool_usage_rules>
65393
+ - Parallelize independent tool calls: multiple file reads, grep searches, agent fires \u2014 all at once
65394
+ - Explore/Librarian = background grep. ALWAYS \`run_in_background=true\`, ALWAYS parallel
65395
+ - After any file edit: restate what changed, where, and what validation follows
65396
+ - Prefer tools over guessing whenever you need specific data (files, configs, patterns)
65397
+ </tool_usage_rules>
64390
65398
 
64391
- // Contextual Grep (internal)
64392
- task(subagent_type="explore", run_in_background=true, load_skills=[], description="Find auth implementations", prompt="I'm implementing JWT auth for the REST API in src/api/routes/. I need to match existing auth conventions so my code fits seamlessly. I'll use this to decide middleware structure and token flow. Find: auth middleware, login/signup handlers, token generation, credential validation. Focus on src/ \u2014 skip tests. Return file paths with pattern descriptions.")
64393
- task(subagent_type="explore", run_in_background=true, load_skills=[], description="Find error handling patterns", prompt="I'm adding error handling to the auth flow and need to follow existing error conventions exactly. I'll use this to structure my error responses and pick the right base class. Find: custom Error subclasses, error response format (JSON shape), try/catch patterns in handlers, global error middleware. Skip test files. Return the error class hierarchy and response format.")
65399
+ **How to call explore/librarian (EXACT syntax \u2014 use \`subagent_type\`, NOT \`category\`):**
65400
+ \`\`\`
65401
+ // Codebase search \u2014 use subagent_type="explore"
65402
+ task(subagent_type="explore", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
64394
65403
 
64395
- // Reference Grep (external)
64396
- task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find JWT security docs", prompt="I'm implementing JWT auth and need current security best practices to choose token storage (httpOnly cookies vs localStorage) and set expiration policy. Find: OWASP auth guidelines, recommended token lifetimes, refresh token rotation strategies, common JWT vulnerabilities. Skip 'what is JWT' tutorials \u2014 production security guidance only.")
64397
- task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find Express auth patterns", prompt="I'm building Express auth middleware and need production-quality patterns to structure my middleware chain. Find how established Express apps (1000+ stars) handle: middleware ordering, token refresh, role-based access control, auth error propagation. Skip basic tutorials \u2014 I need battle-tested patterns with proper error handling.")
64398
- // Continue immediately - collect results when needed
65404
+ // External docs/OSS search \u2014 use subagent_type="librarian"
65405
+ task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
64399
65406
 
64400
- // WRONG: Sequential or blocking - NEVER DO THIS
64401
- result = task(..., run_in_background=false) // Never wait synchronously for explore/librarian
65407
+ // ALWAYS use subagent_type for explore/librarian \u2014 not category
64402
65408
  \`\`\`
64403
65409
 
65410
+ Prompt structure for each agent:
65411
+ - [CONTEXT]: Task, files/modules involved, approach
65412
+ - [GOAL]: Specific outcome needed \u2014 what decision this unblocks
65413
+ - [DOWNSTREAM]: How results will be used
65414
+ - [REQUEST]: What to find, format to return, what to SKIP
65415
+
64404
65416
  **Rules:**
64405
65417
  - Fire 2-5 explore agents in parallel for any non-trivial codebase question
65418
+ - Parallelize independent file reads \u2014 don't read files one at a time
64406
65419
  - NEVER use \`run_in_background=false\` for explore/librarian
64407
- - Continue your work immediately after launching
65420
+ - ALWAYS use \`subagent_type\` for explore/librarian
65421
+ - Continue your work immediately after launching background agents
64408
65422
  - Collect results with \`background_output(task_id="...")\` when needed
64409
65423
  - BEFORE final answer: \`background_cancel(all=true)\` to clean up
64410
65424
 
@@ -64420,282 +65434,186 @@ STOP searching when:
64420
65434
 
64421
65435
  ---
64422
65436
 
64423
- ## Execution Loop (EXPLORE \u2192 PLAN \u2192 DECIDE \u2192 EXECUTE)
64424
-
64425
- For any non-trivial task, follow this loop:
65437
+ ## Execution Loop (EXPLORE \u2192 PLAN \u2192 DECIDE \u2192 EXECUTE \u2192 VERIFY)
64426
65438
 
64427
- ### Step 1: EXPLORE (Parallel Background Agents)
65439
+ 1. **EXPLORE**: Fire 2-5 explore/librarian agents IN PARALLEL + direct tool reads simultaneously
65440
+ \u2192 Tell user: "Checking [area] for [pattern]..."
65441
+ 2. **PLAN**: List files to modify, specific changes, dependencies, complexity estimate
65442
+ \u2192 Tell user: "Found [X]. Here's my plan: [clear summary]."
65443
+ 3. **DECIDE**: Trivial (<10 lines, single file) \u2192 self. Complex (multi-file, >100 lines) \u2192 MUST delegate
65444
+ 4. **EXECUTE**: Surgical changes yourself, or exhaustive context in delegation prompts
65445
+ \u2192 Before large edits: "Modifying [files] \u2014 [what and why]."
65446
+ \u2192 After edits: "Updated [file] \u2014 [what changed]. Running verification."
65447
+ 5. **VERIFY**: \`lsp_diagnostics\` on ALL modified files \u2192 build \u2192 tests
65448
+ \u2192 Tell user: "[result]. [any issues or all clear]."
64428
65449
 
64429
- Fire 2-5 explore/librarian agents IN PARALLEL to gather comprehensive context.
65450
+ **If verification fails: return to Step 1 (max 3 iterations, then consult Oracle).**
64430
65451
 
64431
- ### Step 2: PLAN (Create Work Plan)
65452
+ ---
64432
65453
 
64433
- After collecting exploration results, create a concrete work plan:
64434
- - List all files to be modified
64435
- - Define the specific changes for each file
64436
- - Identify dependencies between changes
64437
- - Estimate complexity (trivial / moderate / complex)
65454
+ ${todoDiscipline}
64438
65455
 
64439
- ### Step 3: DECIDE (Self vs Delegate)
65456
+ ---
64440
65457
 
64441
- For EACH task in your plan, explicitly decide:
65458
+ ## Progress Updates
64442
65459
 
64443
- | Complexity | Criteria | Decision |
64444
- |------------|----------|----------|
64445
- | **Trivial** | <10 lines, single file, obvious change | Do it yourself |
64446
- | **Moderate** | Single domain, clear pattern, <100 lines | Do it yourself OR delegate |
64447
- | **Complex** | Multi-file, unfamiliar domain, >100 lines | MUST delegate |
65460
+ **Report progress proactively \u2014 the user should always know what you're doing and why.**
64448
65461
 
64449
- **When in doubt: DELEGATE. The overhead is worth the quality.**
65462
+ When to update (MANDATORY):
65463
+ - **Before exploration**: "Checking the repo structure for auth patterns..."
65464
+ - **After discovery**: "Found the config in \`src/config/\`. The pattern uses factory functions."
65465
+ - **Before large edits**: "About to refactor the handler \u2014 touching 3 files."
65466
+ - **On phase transitions**: "Exploration done. Moving to implementation."
65467
+ - **On blockers**: "Hit a snag with the types \u2014 trying generics instead."
64450
65468
 
64451
- ### Step 4: EXECUTE
65469
+ Style:
65470
+ - 1-2 sentences, friendly and concrete \u2014 explain in plain language so anyone can follow
65471
+ - Include at least one specific detail (file path, pattern found, decision made)
65472
+ - When explaining technical decisions, explain the WHY \u2014 not just what you did
65473
+ - Don't narrate every \`grep\` or \`cat\` \u2014 but DO signal meaningful progress
64452
65474
 
64453
- Execute your plan:
64454
- - If doing yourself: make surgical, minimal changes
64455
- - If delegating: provide exhaustive context and success criteria in the prompt
65475
+ **Examples:**
65476
+ - "Explored the repo \u2014 auth middleware lives in \`src/middleware/\`. Now patching the handler."
65477
+ - "All tests passing. Just cleaning up the 2 lint errors from my changes."
65478
+ - "Found the pattern in \`utils/parser.ts\`. Applying the same approach to the new module."
65479
+ - "Hit a snag with the types \u2014 trying an alternative approach using generics instead."
64456
65480
 
64457
- ### Step 5: VERIFY
65481
+ ---
64458
65482
 
64459
- After execution:
64460
- 1. Run \`lsp_diagnostics\` on ALL modified files
64461
- 2. Run build command (if applicable)
64462
- 3. Run tests (if applicable)
64463
- 4. Confirm all Success Criteria are met
65483
+ ## Implementation
64464
65484
 
64465
- **If verification fails: return to Step 1 (max 3 iterations, then consult Oracle)**
65485
+ ${categorySkillsGuide}
64466
65486
 
64467
- ---
65487
+ ### Skill Loading Examples
64468
65488
 
64469
- ${todoDiscipline}
65489
+ When delegating, ALWAYS check if relevant skills should be loaded:
64470
65490
 
64471
- ---
65491
+ | Task Domain | Required Skills | Why |
65492
+ |-------------|----------------|-----|
65493
+ | Frontend/UI work | \`frontend-ui-ux\` | Anti-slop design: bold typography, intentional color, meaningful motion. Avoids generic AI layouts |
65494
+ | Browser testing | \`playwright\` | Browser automation, screenshots, verification |
65495
+ | Git operations | \`git-master\` | Atomic commits, rebase/squash, blame/bisect |
65496
+ | Tauri desktop app | \`tauri-macos-craft\` | macOS-native UI, vibrancy, traffic lights |
64472
65497
 
64473
- ## Implementation
65498
+ **Example \u2014 frontend task delegation:**
65499
+ \`\`\`
65500
+ task(
65501
+ category="visual-engineering",
65502
+ load_skills=["frontend-ui-ux"],
65503
+ prompt="1. TASK: Build the settings page... 2. EXPECTED OUTCOME: ..."
65504
+ )
65505
+ \`\`\`
64474
65506
 
64475
- ${categorySkillsGuide}
65507
+ **CRITICAL**: User-installed skills get PRIORITY. Always evaluate ALL available skills before delegating.
64476
65508
 
64477
65509
  ${delegationTable}
64478
65510
 
64479
- ### Delegation Prompt Structure (MANDATORY - ALL 6 sections):
64480
-
64481
- When delegating, your prompt MUST include:
65511
+ ### Delegation Prompt (MANDATORY 6 sections)
64482
65512
 
64483
65513
  \`\`\`
64484
65514
  1. TASK: Atomic, specific goal (one action per delegation)
64485
65515
  2. EXPECTED OUTCOME: Concrete deliverables with success criteria
64486
- 3. REQUIRED TOOLS: Explicit tool whitelist (prevents tool sprawl)
64487
- 4. MUST DO: Exhaustive requirements - leave NOTHING implicit
64488
- 5. MUST NOT DO: Forbidden actions - anticipate and block rogue behavior
65516
+ 3. REQUIRED TOOLS: Explicit tool whitelist
65517
+ 4. MUST DO: Exhaustive requirements \u2014 leave NOTHING implicit
65518
+ 5. MUST NOT DO: Forbidden actions \u2014 anticipate and block rogue behavior
64489
65519
  6. CONTEXT: File paths, existing patterns, constraints
64490
65520
  \`\`\`
64491
65521
 
64492
65522
  **Vague prompts = rejected. Be exhaustive.**
64493
65523
 
64494
- ### Delegation Verification (MANDATORY)
64495
-
64496
- AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING:
64497
- - DOES IT WORK AS EXPECTED?
64498
- - DOES IT FOLLOW THE EXISTING CODEBASE PATTERN?
64499
- - DID THE EXPECTED RESULT COME OUT?
64500
- - DID THE AGENT FOLLOW "MUST DO" AND "MUST NOT DO" REQUIREMENTS?
64501
-
65524
+ After delegation, ALWAYS verify: works as expected? follows codebase pattern? MUST DO / MUST NOT DO respected?
64502
65525
  **NEVER trust subagent self-reports. ALWAYS verify with your own tools.**
64503
65526
 
64504
- ### Session Continuity (MANDATORY)
65527
+ ### Session Continuity
64505
65528
 
64506
- Every \`task()\` output includes a session_id. **USE IT.**
65529
+ Every \`task()\` output includes a session_id. **USE IT for follow-ups.**
64507
65530
 
64508
- **ALWAYS continue when:**
64509
65531
  | Scenario | Action |
64510
65532
  |----------|--------|
64511
- | Task failed/incomplete | \`session_id="{session_id}", prompt="Fix: {specific error}"\` |
64512
- | Follow-up question on result | \`session_id="{session_id}", prompt="Also: {question}"\` |
64513
- | Multi-turn with same agent | \`session_id="{session_id}"\` - NEVER start fresh |
64514
- | Verification failed | \`session_id="{session_id}", prompt="Failed verification: {error}. Fix."\` |
64515
-
64516
- **After EVERY delegation, STORE the session_id for potential continuation.**
65533
+ | Task failed/incomplete | \`session_id="{id}", prompt="Fix: {error}"\` |
65534
+ | Follow-up on result | \`session_id="{id}", prompt="Also: {question}"\` |
65535
+ | Verification failed | \`session_id="{id}", prompt="Failed: {error}. Fix."\` |
64517
65536
 
64518
65537
  ${oracleSection ? `
64519
65538
  ${oracleSection}
64520
65539
  ` : ""}
64521
65540
 
64522
- ## Role & Agency (CRITICAL - READ CAREFULLY)
64523
-
64524
- **KEEP GOING UNTIL THE QUERY IS COMPLETELY RESOLVED.**
64525
-
64526
- Only terminate your turn when you are SURE the problem is SOLVED.
64527
- Autonomously resolve the query to the BEST of your ability.
64528
- Do NOT guess. Do NOT ask unnecessary questions. Do NOT stop early.
64529
-
64530
- **When you hit a wall:**
64531
- - Do NOT immediately ask for help
64532
- - Try at least 3 DIFFERENT approaches
64533
- - Each approach should be meaningfully different (not just tweaking parameters)
64534
- - Document what you tried in your final message
64535
- - Only ask after genuine creative exhaustion
64536
-
64537
- **Completion Checklist (ALL must be true):**
64538
- 1. User asked for X \u2192 X is FULLY implemented (not partial, not "basic version")
64539
- 2. X passes lsp_diagnostics (zero errors on ALL modified files)
64540
- 3. X passes related tests (or you documented pre-existing failures)
64541
- 4. Build succeeds (if applicable)
64542
- 5. You have EVIDENCE for each verification step
64543
-
64544
- **FORBIDDEN (will result in incomplete work):**
64545
- - "I've made the changes, let me know if you want me to continue" \u2192 NO. FINISH IT.
64546
- - "Should I proceed with X?" \u2192 NO. JUST DO IT.
64547
- - "Do you want me to run tests?" \u2192 NO. RUN THEM YOURSELF.
64548
- - "I noticed Y, should I fix it?" \u2192 NO. FIX IT OR NOTE IT IN FINAL MESSAGE.
64549
- - Stopping after partial implementation \u2192 NO. 100% OR NOTHING.
64550
- - Asking about implementation details \u2192 NO. YOU DECIDE.
64551
-
64552
- **CORRECT behavior:**
64553
- - Keep going until COMPLETELY done. No intermediate checkpoints with user.
64554
- - Run verification (lint, tests, build) WITHOUT asking\u2014just do it.
64555
- - Make decisions. Course-correct only on CONCRETE failure.
64556
- - Note assumptions in final message, not as questions mid-work.
64557
- - If blocked, consult Oracle or explore more\u2014don't ask user for implementation guidance.
64558
-
64559
- **The only valid reasons to stop and ask (AFTER exhaustive exploration):**
64560
- - Mutually exclusive requirements (cannot satisfy both A and B)
64561
- - Truly missing info that CANNOT be found via tools/exploration/inference
64562
- - User explicitly requested clarification
64563
-
64564
- **Before asking ANY question, you MUST have:**
64565
- 1. Tried direct tools (gh, git, grep, file reads)
64566
- 2. Fired explore/librarian agents
64567
- 3. Attempted context inference
64568
- 4. Exhausted all findable information
64569
-
64570
- **You are autonomous. EXPLORE first. Ask ONLY as last resort.**
64571
-
64572
- ## Output Contract (UNIFIED)
65541
+ ## Output Contract
64573
65542
 
64574
65543
  <output_contract>
64575
65544
  **Format:**
64576
65545
  - Default: 3-6 sentences or \u22645 bullets
64577
- - Simple yes/no questions: \u22642 sentences
64578
- - Complex multi-file tasks: 1 overview paragraph + \u22645 tagged bullets (What, Where, Risks, Next, Open)
65546
+ - Simple yes/no: \u22642 sentences
65547
+ - Complex multi-file: 1 overview paragraph + \u22645 tagged bullets (What, Where, Risks, Next, Open)
64579
65548
 
64580
65549
  **Style:**
64581
- - Start work immediately. No acknowledgments ("I'm on it", "Let me...")
64582
- - Answer directly without preamble
65550
+ - Start work immediately. Skip empty preambles ("I'm on it", "Let me...") \u2014 but DO send clear context before significant actions
65551
+ - Be friendly, clear, and easy to understand \u2014 explain so anyone can follow your reasoning
65552
+ - When explaining technical decisions, explain the WHY \u2014 not just the WHAT
64583
65553
  - Don't summarize unless asked
64584
- - One-word answers acceptable when appropriate
65554
+ - For long sessions: periodically track files modified, changes made, next steps internally
64585
65555
 
64586
65556
  **Updates:**
64587
- - Brief updates (1-2 sentences) only when starting major phase or plan changes
64588
- - Avoid narrating routine tool calls
65557
+ - Clear updates (a few sentences) at meaningful milestones
64589
65558
  - Each update must include concrete outcome ("Found X", "Updated Y")
64590
-
64591
- **Scope:**
64592
- - Implement what user requests
64593
- - When blocked, autonomously try alternative approaches before asking
64594
- - No unnecessary features, but solve blockers creatively
65559
+ - Do not expand task beyond what user asked
64595
65560
  </output_contract>
64596
65561
 
64597
- ## Response Compaction (LONG CONTEXT HANDLING)
64598
-
64599
- When working on long sessions or complex multi-file tasks:
64600
- - Periodically summarize your working state internally
64601
- - Track: files modified, changes made, verifications completed, next steps
64602
- - Do not lose track of the original request across many tool calls
64603
- - If context feels overwhelming, pause and create a checkpoint summary
65562
+ ## Code Quality & Verification
64604
65563
 
64605
- ## Code Quality Standards
65564
+ ### Before Writing Code (MANDATORY)
64606
65565
 
64607
- ### Codebase Style Check (MANDATORY)
64608
-
64609
- **BEFORE writing ANY code:**
64610
- 1. SEARCH the existing codebase to find similar patterns/styles
64611
- 2. Your code MUST match the project's existing conventions
64612
- 3. Write READABLE code - no clever tricks
64613
- 4. If unsure about style, explore more files until you find the pattern
65566
+ 1. SEARCH existing codebase for similar patterns/styles
65567
+ 2. Match naming, indentation, import styles, error handling conventions
65568
+ 3. Default to ASCII. Add comments only for non-obvious blocks
64614
65569
 
64615
- **When implementing:**
64616
- - Match existing naming conventions
64617
- - Match existing indentation and formatting
64618
- - Match existing import styles
64619
- - Match existing error handling patterns
64620
- - Match existing comment styles (or lack thereof)
65570
+ ### After Implementation (MANDATORY \u2014 DO NOT SKIP)
64621
65571
 
64622
- ### Minimal Changes
64623
-
64624
- - Default to ASCII
64625
- - Add comments only for non-obvious blocks
64626
- - Make the **minimum change** required
64627
-
64628
- ### Edit Protocol
64629
-
64630
- 1. Always read the file first
64631
- 2. Include sufficient context for unique matching
64632
- 3. Use \`apply_patch\` for edits
64633
- 4. Use multiple context blocks when needed
64634
-
64635
- ## Verification & Completion
64636
-
64637
- ### Post-Change Verification (MANDATORY - DO NOT SKIP)
64638
-
64639
- **After EVERY implementation, you MUST:**
64640
-
64641
- 1. **Run \`lsp_diagnostics\` on ALL modified files**
64642
- - Zero errors required before proceeding
64643
- - Fix any errors YOU introduced (not pre-existing ones)
64644
-
64645
- 2. **Find and run related tests**
64646
- - Search for test files: \`*.test.ts\`, \`*.spec.ts\`, \`__tests__/*\`
64647
- - Look for tests in same directory or \`tests/\` folder
64648
- - Pattern: if you modified \`foo.ts\`, look for \`foo.test.ts\`
64649
- - Run: \`bun test <test-file>\` or project's test command
64650
- - If no tests exist for the file, note it explicitly
64651
-
64652
- 3. **Run typecheck if TypeScript project**
64653
- - \`bun run typecheck\` or \`tsc --noEmit\`
64654
-
64655
- 4. **If project has build command, run it**
64656
- - Ensure exit code 0
64657
-
64658
- **DO NOT report completion until all verification steps pass.**
64659
-
64660
- ### Evidence Requirements
65572
+ 1. **\`lsp_diagnostics\`** on ALL modified files \u2014 zero errors required
65573
+ 2. **Run related tests** \u2014 pattern: modified \`foo.ts\` \u2192 look for \`foo.test.ts\`
65574
+ 3. **Run typecheck** if TypeScript project
65575
+ 4. **Run build** if applicable \u2014 exit code 0 required
65576
+ 5. **Tell user** what you verified and the results \u2014 keep it clear and helpful
64661
65577
 
64662
65578
  | Action | Required Evidence |
64663
65579
  |--------|-------------------|
64664
65580
  | File edit | \`lsp_diagnostics\` clean |
64665
- | Build command | Exit code 0 |
64666
- | Test run | Pass (or pre-existing failures noted) |
65581
+ | Build | Exit code 0 |
65582
+ | Tests | Pass (or pre-existing failures noted) |
64667
65583
 
64668
65584
  **NO EVIDENCE = NOT COMPLETE.**
64669
65585
 
64670
- ## Failure Recovery
64671
-
64672
- ### Fix Protocol
65586
+ ## Completion Guarantee (NON-NEGOTIABLE \u2014 READ THIS LAST, REMEMBER IT ALWAYS)
64673
65587
 
64674
- 1. Fix root causes, not symptoms
64675
- 2. Re-verify after EVERY fix attempt
64676
- 3. Never shotgun debug
65588
+ **You do NOT end your turn until the user's request is 100% done, verified, and proven.**
64677
65589
 
64678
- ### After Failure (AUTONOMOUS RECOVERY)
65590
+ This means:
65591
+ 1. **Implement** everything the user asked for \u2014 no partial delivery, no "basic version"
65592
+ 2. **Verify** with real tools: \`lsp_diagnostics\`, build, tests \u2014 not "it should work"
65593
+ 3. **Confirm** every verification passed \u2014 show what you ran and what the output was
65594
+ 4. **Re-read** the original request \u2014 did you miss anything? Check EVERY requirement
64679
65595
 
64680
- 1. **Try alternative approach** - different algorithm, different library, different pattern
64681
- 2. **Decompose** - break into smaller, independently solvable steps
64682
- 3. **Challenge assumptions** - what if your initial interpretation was wrong?
64683
- 4. **Explore more** - fire explore/librarian agents for similar problems solved elsewhere
65596
+ **If ANY of these are false, you are NOT done:**
65597
+ - All requested functionality fully implemented
65598
+ - \`lsp_diagnostics\` returns zero errors on ALL modified files
65599
+ - Build passes (if applicable)
65600
+ - Tests pass (or pre-existing failures documented)
65601
+ - You have EVIDENCE for each verification step
64684
65602
 
64685
- ### After 3 DIFFERENT Approaches Fail
65603
+ **Keep going until the task is fully resolved.** Persist even when tool calls fail. Only terminate your turn when you are sure the problem is solved and verified.
64686
65604
 
64687
- 1. **STOP** all edits
64688
- 2. **REVERT** to last working state
64689
- 3. **DOCUMENT** what you tried (all 3 approaches)
64690
- 4. **CONSULT** Oracle with full context
64691
- 5. If Oracle cannot help, **ASK USER** with clear explanation of attempts
65605
+ **When you think you're done: Re-read the request. Run verification ONE MORE TIME. Then report.**
64692
65606
 
64693
- **Never**: Leave code broken, delete failing tests, continue hoping
65607
+ ## Failure Recovery
64694
65608
 
64695
- ## Soft Guidelines
65609
+ 1. Fix root causes, not symptoms. Re-verify after EVERY attempt.
65610
+ 2. If first approach fails \u2192 try alternative (different algorithm, pattern, library)
65611
+ 3. After 3 DIFFERENT approaches fail:
65612
+ - STOP all edits \u2192 REVERT to last working state
65613
+ - DOCUMENT what you tried \u2192 CONSULT Oracle
65614
+ - If Oracle fails \u2192 ASK USER with clear explanation
64696
65615
 
64697
- - Prefer existing libraries over new dependencies
64698
- - Prefer small, focused changes over large refactors`;
65616
+ **Never**: Leave code broken, delete failing tests, shotgun debug`;
64699
65617
  }
64700
65618
  function createHephaestusAgent(model, availableAgents, availableToolNames, availableSkills, availableCategories, useTaskSystem = false) {
64701
65619
  const tools = availableToolNames ? categorizeTools(availableToolNames) : [];
@@ -64781,7 +65699,7 @@ function buildAgent(source, model, categories, gitMasterConfig, browserProvider,
64781
65699
  }
64782
65700
 
64783
65701
  // src/agents/builtin-agents/resolve-file-uri.ts
64784
- import { existsSync as existsSync63, readFileSync as readFileSync41 } from "fs";
65702
+ import { existsSync as existsSync63, readFileSync as readFileSync42 } from "fs";
64785
65703
  import { homedir as homedir13 } from "os";
64786
65704
  import { isAbsolute as isAbsolute7, resolve as resolve10 } from "path";
64787
65705
  function resolvePromptAppend(promptAppend, configDir) {
@@ -64800,7 +65718,7 @@ function resolvePromptAppend(promptAppend, configDir) {
64800
65718
  return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
64801
65719
  }
64802
65720
  try {
64803
- return readFileSync41(filePath, "utf8");
65721
+ return readFileSync42(filePath, "utf8");
64804
65722
  } catch {
64805
65723
  return `[WARNING: Could not read file: ${promptAppend}]`;
64806
65724
  }
@@ -65182,10 +66100,12 @@ var agentMetadata = {
65182
66100
  };
65183
66101
  async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory, systemDefaultModel, categories, gitMasterConfig, discoveredSkills = [], customAgentSummaries, browserProvider, uiSelectedModel, disabledSkills, useTaskSystem = false) {
65184
66102
  const connectedProviders = readConnectedProvidersCache();
66103
+ const providerModelsConnected = connectedProviders ? readProviderModelsCache()?.connected ?? [] : [];
66104
+ const mergedConnectedProviders = Array.from(new Set([...connectedProviders ?? [], ...providerModelsConnected]));
65185
66105
  const availableModels = await fetchAvailableModels(undefined, {
65186
- connectedProviders: connectedProviders ?? undefined
66106
+ connectedProviders: mergedConnectedProviders.length > 0 ? mergedConnectedProviders : undefined
65187
66107
  });
65188
- const isFirstRunNoCache = availableModels.size === 0 && (!connectedProviders || connectedProviders.length === 0);
66108
+ const isFirstRunNoCache = availableModels.size === 0 && mergedConnectedProviders.length === 0;
65189
66109
  const result = {};
65190
66110
  const mergedCategories = mergeCategories(categories);
65191
66111
  const availableCategories = Object.entries(mergedCategories).map(([name]) => ({
@@ -66647,15 +67567,12 @@ var PROMETHEUS_PERMISSION = {
66647
67567
  // src/agents/sisyphus-junior/default.ts
66648
67568
  function buildDefaultSisyphusJuniorPrompt(useTaskSystem, promptAppend) {
66649
67569
  const todoDiscipline = buildTodoDisciplineSection2(useTaskSystem);
66650
- const constraintsSection = buildConstraintsSection(useTaskSystem);
66651
67570
  const verificationText = useTaskSystem ? "All tasks marked completed" : "All todos marked completed";
66652
67571
  const prompt = `<Role>
66653
67572
  Sisyphus-Junior - Focused executor from OhMyOpenCode.
66654
- Execute tasks directly. NEVER delegate or spawn other agents.
67573
+ Execute tasks directly.
66655
67574
  </Role>
66656
67575
 
66657
- ${constraintsSection}
66658
-
66659
67576
  ${todoDiscipline}
66660
67577
 
66661
67578
  <Verification>
@@ -66676,34 +67593,13 @@ Task NOT complete without:
66676
67593
 
66677
67594
  ` + resolvePromptAppend(promptAppend);
66678
67595
  }
66679
- function buildConstraintsSection(useTaskSystem) {
66680
- if (useTaskSystem) {
66681
- return `<Critical_Constraints>
66682
- BLOCKED ACTIONS (will fail if attempted):
66683
- - task (agent delegation tool): BLOCKED \u2014 you cannot delegate work to other agents
66684
-
66685
- ALLOWED tools:
66686
- - call_omo_agent: You CAN spawn explore/librarian agents for research
66687
- - task_create, task_update, task_list, task_get: ALLOWED \u2014 use these for tracking your work
66688
-
66689
- You work ALONE for implementation. No delegation of implementation tasks.
66690
- </Critical_Constraints>`;
66691
- }
66692
- return `<Critical_Constraints>
66693
- BLOCKED ACTIONS (will fail if attempted):
66694
- - task (agent delegation tool): BLOCKED \u2014 you cannot delegate work to other agents
66695
-
66696
- ALLOWED: call_omo_agent - You CAN spawn explore/librarian agents for research.
66697
- You work ALONE for implementation. No delegation of implementation tasks.
66698
- </Critical_Constraints>`;
66699
- }
66700
67596
  function buildTodoDisciplineSection2(useTaskSystem) {
66701
67597
  if (useTaskSystem) {
66702
67598
  return `<Task_Discipline>
66703
67599
  TASK OBSESSION (NON-NEGOTIABLE):
66704
- - 2+ steps \u2192 TaskCreate FIRST, atomic breakdown
66705
- - TaskUpdate(status="in_progress") before starting (ONE at a time)
66706
- - TaskUpdate(status="completed") IMMEDIATELY after each step
67600
+ - 2+ steps \u2192 task_create FIRST, atomic breakdown
67601
+ - task_update(status="in_progress") before starting (ONE at a time)
67602
+ - task_update(status="completed") IMMEDIATELY after each step
66707
67603
  - NEVER batch completions
66708
67604
 
66709
67605
  No tasks on multi-step work = INCOMPLETE WORK.
@@ -66722,130 +67618,146 @@ No todos on multi-step work = INCOMPLETE WORK.
66722
67618
  // src/agents/sisyphus-junior/gpt.ts
66723
67619
  function buildGptSisyphusJuniorPrompt(useTaskSystem, promptAppend) {
66724
67620
  const taskDiscipline = buildGptTaskDisciplineSection(useTaskSystem);
66725
- const blockedActionsSection = buildGptBlockedActionsSection(useTaskSystem);
66726
67621
  const verificationText = useTaskSystem ? "All tasks marked completed" : "All todos marked completed";
66727
- const prompt = `<identity>
66728
- You are Sisyphus-Junior - Focused task executor from OhMyOpenCode.
66729
- Role: Execute tasks directly. You work ALONE.
66730
- </identity>
67622
+ const prompt = `You are Sisyphus-Junior \u2014 a focused task executor from OhMyOpenCode.
66731
67623
 
66732
- <output_verbosity_spec>
66733
- - Default: 2-4 sentences for status updates.
66734
- - For progress: 1 sentence + current step.
66735
- - AVOID long explanations; prefer compact bullets.
66736
- - Do NOT rephrase the task unless semantics change.
66737
- </output_verbosity_spec>
67624
+ ## Identity
66738
67625
 
66739
- <scope_and_design_constraints>
66740
- - Implement EXACTLY and ONLY what is requested.
66741
- - No extra features, no UX embellishments, no scope creep.
66742
- - If any instruction is ambiguous, choose the simplest valid interpretation OR ask.
66743
- - Do NOT invent new requirements.
66744
- - Do NOT expand task boundaries beyond what's written.
66745
- </scope_and_design_constraints>
67626
+ You execute tasks directly as a **Senior Engineer**. You do not guess. You verify. You do not stop early. You complete.
66746
67627
 
66747
- ${blockedActionsSection}
67628
+ **KEEP GOING. SOLVE PROBLEMS. ASK ONLY WHEN TRULY IMPOSSIBLE.**
66748
67629
 
66749
- <uncertainty_and_ambiguity>
66750
- - If a task is ambiguous or underspecified:
66751
- - Ask 1-2 precise clarifying questions, OR
66752
- - State your interpretation explicitly and proceed with the simplest approach.
66753
- - Never fabricate file paths, requirements, or behavior.
66754
- - Prefer language like "Based on the request..." instead of absolute claims.
66755
- </uncertainty_and_ambiguity>
67630
+ When blocked: try a different approach \u2192 decompose the problem \u2192 challenge assumptions \u2192 explore how others solved it.
67631
+
67632
+ ### Do NOT Ask \u2014 Just Do
67633
+
67634
+ **FORBIDDEN:**
67635
+ - "Should I proceed with X?" \u2192 JUST DO IT.
67636
+ - "Do you want me to run tests?" \u2192 RUN THEM.
67637
+ - "I noticed Y, should I fix it?" \u2192 FIX IT OR NOTE IN FINAL MESSAGE.
67638
+ - Stopping after partial implementation \u2192 100% OR NOTHING.
67639
+
67640
+ **CORRECT:**
67641
+ - Keep going until COMPLETELY done
67642
+ - Run verification (lint, tests, build) WITHOUT asking
67643
+ - Make decisions. Course-correct only on CONCRETE failure
67644
+ - Note assumptions in final message, not as questions mid-work
67645
+ - Need context? Fire explore/librarian via call_omo_agent IMMEDIATELY \u2014 keep working while they search
67646
+
67647
+ ## Scope Discipline
67648
+
67649
+ - Implement EXACTLY and ONLY what is requested
67650
+ - No extra features, no UX embellishments, no scope creep
67651
+ - If ambiguous, choose the simplest valid interpretation OR ask ONE precise question
67652
+ - Do NOT invent new requirements or expand task boundaries
67653
+
67654
+ ## Ambiguity Protocol (EXPLORE FIRST)
67655
+
67656
+ | Situation | Action |
67657
+ |-----------|--------|
67658
+ | Single valid interpretation | Proceed immediately |
67659
+ | Missing info that MIGHT exist | **EXPLORE FIRST** \u2014 use tools (grep, rg, file reads, explore agents) to find it |
67660
+ | Multiple plausible interpretations | State your interpretation, proceed with simplest approach |
67661
+ | Truly impossible to proceed | Ask ONE precise question (LAST RESORT) |
66756
67662
 
66757
67663
  <tool_usage_rules>
66758
- - ALWAYS use tools over internal knowledge for:
66759
- - File contents (use Read, not memory)
66760
- - Current project state (use lsp_diagnostics, glob)
66761
- - Verification (use Bash for tests/build)
66762
- - Parallelize independent tool calls when possible.
67664
+ - Parallelize independent tool calls: multiple file reads, grep searches, agent fires \u2014 all at once
67665
+ - Explore/Librarian via call_omo_agent = background research. Fire them and keep working
67666
+ - After any file edit: restate what changed, where, and what validation follows
67667
+ - Prefer tools over guessing whenever you need specific data (files, configs, patterns)
67668
+ - ALWAYS use tools over internal knowledge for file contents, project state, and verification
66763
67669
  </tool_usage_rules>
66764
67670
 
66765
67671
  ${taskDiscipline}
66766
67672
 
66767
- <verification_spec>
66768
- Task NOT complete without evidence:
67673
+ ## Progress Updates
67674
+
67675
+ **Report progress proactively \u2014 the user should always know what you're doing and why.**
67676
+
67677
+ When to update (MANDATORY):
67678
+ - **Before exploration**: "Checking the repo structure for [pattern]..."
67679
+ - **After discovery**: "Found the config in \`src/config/\`. The pattern uses factory functions."
67680
+ - **Before large edits**: "About to modify [files] \u2014 [what and why]."
67681
+ - **After edits**: "Updated [file] \u2014 [what changed]. Running verification."
67682
+ - **On blockers**: "Hit a snag with [issue] \u2014 trying [alternative] instead."
67683
+
67684
+ Style:
67685
+ - A few sentences, friendly and concrete \u2014 explain in plain language so anyone can follow
67686
+ - Include at least one specific detail (file path, pattern found, decision made)
67687
+ - When explaining technical decisions, explain the WHY \u2014 not just what you did
67688
+
67689
+ ## Code Quality & Verification
67690
+
67691
+ ### Before Writing Code (MANDATORY)
67692
+
67693
+ 1. SEARCH existing codebase for similar patterns/styles
67694
+ 2. Match naming, indentation, import styles, error handling conventions
67695
+ 3. Default to ASCII. Add comments only for non-obvious blocks
67696
+
67697
+ ### After Implementation (MANDATORY \u2014 DO NOT SKIP)
67698
+
67699
+ 1. **\`lsp_diagnostics\`** on ALL modified files \u2014 zero errors required
67700
+ 2. **Run related tests** \u2014 pattern: modified \`foo.ts\` \u2192 look for \`foo.test.ts\`
67701
+ 3. **Run typecheck** if TypeScript project
67702
+ 4. **Run build** if applicable \u2014 exit code 0 required
67703
+ 5. **Tell user** what you verified and the results \u2014 keep it clear and helpful
67704
+
66769
67705
  | Check | Tool | Expected |
66770
67706
  |-------|------|----------|
66771
67707
  | Diagnostics | lsp_diagnostics | ZERO errors on changed files |
66772
67708
  | Build | Bash | Exit code 0 (if applicable) |
66773
- | Tracking | ${useTaskSystem ? "TaskUpdate" : "todowrite"} | ${verificationText} |
67709
+ | Tracking | ${useTaskSystem ? "task_update" : "todowrite"} | ${verificationText} |
66774
67710
 
66775
67711
  **No evidence = not complete.**
66776
- </verification_spec>
66777
67712
 
66778
- <style_spec>
66779
- - Start immediately. No acknowledgments ("I'll...", "Let me...").
66780
- - Match user's communication style.
66781
- - Dense > verbose.
66782
- - Use structured output (bullets, tables) over prose.
66783
- </style_spec>`;
67713
+ ## Output Contract
67714
+
67715
+ <output_contract>
67716
+ **Format:**
67717
+ - Default: 3-6 sentences or \u22645 bullets
67718
+ - Simple yes/no: \u22642 sentences
67719
+ - Complex multi-file: 1 overview paragraph + \u22645 tagged bullets (What, Where, Risks, Next, Open)
67720
+
67721
+ **Style:**
67722
+ - Start work immediately. Skip empty preambles ("I'm on it", "Let me...") \u2014 but DO send clear context before significant actions
67723
+ - Be friendly, clear, and easy to understand \u2014 explain so anyone can follow your reasoning
67724
+ - When explaining technical decisions, explain the WHY \u2014 not just the WHAT
67725
+ </output_contract>
67726
+
67727
+ ## Failure Recovery
67728
+
67729
+ 1. Fix root causes, not symptoms. Re-verify after EVERY attempt.
67730
+ 2. If first approach fails \u2192 try alternative (different algorithm, pattern, library)
67731
+ 3. After 3 DIFFERENT approaches fail \u2192 STOP and report what you tried clearly`;
66784
67732
  if (!promptAppend)
66785
67733
  return prompt;
66786
67734
  return prompt + `
66787
67735
 
66788
67736
  ` + resolvePromptAppend(promptAppend);
66789
67737
  }
66790
- function buildGptBlockedActionsSection(useTaskSystem) {
66791
- if (useTaskSystem) {
66792
- return `<blocked_actions>
66793
- BLOCKED (will fail if attempted):
66794
- | Tool | Status | Description |
66795
- |------|--------|-------------|
66796
- | task | BLOCKED | Agent delegation tool \u2014 you cannot spawn other agents |
66797
-
66798
- ALLOWED:
66799
- | Tool | Usage |
66800
- |------|-------|
66801
- | call_omo_agent | Spawn explore/librarian for research ONLY |
66802
- | task_create | Create tasks to track your work |
66803
- | task_update | Update task status (in_progress, completed) |
66804
- | task_list | List active tasks |
66805
- | task_get | Get task details by ID |
66806
-
66807
- You work ALONE for implementation. No delegation.
66808
- </blocked_actions>`;
66809
- }
66810
- return `<blocked_actions>
66811
- BLOCKED (will fail if attempted):
66812
- | Tool | Status | Description |
66813
- |------|--------|-------------|
66814
- | task | BLOCKED | Agent delegation tool \u2014 you cannot spawn other agents |
66815
-
66816
- ALLOWED:
66817
- | Tool | Usage |
66818
- |------|-------|
66819
- | call_omo_agent | Spawn explore/librarian for research ONLY |
66820
-
66821
- You work ALONE for implementation. No delegation.
66822
- </blocked_actions>`;
66823
- }
66824
67738
  function buildGptTaskDisciplineSection(useTaskSystem) {
66825
67739
  if (useTaskSystem) {
66826
- return `<task_discipline_spec>
66827
- TASK TRACKING (NON-NEGOTIABLE):
67740
+ return `## Task Discipline (NON-NEGOTIABLE)
67741
+
66828
67742
  | Trigger | Action |
66829
67743
  |---------|--------|
66830
- | 2+ steps | TaskCreate FIRST, atomic breakdown |
66831
- | Starting step | TaskUpdate(status="in_progress") - ONE at a time |
66832
- | Completing step | TaskUpdate(status="completed") IMMEDIATELY |
67744
+ | 2+ steps | task_create FIRST, atomic breakdown |
67745
+ | Starting step | task_update(status="in_progress") \u2014 ONE at a time |
67746
+ | Completing step | task_update(status="completed") IMMEDIATELY |
66833
67747
  | Batching | NEVER batch completions |
66834
67748
 
66835
- No tasks on multi-step work = INCOMPLETE WORK.
66836
- </task_discipline_spec>`;
67749
+ No tasks on multi-step work = INCOMPLETE WORK.`;
66837
67750
  }
66838
- return `<todo_discipline_spec>
66839
- TODO TRACKING (NON-NEGOTIABLE):
67751
+ return `## Todo Discipline (NON-NEGOTIABLE)
67752
+
66840
67753
  | Trigger | Action |
66841
67754
  |---------|--------|
66842
67755
  | 2+ steps | todowrite FIRST, atomic breakdown |
66843
- | Starting step | Mark in_progress - ONE at a time |
67756
+ | Starting step | Mark in_progress \u2014 ONE at a time |
66844
67757
  | Completing step | Mark completed IMMEDIATELY |
66845
67758
  | Batching | NEVER batch completions |
66846
67759
 
66847
- No todos on multi-step work = INCOMPLETE WORK.
66848
- </todo_discipline_spec>`;
67760
+ No todos on multi-step work = INCOMPLETE WORK.`;
66849
67761
  }
66850
67762
  // src/agents/sisyphus-junior/agent.ts
66851
67763
  var MODE10 = "subagent";
@@ -66911,7 +67823,7 @@ function createSisyphusJuniorAgentWithOverrides(override, systemDefaultModel, us
66911
67823
  }
66912
67824
  createSisyphusJuniorAgentWithOverrides.mode = MODE10;
66913
67825
  // src/features/claude-code-agent-loader/loader.ts
66914
- import { existsSync as existsSync64, readdirSync as readdirSync19, readFileSync as readFileSync42 } from "fs";
67826
+ import { existsSync as existsSync64, readdirSync as readdirSync19, readFileSync as readFileSync43 } from "fs";
66915
67827
  import { join as join74, basename as basename7 } from "path";
66916
67828
  function parseToolsConfig(toolsStr) {
66917
67829
  if (!toolsStr)
@@ -66937,7 +67849,7 @@ function loadAgentsFromDir(agentsDir, scope) {
66937
67849
  const agentPath = join74(agentsDir, entry.name);
66938
67850
  const agentName = basename7(entry.name, ".md");
66939
67851
  try {
66940
- const content = readFileSync42(agentPath, "utf-8");
67852
+ const content = readFileSync43(agentPath, "utf-8");
66941
67853
  const { data, body } = parseFrontmatter(content);
66942
67854
  const name = data.name || agentName;
66943
67855
  const originalDescription = data.description || "";
@@ -67105,6 +68017,10 @@ function buildPlanDemoteConfig(prometheusConfig, planOverride) {
67105
68017
  }
67106
68018
 
67107
68019
  // src/plugin-handlers/agent-config-handler.ts
68020
+ function hasConfiguredDefaultAgent(config3) {
68021
+ const defaultAgent = config3.default_agent;
68022
+ return typeof defaultAgent === "string" && defaultAgent.trim().length > 0;
68023
+ }
67108
68024
  async function applyAgentConfig(params) {
67109
68025
  const migratedDisabledAgents = (params.pluginConfig.disabled_agents ?? []).map((agent) => {
67110
68026
  return AGENT_NAME_MAP[agent.toLowerCase()] ?? AGENT_NAME_MAP[agent] ?? agent;
@@ -67153,7 +68069,9 @@ async function applyAgentConfig(params) {
67153
68069
  const shouldDemotePlan = plannerEnabled && replacePlan;
67154
68070
  const configAgent = params.config.agent;
67155
68071
  if (isSisyphusEnabled && builtinAgents.sisyphus) {
67156
- params.config.default_agent = getAgentDisplayName("sisyphus");
68072
+ if (!hasConfiguredDefaultAgent(params.config)) {
68073
+ params.config.default_agent = getAgentDisplayName("sisyphus");
68074
+ }
67157
68075
  const agentConfig = {
67158
68076
  sisyphus: builtinAgents.sisyphus
67159
68077
  };
@@ -67381,7 +68299,7 @@ function remapCommandAgentFields(commands2) {
67381
68299
  }
67382
68300
  }
67383
68301
  // src/features/claude-code-mcp-loader/loader.ts
67384
- import { existsSync as existsSync65, readFileSync as readFileSync43 } from "fs";
68302
+ import { existsSync as existsSync65, readFileSync as readFileSync44 } from "fs";
67385
68303
  import { join as join76 } from "path";
67386
68304
  import { homedir as homedir14 } from "os";
67387
68305
 
@@ -67449,7 +68367,7 @@ function getSystemMcpServerNames() {
67449
68367
  if (!existsSync65(path10))
67450
68368
  continue;
67451
68369
  try {
67452
- const content = readFileSync43(path10, "utf-8");
68370
+ const content = readFileSync44(path10, "utf-8");
67453
68371
  const config3 = JSON.parse(content);
67454
68372
  if (!config3?.mcpServers)
67455
68373
  continue;
@@ -67627,7 +68545,7 @@ init_logger();
67627
68545
 
67628
68546
  // src/features/claude-code-plugin-loader/discovery.ts
67629
68547
  init_logger();
67630
- import { existsSync as existsSync66, readFileSync as readFileSync44 } from "fs";
68548
+ import { existsSync as existsSync66, readFileSync as readFileSync45 } from "fs";
67631
68549
  import { homedir as homedir15 } from "os";
67632
68550
  import { join as join77 } from "path";
67633
68551
  function getPluginsBaseDir() {
@@ -67645,7 +68563,7 @@ function loadInstalledPlugins() {
67645
68563
  return null;
67646
68564
  }
67647
68565
  try {
67648
- const content = readFileSync44(dbPath, "utf-8");
68566
+ const content = readFileSync45(dbPath, "utf-8");
67649
68567
  return JSON.parse(content);
67650
68568
  } catch (error45) {
67651
68569
  log("Failed to load installed plugins database", error45);
@@ -67664,7 +68582,7 @@ function loadClaudeSettings() {
67664
68582
  return null;
67665
68583
  }
67666
68584
  try {
67667
- const content = readFileSync44(settingsPath, "utf-8");
68585
+ const content = readFileSync45(settingsPath, "utf-8");
67668
68586
  return JSON.parse(content);
67669
68587
  } catch (error45) {
67670
68588
  log("Failed to load Claude settings", error45);
@@ -67677,7 +68595,7 @@ function loadPluginManifest(installPath) {
67677
68595
  return null;
67678
68596
  }
67679
68597
  try {
67680
- const content = readFileSync44(manifestPath, "utf-8");
68598
+ const content = readFileSync45(manifestPath, "utf-8");
67681
68599
  return JSON.parse(content);
67682
68600
  } catch (error45) {
67683
68601
  log(`Failed to load plugin manifest from ${manifestPath}`, error45);
@@ -67766,7 +68684,7 @@ function discoverInstalledPlugins(options) {
67766
68684
  }
67767
68685
 
67768
68686
  // src/features/claude-code-plugin-loader/command-loader.ts
67769
- import { existsSync as existsSync67, readdirSync as readdirSync20, readFileSync as readFileSync45 } from "fs";
68687
+ import { existsSync as existsSync67, readdirSync as readdirSync20, readFileSync as readFileSync46 } from "fs";
67770
68688
  import { basename as basename9, join as join78 } from "path";
67771
68689
  init_logger();
67772
68690
  function loadPluginCommands(plugins) {
@@ -67782,7 +68700,7 @@ function loadPluginCommands(plugins) {
67782
68700
  const commandName = basename9(entry.name, ".md");
67783
68701
  const namespacedName = `${plugin.name}:${commandName}`;
67784
68702
  try {
67785
- const content = readFileSync45(commandPath, "utf-8");
68703
+ const content = readFileSync46(commandPath, "utf-8");
67786
68704
  const { data, body } = parseFrontmatter(content);
67787
68705
  const wrappedTemplate = `<command-instruction>
67788
68706
  ${body.trim()}
@@ -67813,7 +68731,7 @@ $ARGUMENTS
67813
68731
  }
67814
68732
 
67815
68733
  // src/features/claude-code-plugin-loader/skill-loader.ts
67816
- import { existsSync as existsSync68, readdirSync as readdirSync21, readFileSync as readFileSync46 } from "fs";
68734
+ import { existsSync as existsSync68, readdirSync as readdirSync21, readFileSync as readFileSync47 } from "fs";
67817
68735
  import { join as join79 } from "path";
67818
68736
  init_logger();
67819
68737
  function loadPluginSkillsAsCommands(plugins) {
@@ -67833,7 +68751,7 @@ function loadPluginSkillsAsCommands(plugins) {
67833
68751
  if (!existsSync68(skillMdPath))
67834
68752
  continue;
67835
68753
  try {
67836
- const content = readFileSync46(skillMdPath, "utf-8");
68754
+ const content = readFileSync47(skillMdPath, "utf-8");
67837
68755
  const { data, body } = parseFrontmatter(content);
67838
68756
  const skillName = data.name || entry.name;
67839
68757
  const namespacedName = `${plugin.name}:${skillName}`;
@@ -67868,7 +68786,7 @@ $ARGUMENTS
67868
68786
  }
67869
68787
 
67870
68788
  // src/features/claude-code-plugin-loader/agent-loader.ts
67871
- import { existsSync as existsSync69, readdirSync as readdirSync22, readFileSync as readFileSync47 } from "fs";
68789
+ import { existsSync as existsSync69, readdirSync as readdirSync22, readFileSync as readFileSync48 } from "fs";
67872
68790
  import { basename as basename10, join as join80 } from "path";
67873
68791
  init_logger();
67874
68792
  function parseToolsConfig2(toolsStr) {
@@ -67896,7 +68814,7 @@ function loadPluginAgents(plugins) {
67896
68814
  const agentName = basename10(entry.name, ".md");
67897
68815
  const namespacedName = `${plugin.name}:${agentName}`;
67898
68816
  try {
67899
- const content = readFileSync47(agentPath, "utf-8");
68817
+ const content = readFileSync48(agentPath, "utf-8");
67900
68818
  const { data, body } = parseFrontmatter(content);
67901
68819
  const originalDescription = data.description || "";
67902
68820
  const formattedDescription = `(plugin: ${plugin.name}) ${originalDescription}`;
@@ -67983,14 +68901,14 @@ async function loadPluginMcpServers(plugins) {
67983
68901
 
67984
68902
  // src/features/claude-code-plugin-loader/hook-loader.ts
67985
68903
  init_logger();
67986
- import { existsSync as existsSync71, readFileSync as readFileSync48 } from "fs";
68904
+ import { existsSync as existsSync71, readFileSync as readFileSync49 } from "fs";
67987
68905
  function loadPluginHooksConfigs(plugins) {
67988
68906
  const configs = [];
67989
68907
  for (const plugin of plugins) {
67990
68908
  if (!plugin.hooksPath || !existsSync71(plugin.hooksPath))
67991
68909
  continue;
67992
68910
  try {
67993
- const content = readFileSync48(plugin.hooksPath, "utf-8");
68911
+ const content = readFileSync49(plugin.hooksPath, "utf-8");
67994
68912
  let config3 = JSON.parse(content);
67995
68913
  config3 = resolvePluginPaths(config3, plugin.installPath);
67996
68914
  configs.push(config3);
@@ -68182,7 +69100,7 @@ function createConfigHandler(deps) {
68182
69100
  }
68183
69101
  // src/create-managers.ts
68184
69102
  function createManagers(args) {
68185
- const { ctx, pluginConfig, tmuxConfig, modelCacheState } = args;
69103
+ const { ctx, pluginConfig, tmuxConfig, modelCacheState, backgroundNotificationHookEnabled } = args;
68186
69104
  const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
68187
69105
  const backgroundManager = new BackgroundManager(ctx, pluginConfig.background_task, {
68188
69106
  tmuxConfig,
@@ -68208,7 +69126,8 @@ function createManagers(args) {
68208
69126
  tmuxSessionManager.cleanup().catch((error45) => {
68209
69127
  log("[index] tmux cleanup error during shutdown:", error45);
68210
69128
  });
68211
- }
69129
+ },
69130
+ enableParentSessionNotifications: backgroundNotificationHookEnabled
68212
69131
  });
68213
69132
  initTaskToastManager(ctx.client);
68214
69133
  const skillMcpManager = new SkillMcpManager;
@@ -68310,7 +69229,7 @@ function filterDisabledTools(tools, disabledTools) {
68310
69229
  function createToolRegistry(args) {
68311
69230
  const { ctx, pluginConfig, managers, skillContext, availableCategories } = args;
68312
69231
  const backgroundTools = createBackgroundTools(managers.backgroundManager, ctx.client);
68313
- const callOmoAgent = createCallOmoAgent(ctx, managers.backgroundManager);
69232
+ const callOmoAgent = createCallOmoAgent(ctx, managers.backgroundManager, pluginConfig.disabled_agents ?? []);
68314
69233
  const isMultimodalLookerEnabled = !(pluginConfig.disabled_agents ?? []).some((agent) => agent.toLowerCase() === "multimodal-looker");
68315
69234
  const lookAt = isMultimodalLookerEnabled ? createLookAt(ctx) : null;
68316
69235
  const delegateTask = createDelegateTask({
@@ -68367,6 +69286,8 @@ function createToolRegistry(args) {
68367
69286
  task_list: createTaskList(pluginConfig),
68368
69287
  task_update: createTaskUpdateTool(pluginConfig, ctx)
68369
69288
  } : {};
69289
+ const hashlineEnabled = pluginConfig.experimental?.hashline_edit ?? false;
69290
+ const hashlineToolsRecord = hashlineEnabled ? { edit: createHashlineEditTool() } : {};
68370
69291
  const allTools = {
68371
69292
  ...builtinTools,
68372
69293
  ...createGrepTools(ctx),
@@ -68381,7 +69302,8 @@ function createToolRegistry(args) {
68381
69302
  skill_mcp: skillMcpTool,
68382
69303
  slashcommand: slashcommandTool,
68383
69304
  interactive_bash,
68384
- ...taskToolsRecord
69305
+ ...taskToolsRecord,
69306
+ ...hashlineToolsRecord
68385
69307
  };
68386
69308
  const filteredTools = filterDisabledTools(allTools, pluginConfig.disabled_tools);
68387
69309
  return {
@@ -68746,6 +69668,7 @@ function createToolExecuteAfterHandler3(args) {
68746
69668
  await hooks.delegateTaskRetry?.["tool.execute.after"]?.(input, output);
68747
69669
  await hooks.atlasHook?.["tool.execute.after"]?.(input, output);
68748
69670
  await hooks.taskResumeInfo?.["tool.execute.after"]?.(input, output);
69671
+ await hooks.hashlineReadEnhancer?.["tool.execute.after"]?.(input, output);
68749
69672
  };
68750
69673
  }
68751
69674
 
@@ -68987,7 +69910,8 @@ var BackgroundTaskConfigSchema = exports_external.object({
68987
69910
  var BrowserAutomationProviderSchema = exports_external.enum([
68988
69911
  "playwright",
68989
69912
  "agent-browser",
68990
- "dev-browser"
69913
+ "dev-browser",
69914
+ "playwright-cli"
68991
69915
  ]);
68992
69916
  var BrowserAutomationConfigSchema = exports_external.object({
68993
69917
  provider: BrowserAutomationProviderSchema.default("playwright")
@@ -69086,7 +70010,8 @@ var ExperimentalConfigSchema = exports_external.object({
69086
70010
  dynamic_context_pruning: DynamicContextPruningConfigSchema.optional(),
69087
70011
  task_system: exports_external.boolean().optional(),
69088
70012
  plugin_load_timeout_ms: exports_external.number().min(1000).optional(),
69089
- safe_hook_creation: exports_external.boolean().optional()
70013
+ safe_hook_creation: exports_external.boolean().optional(),
70014
+ hashline_edit: exports_external.boolean().optional()
69090
70015
  });
69091
70016
  // src/config/schema/git-master.ts
69092
70017
  var GitMasterConfigSchema = exports_external.object({
@@ -69136,7 +70061,8 @@ var HookNameSchema = exports_external.enum([
69136
70061
  "stop-continuation-guard",
69137
70062
  "tasks-todowrite-disabler",
69138
70063
  "write-existing-file-guard",
69139
- "anthropic-effort"
70064
+ "anthropic-effort",
70065
+ "hashline-read-enhancer"
69140
70066
  ]);
69141
70067
  // src/config/schema/notification.ts
69142
70068
  var NotificationConfigSchema = exports_external.object({
@@ -69434,7 +70360,8 @@ var OhMyOpenCodePlugin = async (ctx) => {
69434
70360
  ctx,
69435
70361
  pluginConfig,
69436
70362
  tmuxConfig,
69437
- modelCacheState
70363
+ modelCacheState,
70364
+ backgroundNotificationHookEnabled: isHookEnabled("background-notification")
69438
70365
  });
69439
70366
  const toolsResult = await createTools({
69440
70367
  ctx,
@@ -69444,6 +70371,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
69444
70371
  const hooks2 = createHooks({
69445
70372
  ctx,
69446
70373
  pluginConfig,
70374
+ modelCacheState,
69447
70375
  backgroundManager: managers.backgroundManager,
69448
70376
  isHookEnabled,
69449
70377
  safeHookEnabled,
@@ -69462,10 +70390,10 @@ var OhMyOpenCodePlugin = async (ctx) => {
69462
70390
  ...pluginInterface,
69463
70391
  "experimental.session.compacting": async (_input, output) => {
69464
70392
  await hooks2.compactionTodoPreserver?.capture(_input.sessionID);
69465
- if (!hooks2.compactionContextInjector) {
69466
- return;
70393
+ await hooks2.claudeCodeHooks?.["experimental.session.compacting"]?.(_input, output);
70394
+ if (hooks2.compactionContextInjector) {
70395
+ output.context.push(hooks2.compactionContextInjector(_input.sessionID));
69467
70396
  }
69468
- output.context.push(hooks2.compactionContextInjector(_input.sessionID));
69469
70397
  }
69470
70398
  };
69471
70399
  };