oh-my-opencode 3.15.1 → 3.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17496,7 +17496,7 @@ function getOpenCodeConfigPaths(options) {
17496
17496
  configJson: join6(configDir, "opencode.json"),
17497
17497
  configJsonc: join6(configDir, "opencode.jsonc"),
17498
17498
  packageJson: join6(configDir, "package.json"),
17499
- omoConfig: join6(configDir, "oh-my-opencode.json")
17499
+ omoConfig: join6(configDir, `${CONFIG_BASENAME}.json`)
17500
17500
  };
17501
17501
  }
17502
17502
  // src/shared/opencode-version.ts
@@ -62294,11 +62294,11 @@ function getPluginsBaseDir() {
62294
62294
  }
62295
62295
  return join18(homedir6(), ".claude", "plugins");
62296
62296
  }
62297
- function getInstalledPluginsPath() {
62298
- return join18(getPluginsBaseDir(), "installed_plugins.json");
62297
+ function getInstalledPluginsPath(pluginsBaseDir) {
62298
+ return join18(pluginsBaseDir ?? getPluginsBaseDir(), "installed_plugins.json");
62299
62299
  }
62300
- function loadInstalledPlugins() {
62301
- const dbPath = getInstalledPluginsPath();
62300
+ function loadInstalledPlugins(pluginsBaseDir) {
62301
+ const dbPath = getInstalledPluginsPath(pluginsBaseDir);
62302
62302
  if (!existsSync14(dbPath)) {
62303
62303
  return null;
62304
62304
  }
@@ -62402,7 +62402,8 @@ function extractPluginEntries(db) {
62402
62402
  return Object.entries(db.plugins).map(([key, installations]) => [key, installations[0]]);
62403
62403
  }
62404
62404
  function discoverInstalledPlugins(options) {
62405
- const db = loadInstalledPlugins();
62405
+ const pluginsBaseDir = options?.pluginsHomeOverride ?? getPluginsBaseDir();
62406
+ const db = loadInstalledPlugins(pluginsBaseDir);
62406
62407
  const settings = loadClaudeSettings();
62407
62408
  const plugins = [];
62408
62409
  const errors = [];
@@ -62411,6 +62412,7 @@ function discoverInstalledPlugins(options) {
62411
62412
  }
62412
62413
  const settingsEnabledPlugins = settings?.enabledPlugins;
62413
62414
  const overrideEnabledPlugins = options?.enabledPluginsOverride;
62415
+ const pluginManifestLoader = options?.loadPluginManifestOverride ?? loadPluginManifest;
62414
62416
  for (const [pluginKey, installation] of extractPluginEntries(db)) {
62415
62417
  if (!installation)
62416
62418
  continue;
@@ -62427,7 +62429,7 @@ function discoverInstalledPlugins(options) {
62427
62429
  });
62428
62430
  continue;
62429
62431
  }
62430
- const manifest = loadPluginManifest(installPath);
62432
+ const manifest = pluginManifestLoader(installPath);
62431
62433
  const pluginName = manifest?.name || derivePluginNameFromKey(pluginKey);
62432
62434
  const loadedPlugin = {
62433
62435
  name: pluginName,
@@ -62928,11 +62930,21 @@ function loadPluginHooksConfigs(plugins) {
62928
62930
  }
62929
62931
 
62930
62932
  // src/features/claude-code-plugin-loader/loader.ts
62933
+ var cachedPluginComponentsByKey = new Map;
62934
+ function clonePluginComponentsResult(result) {
62935
+ return structuredClone(result);
62936
+ }
62931
62937
  function isClaudeCodePluginsDisabled() {
62932
62938
  const disableFlag = process.env.OPENCODE_DISABLE_CLAUDE_CODE;
62933
62939
  const disablePluginsFlag = process.env.OPENCODE_DISABLE_CLAUDE_CODE_PLUGINS;
62934
62940
  return disableFlag === "true" || disableFlag === "1" || disablePluginsFlag === "true" || disablePluginsFlag === "1";
62935
62941
  }
62942
+ function getPluginComponentsCacheKey(options) {
62943
+ const overrideEntries = Object.entries(options?.enabledPluginsOverride ?? {}).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey));
62944
+ return JSON.stringify({
62945
+ enabledPluginsOverride: overrideEntries
62946
+ });
62947
+ }
62936
62948
  async function loadAllPluginComponents(options) {
62937
62949
  if (isClaudeCodePluginsDisabled()) {
62938
62950
  log("Claude Code plugin loading disabled via OPENCODE_DISABLE_CLAUDE_CODE env var");
@@ -62946,6 +62958,11 @@ async function loadAllPluginComponents(options) {
62946
62958
  errors: []
62947
62959
  };
62948
62960
  }
62961
+ const cacheKey = getPluginComponentsCacheKey(options);
62962
+ const cachedPluginComponents = cachedPluginComponentsByKey.get(cacheKey);
62963
+ if (cachedPluginComponents) {
62964
+ return clonePluginComponentsResult(cachedPluginComponents);
62965
+ }
62949
62966
  const { plugins, errors } = discoverInstalledPlugins(options);
62950
62967
  const [commands, skills, agents, mcpServers, hooksConfigs] = await Promise.all([
62951
62968
  Promise.resolve(loadPluginCommands(plugins)),
@@ -62955,7 +62972,7 @@ async function loadAllPluginComponents(options) {
62955
62972
  Promise.resolve(loadPluginHooksConfigs(plugins))
62956
62973
  ]);
62957
62974
  log(`Loaded ${plugins.length} plugins with ${Object.keys(commands).length} commands, ${Object.keys(skills).length} skills, ${Object.keys(agents).length} agents, ${Object.keys(mcpServers).length} MCP servers`);
62958
- return {
62975
+ const result = {
62959
62976
  commands,
62960
62977
  skills,
62961
62978
  agents,
@@ -62964,6 +62981,8 @@ async function loadAllPluginComponents(options) {
62964
62981
  plugins,
62965
62982
  errors
62966
62983
  };
62984
+ cachedPluginComponentsByKey.set(cacheKey, clonePluginComponentsResult(result));
62985
+ return clonePluginComponentsResult(result);
62967
62986
  }
62968
62987
  // src/shared/plugin-command-discovery.ts
62969
62988
  function discoverPluginCommandDefinitions(options) {
@@ -82203,6 +82222,7 @@ var RETRYABLE_MESSAGE_PATTERNS = [
82203
82222
  "over limit",
82204
82223
  "overloaded",
82205
82224
  "bad gateway",
82225
+ "bad request",
82206
82226
  "unknown provider",
82207
82227
  "provider not found",
82208
82228
  "model_not_supported",
@@ -82529,13 +82549,15 @@ init_logger();
82529
82549
  import { existsSync as existsSync39 } from "fs";
82530
82550
  import { join as join42 } from "path";
82531
82551
  var CONFIG_CACHE_TTL_MS2 = 30000;
82532
- var USER_CONFIG_PATH = join42(getOpenCodeConfigDir({ binary: "opencode" }), "opencode-cc-plugin.json");
82533
82552
  var configCache2 = new Map;
82553
+ function getUserConfigPath() {
82554
+ return join42(getOpenCodeConfigDir({ binary: "opencode" }), "opencode-cc-plugin.json");
82555
+ }
82534
82556
  function getProjectConfigPath() {
82535
82557
  return join42(process.cwd(), ".opencode", "opencode-cc-plugin.json");
82536
82558
  }
82537
82559
  function getCacheKey2() {
82538
- return process.cwd();
82560
+ return `${process.cwd()}::${getUserConfigPath()}`;
82539
82561
  }
82540
82562
  function getCachedConfig2(cacheKey) {
82541
82563
  const cachedEntry = configCache2.get(cacheKey);
@@ -82582,7 +82604,7 @@ async function loadPluginExtendedConfig() {
82582
82604
  if (cachedConfig) {
82583
82605
  return cachedConfig;
82584
82606
  }
82585
- const userConfig = await loadConfigFromPath(USER_CONFIG_PATH);
82607
+ const userConfig = await loadConfigFromPath(getUserConfigPath());
82586
82608
  const projectConfig = await loadConfigFromPath(getProjectConfigPath());
82587
82609
  const merged = {
82588
82610
  disabledHooks: mergeDisabledHooks(userConfig?.disabledHooks, projectConfig?.disabledHooks)
@@ -84427,26 +84449,33 @@ function getWindowsAppdataDir2() {
84427
84449
  return null;
84428
84450
  return process.env.APPDATA ?? path5.join(os4.homedir(), "AppData", "Roaming");
84429
84451
  }
84430
- var USER_CONFIG_DIR = getOpenCodeConfigDir({ binary: "opencode" });
84431
- var USER_OPENCODE_CONFIG = path5.join(USER_CONFIG_DIR, "opencode.json");
84432
- var USER_OPENCODE_CONFIG_JSONC = path5.join(USER_CONFIG_DIR, "opencode.jsonc");
84452
+ function getUserConfigDir() {
84453
+ return getOpenCodeConfigDir({ binary: "opencode" });
84454
+ }
84455
+ function getUserOpencodeConfig() {
84456
+ return path5.join(getUserConfigDir(), "opencode.json");
84457
+ }
84458
+ function getUserOpencodeConfigJsonc() {
84459
+ return path5.join(getUserConfigDir(), "opencode.jsonc");
84460
+ }
84433
84461
  var INSTALLED_PACKAGE_JSON = path5.join(CACHE_DIR, "node_modules", PACKAGE_NAME, "package.json");
84434
84462
 
84435
84463
  // src/hooks/auto-update-checker/checker/config-paths.ts
84436
84464
  import * as os5 from "os";
84437
84465
  import * as path6 from "path";
84438
84466
  function getConfigPaths2(directory) {
84467
+ const userConfigDir = getUserConfigDir();
84439
84468
  const paths = [
84440
84469
  path6.join(directory, ".opencode", "opencode.json"),
84441
84470
  path6.join(directory, ".opencode", "opencode.jsonc"),
84442
- USER_OPENCODE_CONFIG,
84443
- USER_OPENCODE_CONFIG_JSONC
84471
+ getUserOpencodeConfig(),
84472
+ getUserOpencodeConfigJsonc()
84444
84473
  ];
84445
84474
  if (process.platform === "win32") {
84446
84475
  const crossPlatformDir = path6.join(os5.homedir(), ".config");
84447
84476
  const appdataDir = getWindowsAppdataDir2();
84448
84477
  if (appdataDir) {
84449
- const alternateDir = USER_CONFIG_DIR === crossPlatformDir ? appdataDir : crossPlatformDir;
84478
+ const alternateDir = userConfigDir === crossPlatformDir ? appdataDir : crossPlatformDir;
84450
84479
  const alternateConfig = path6.join(alternateDir, "opencode", "opencode.json");
84451
84480
  const alternateConfigJsonc = path6.join(alternateDir, "opencode", "opencode.jsonc");
84452
84481
  if (!paths.includes(alternateConfig)) {
@@ -84937,8 +84966,9 @@ function removeFromBunLock(packageName) {
84937
84966
  }
84938
84967
  function invalidatePackage(packageName = PACKAGE_NAME) {
84939
84968
  try {
84969
+ const userConfigDir = getUserConfigDir();
84940
84970
  const pkgDirs = [
84941
- path10.join(USER_CONFIG_DIR, "node_modules", packageName),
84971
+ path10.join(userConfigDir, "node_modules", packageName),
84942
84972
  path10.join(CACHE_DIR, "node_modules", packageName)
84943
84973
  ];
84944
84974
  let packageRemoved = false;
@@ -98309,7 +98339,7 @@ function mergeConfigs(base, override) {
98309
98339
  function loadPluginConfig(directory, ctx) {
98310
98340
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
98311
98341
  const userDetected = detectPluginConfigFile(configDir);
98312
- let userConfigPath = userDetected.format !== "none" ? userDetected.path : path11.join(configDir, "oh-my-opencode.json");
98342
+ let userConfigPath = userDetected.format !== "none" ? userDetected.path : path11.join(configDir, `${CONFIG_BASENAME}.json`);
98313
98343
  if (userDetected.legacyPath) {
98314
98344
  log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
98315
98345
  canonicalPath: userDetected.path,
@@ -98317,12 +98347,15 @@ function loadPluginConfig(directory, ctx) {
98317
98347
  });
98318
98348
  }
98319
98349
  if (userDetected.format !== "none" && path11.basename(userDetected.path).startsWith(LEGACY_CONFIG_BASENAME)) {
98320
- migrateLegacyConfigFile(userDetected.path);
98321
- userConfigPath = path11.join(path11.dirname(userDetected.path), `${CONFIG_BASENAME}${path11.extname(userDetected.path)}`);
98350
+ const migrated = migrateLegacyConfigFile(userDetected.path);
98351
+ const canonicalPath = path11.join(path11.dirname(userDetected.path), `${CONFIG_BASENAME}${path11.extname(userDetected.path)}`);
98352
+ if (migrated || fs18.existsSync(canonicalPath)) {
98353
+ userConfigPath = canonicalPath;
98354
+ }
98322
98355
  }
98323
98356
  const projectBasePath = path11.join(directory, ".opencode");
98324
98357
  const projectDetected = detectPluginConfigFile(projectBasePath);
98325
- let projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : path11.join(projectBasePath, "oh-my-opencode.json");
98358
+ let projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : path11.join(projectBasePath, `${CONFIG_BASENAME}.json`);
98326
98359
  if (projectDetected.legacyPath) {
98327
98360
  log("Canonical plugin config detected alongside legacy config. Remove the legacy file to avoid confusion.", {
98328
98361
  canonicalPath: projectDetected.path,
@@ -98330,8 +98363,11 @@ function loadPluginConfig(directory, ctx) {
98330
98363
  });
98331
98364
  }
98332
98365
  if (projectDetected.format !== "none" && path11.basename(projectDetected.path).startsWith(LEGACY_CONFIG_BASENAME)) {
98333
- migrateLegacyConfigFile(projectDetected.path);
98334
- projectConfigPath = path11.join(path11.dirname(projectDetected.path), `${CONFIG_BASENAME}${path11.extname(projectDetected.path)}`);
98366
+ const projectMigrated = migrateLegacyConfigFile(projectDetected.path);
98367
+ const canonicalProjectPath = path11.join(path11.dirname(projectDetected.path), `${CONFIG_BASENAME}${path11.extname(projectDetected.path)}`);
98368
+ if (projectMigrated || fs18.existsSync(canonicalProjectPath)) {
98369
+ projectConfigPath = canonicalProjectPath;
98370
+ }
98335
98371
  }
98336
98372
  const userConfig = loadConfigFromPath2(userConfigPath, ctx);
98337
98373
  let config2 = userConfig ?? OhMyOpenCodeConfigSchema.parse({});
@@ -117509,7 +117545,48 @@ async function readSessionTodos(sessionID) {
117509
117545
  }
117510
117546
  return getFileSessionTodos(sessionID);
117511
117547
  }
117548
+ async function readSessionTranscript(sessionID) {
117549
+ return getFileSessionTranscript(sessionID);
117550
+ }
117512
117551
  async function getSessionInfo(sessionID) {
117552
+ if (isSqliteBackend() && sdkClient) {
117553
+ try {
117554
+ const sdkMessages = await getSdkSessionMessages(sdkClient, sessionID);
117555
+ if (sdkMessages.length > 0) {
117556
+ const agentsUsed = new Set;
117557
+ let firstMessage;
117558
+ let lastMessage;
117559
+ for (const msg of sdkMessages) {
117560
+ if (msg.agent)
117561
+ agentsUsed.add(msg.agent);
117562
+ if (msg.time?.created) {
117563
+ const date9 = new Date(msg.time.created);
117564
+ if (!firstMessage || date9 < firstMessage)
117565
+ firstMessage = date9;
117566
+ if (!lastMessage || date9 > lastMessage)
117567
+ lastMessage = date9;
117568
+ }
117569
+ }
117570
+ const todos = await readSessionTodos(sessionID);
117571
+ const transcriptEntries = await readSessionTranscript(sessionID);
117572
+ return {
117573
+ id: sessionID,
117574
+ message_count: sdkMessages.length,
117575
+ first_message: firstMessage,
117576
+ last_message: lastMessage,
117577
+ agents_used: Array.from(agentsUsed),
117578
+ has_todos: todos.length > 0,
117579
+ has_transcript: transcriptEntries > 0,
117580
+ todos,
117581
+ transcript_entries: transcriptEntries
117582
+ };
117583
+ }
117584
+ } catch (error92) {
117585
+ if (!shouldFallbackFromSdkError(error92))
117586
+ throw error92;
117587
+ log("[session-manager] falling back to file session info after SDK unavailable error", { error: String(error92), sessionID });
117588
+ }
117589
+ }
117513
117590
  return getFileSessionInfo(sessionID);
117514
117591
  }
117515
117592
 
@@ -124046,6 +124123,9 @@ var builtinTools = {
124046
124123
  function isTmuxIntegrationEnabled(pluginConfig) {
124047
124124
  return pluginConfig.tmux?.enabled ?? false;
124048
124125
  }
124126
+ function isInteractiveBashEnabled(which = Bun.which) {
124127
+ return which("tmux") !== null;
124128
+ }
124049
124129
  function createRuntimeTmuxConfig(pluginConfig) {
124050
124130
  return TmuxConfigSchema.parse(pluginConfig.tmux ?? {});
124051
124131
  }
@@ -124594,6 +124674,40 @@ function createHooks(args) {
124594
124674
  }
124595
124675
  };
124596
124676
  }
124677
+ // src/features/background-agent/constants.ts
124678
+ var TASK_TTL_MS = 30 * 60 * 1000;
124679
+ var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
124680
+ var MIN_STABILITY_TIME_MS2 = 10 * 1000;
124681
+ var DEFAULT_STALE_TIMEOUT_MS = 2700000;
124682
+ var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
124683
+ var DEFAULT_MAX_TOOL_CALLS = 4000;
124684
+ var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
124685
+ var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
124686
+ var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
124687
+ var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
124688
+ var MIN_IDLE_TIME_MS = 5000;
124689
+ var POLLING_INTERVAL_MS = 3000;
124690
+ var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
124691
+
124692
+ // src/features/background-agent/spawner.ts
124693
+ var FALLBACK_AGENT = "general";
124694
+ function isAgentNotFoundError(error92) {
124695
+ const message = typeof error92 === "string" ? error92 : error92 instanceof Error ? error92.message : typeof error92 === "object" && error92 !== null && typeof error92.message === "string" ? error92.message : String(error92);
124696
+ return message.includes("Agent not found") || message.includes("agent.name");
124697
+ }
124698
+ function buildFallbackBody(originalBody, fallbackAgent) {
124699
+ return {
124700
+ ...originalBody,
124701
+ agent: fallbackAgent,
124702
+ tools: {
124703
+ task: false,
124704
+ call_omo_agent: true,
124705
+ question: false,
124706
+ ...getAgentToolRestrictions(fallbackAgent)
124707
+ }
124708
+ };
124709
+ }
124710
+
124597
124711
  // src/features/background-agent/task-history.ts
124598
124712
  var MAX_ENTRIES_PER_PARENT = 100;
124599
124713
 
@@ -124751,21 +124865,6 @@ class ConcurrencyManager {
124751
124865
  }
124752
124866
  }
124753
124867
 
124754
- // src/features/background-agent/constants.ts
124755
- var TASK_TTL_MS = 30 * 60 * 1000;
124756
- var TERMINAL_TASK_TTL_MS = 30 * 60 * 1000;
124757
- var MIN_STABILITY_TIME_MS2 = 10 * 1000;
124758
- var DEFAULT_STALE_TIMEOUT_MS = 2700000;
124759
- var DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS = 3600000;
124760
- var DEFAULT_MAX_TOOL_CALLS = 4000;
124761
- var DEFAULT_CIRCUIT_BREAKER_CONSECUTIVE_THRESHOLD = 20;
124762
- var DEFAULT_CIRCUIT_BREAKER_ENABLED = true;
124763
- var MIN_RUNTIME_BEFORE_STALE_MS = 30000;
124764
- var DEFAULT_SESSION_GONE_TIMEOUT_MS = 60000;
124765
- var MIN_IDLE_TIME_MS = 5000;
124766
- var POLLING_INTERVAL_MS = 3000;
124767
- var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
124768
-
124769
124868
  // src/features/background-agent/duration-formatter.ts
124770
124869
  function formatDuration3(start, end) {
124771
124870
  const duration5 = (end ?? new Date).getTime() - start.getTime();
@@ -125957,32 +126056,52 @@ class BackgroundManager {
125957
126056
  if (input.model) {
125958
126057
  applySessionPromptParams(sessionID, input.model);
125959
126058
  }
126059
+ const promptBody = {
126060
+ agent: input.agent,
126061
+ ...launchModel ? { model: launchModel } : {},
126062
+ ...launchVariant ? { variant: launchVariant } : {},
126063
+ system: input.skillContent,
126064
+ tools: (() => {
126065
+ const tools = {
126066
+ task: false,
126067
+ call_omo_agent: true,
126068
+ question: false,
126069
+ ...getAgentToolRestrictions(input.agent)
126070
+ };
126071
+ setSessionTools(sessionID, tools);
126072
+ return tools;
126073
+ })(),
126074
+ parts: [createInternalAgentTextPart(input.prompt)]
126075
+ };
125960
126076
  promptWithModelSuggestionRetry(this.client, {
125961
126077
  path: { id: sessionID },
125962
- body: {
125963
- agent: input.agent,
125964
- ...launchModel ? { model: launchModel } : {},
125965
- ...launchVariant ? { variant: launchVariant } : {},
125966
- system: input.skillContent,
125967
- tools: (() => {
125968
- const tools = {
125969
- task: false,
125970
- call_omo_agent: true,
125971
- question: false,
125972
- ...getAgentToolRestrictions(input.agent)
125973
- };
125974
- setSessionTools(sessionID, tools);
125975
- return tools;
125976
- })(),
125977
- parts: [createInternalAgentTextPart(input.prompt)]
125978
- }
126078
+ body: promptBody
125979
126079
  }).catch(async (error92) => {
126080
+ if (isAgentNotFoundError(error92) && input.agent !== FALLBACK_AGENT) {
126081
+ log("[background-agent] Agent not found, retrying with fallback agent", {
126082
+ original: input.agent,
126083
+ fallback: FALLBACK_AGENT,
126084
+ taskId: task.id
126085
+ });
126086
+ try {
126087
+ const fallbackBody = buildFallbackBody(promptBody, FALLBACK_AGENT);
126088
+ setSessionTools(sessionID, fallbackBody.tools);
126089
+ await promptWithModelSuggestionRetry(this.client, {
126090
+ path: { id: sessionID },
126091
+ body: fallbackBody
126092
+ });
126093
+ task.agent = FALLBACK_AGENT;
126094
+ return;
126095
+ } catch (retryError) {
126096
+ log("[background-agent] Fallback agent also failed:", retryError);
126097
+ }
126098
+ }
125980
126099
  log("[background-agent] promptAsync error:", error92);
125981
126100
  const existingTask = this.findBySession(sessionID);
125982
126101
  if (existingTask) {
125983
126102
  existingTask.status = "interrupt";
125984
126103
  const errorMessage = error92 instanceof Error ? error92.message : String(error92);
125985
- if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
126104
+ if (errorMessage.includes("agent.name") || errorMessage.includes("undefined") || isAgentNotFoundError(error92)) {
125986
126105
  existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
125987
126106
  } else {
125988
126107
  existingTask.error = errorMessage;
@@ -126493,6 +126612,13 @@ class BackgroundManager {
126493
126612
  }
126494
126613
  async handleSessionErrorEvent(args) {
126495
126614
  const { task, errorInfo, errorMessage, errorName } = args;
126615
+ if (isAgentNotFoundError({ message: errorInfo.message })) {
126616
+ log("[background-agent] Skipping session.error fallback for agent-not-found (handled by prompt catch)", {
126617
+ taskId: task.id,
126618
+ errorMessage: errorInfo.message?.slice(0, 100)
126619
+ });
126620
+ return;
126621
+ }
126496
126622
  if (await this.tryFallbackRetry(task, errorInfo, "session.error")) {
126497
126623
  return;
126498
126624
  }
@@ -138815,43 +138941,39 @@ function buildTodoDisciplineSection3(useTaskSystem) {
138815
138941
  if (useTaskSystem) {
138816
138942
  return `## Task Discipline (NON-NEGOTIABLE)
138817
138943
 
138818
- Track ALL multi-step work with tasks. This is your execution backbone.
138944
+ **Track ALL multi-step work with tasks. This is your execution backbone.**
138819
138945
 
138820
138946
  ### When to Create Tasks (MANDATORY)
138821
138947
 
138822
- - 2+ step task - \`task_create\` FIRST, atomic breakdown
138823
- - Uncertain scope - \`task_create\` to clarify thinking
138824
- - Complex single task - break down into trackable steps
138948
+ - **2+ step task** - \`task_create\` FIRST, atomic breakdown
138949
+ - **Uncertain scope** - \`task_create\` to clarify thinking
138950
+ - **Complex single task** - Break down into trackable steps
138825
138951
 
138826
138952
  ### Workflow (STRICT)
138827
138953
 
138828
- 1. On task start: \`task_create\` with atomic steps - no announcements, just create
138829
- 2. Before each step: \`task_update(status="in_progress")\` (ONE at a time)
138830
- 3. After each step: \`task_update(status="completed")\` IMMEDIATELY (NEVER batch)
138831
- 4. Scope changes: update tasks BEFORE proceeding
138832
-
138833
- Tasks prevent drift, enable recovery if interrupted, and make each commitment explicit. Skipping tasks on multi-step work, batch-completing, or proceeding without \`in_progress\` are blocking violations.
138954
+ 1. **On task start**: \`task_create\` with atomic steps-no announcements, just create
138955
+ 2. **Before each step**: \`task_update(status="in_progress")\` (ONE at a time)
138956
+ 3. **After each step**: \`task_update(status="completed")\` IMMEDIATELY (NEVER batch)
138957
+ 4. **Scope changes**: Update tasks BEFORE proceeding
138834
138958
 
138835
138959
  **NO TASKS ON MULTI-STEP WORK = INCOMPLETE WORK.**`;
138836
138960
  }
138837
138961
  return `## Todo Discipline (NON-NEGOTIABLE)
138838
138962
 
138839
- Track ALL multi-step work with todos. This is your execution backbone.
138963
+ **Track ALL multi-step work with todos. This is your execution backbone.**
138840
138964
 
138841
138965
  ### When to Create Todos (MANDATORY)
138842
138966
 
138843
- - 2+ step task - \`todowrite\` FIRST, atomic breakdown
138844
- - Uncertain scope - \`todowrite\` to clarify thinking
138845
- - Complex single task - break down into trackable steps
138967
+ - **2+ step task** - \`todowrite\` FIRST, atomic breakdown
138968
+ - **Uncertain scope** - \`todowrite\` to clarify thinking
138969
+ - **Complex single task** - Break down into trackable steps
138846
138970
 
138847
138971
  ### Workflow (STRICT)
138848
138972
 
138849
- 1. On task start: \`todowrite\` with atomic steps - no announcements, just create
138850
- 2. Before each step: mark \`in_progress\` (ONE at a time)
138851
- 3. After each step: mark \`completed\` IMMEDIATELY (NEVER batch)
138852
- 4. Scope changes: update todos BEFORE proceeding
138853
-
138854
- Todos prevent drift, enable recovery if interrupted, and make each commitment explicit. Skipping todos on multi-step work, batch-completing, or proceeding without \`in_progress\` are blocking violations.
138973
+ 1. **On task start**: \`todowrite\` with atomic steps-no announcements, just create
138974
+ 2. **Before each step**: Mark \`in_progress\` (ONE at a time)
138975
+ 3. **After each step**: Mark \`completed\` IMMEDIATELY (NEVER batch)
138976
+ 4. **Scope changes**: Update todos BEFORE proceeding
138855
138977
 
138856
138978
  **NO TODOS ON MULTI-STEP WORK = INCOMPLETE WORK.**`;
138857
138979
  }
@@ -138865,313 +138987,243 @@ function buildHephaestusPrompt3(availableAgents = [], availableTools = [], avail
138865
138987
  const oracleSection = buildOracleSection(availableAgents);
138866
138988
  const hardBlocks = buildHardBlocksSection();
138867
138989
  const antiPatterns = buildAntiPatternsSection();
138990
+ const antiDuplication = buildAntiDuplicationSection();
138868
138991
  const todoDiscipline = buildTodoDisciplineSection3(useTaskSystem);
138869
- return `You are Hephaestus, an autonomous deep worker for software engineering.
138870
-
138871
- ## Identity
138872
-
138873
- You build context by examining the codebase first without making assumptions. You think through the nuances of the code you encounter. You do not stop early. You complete.
138874
-
138875
- 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.
138876
-
138877
- When blocked: try a different approach \u2192 decompose the problem \u2192 challenge assumptions \u2192 explore how others solved it. Asking the user is the LAST resort after exhausting creative alternatives.
138878
-
138879
- ### Do NOT Ask - Just Do
138880
-
138881
- **FORBIDDEN:**
138882
- - Asking permission in any form ("Should I proceed?", "Would you like me to...?", "I can do X if you want") \u2192 JUST DO IT.
138883
- - "Do you want me to run tests?" \u2192 RUN THEM.
138884
- - "I noticed Y, should I fix it?" \u2192 FIX IT OR NOTE IN FINAL MESSAGE.
138885
- - Stopping after partial implementation \u2192 100% OR NOTHING.
138886
- - Answering a question then stopping \u2192 The question implies action. DO THE ACTION.
138887
- - "I'll do X" / "I recommend X" then ending turn \u2192 You COMMITTED to X. DO X NOW before ending.
138888
- - Explaining findings without acting on them \u2192 ACT on your findings immediately.
138889
-
138890
- **CORRECT:**
138891
- - Keep going until COMPLETELY done
138892
- - Run verification (lint, tests, build) WITHOUT asking
138893
- - Make decisions. Course-correct only on CONCRETE failure
138894
- - Note assumptions in final message, not as questions mid-work
138895
- - Need context? Fire explore/librarian in background IMMEDIATELY - continue only with non-overlapping work while they search
138896
- - User asks "did you do X?" and you didn't \u2192 Acknowledge briefly, DO X immediately
138897
- - User asks a question implying work \u2192 Answer briefly, DO the implied work in the same turn
138898
- - You wrote a plan in your response \u2192 EXECUTE the plan before ending turn - plans are starting lines, not finish lines
138899
-
138900
- ### Task Scope Clarification
138992
+ const identityBlock = `<identity>
138993
+ You are Hephaestus, an autonomous deep worker for software engineering.
138901
138994
 
138902
- You handle multi-step sub-tasks of a SINGLE GOAL. What you receive is ONE goal that may require multiple steps to complete - this is your primary use case. Only reject when given MULTIPLE INDEPENDENT goals in one request.
138995
+ You communicate warmly and directly, like a senior colleague walking through a problem together. You explain the why behind decisions, not just the what. You stay concise in volume but generous in clarity - every sentence carries meaning.
138903
138996
 
138904
- ## Hard Constraints
138997
+ You build context by examining the codebase first without assumptions. You think through the nuances of the code you encounter. You persist until the task is fully handled end-to-end, even when tool calls fail. You only end your turn when the problem is solved and verified.
138905
138998
 
138906
- ${hardBlocks}
138999
+ You are autonomous. When you see work to do, do it - run tests, fix issues, make decisions. Course-correct only on concrete failure. State assumptions in your final message, not as questions along the way. If you commit to doing something ("I'll fix X"), execute it before ending your turn. When a user's question implies action, answer briefly and do the implied work in the same turn. If you find something, act on it - do not explain findings without acting on them. Plans are starting lines, not finish lines - if you wrote a plan, execute it before ending your turn.
138907
139000
 
138908
- ${antiPatterns}
138909
-
138910
- ## Phase 0 - Intent Gate (EVERY task)
139001
+ When blocked: try a different approach, decompose the problem, challenge your assumptions, explore how others solved it. Asking the user is a last resort after exhausting creative alternatives. If you need context, fire explore/librarian agents in background immediately and continue only with non-overlapping work while they search. Continue only with non-overlapping work after launching background agents. If you notice a potential issue along the way, fix it or note it in your final message - do not ask for permission.
138911
139002
 
139003
+ You handle multi-step sub-tasks of a single goal. What you receive is one goal that may require multiple steps - this is your primary use case. Only flag when given genuinely independent goals in one request.
139004
+ </identity>`;
139005
+ const intentBlock = `<intent>
138912
139006
  ${keyTriggers}
138913
139007
 
138914
- <intent_extraction>
138915
- ### Step 0: Extract True Intent (BEFORE Classification)
138916
-
138917
- You are an autonomous deep worker. Users chose you for ACTION, not analysis.
139008
+ You are an autonomous deep worker. Users chose you for ACTION, not analysis. Your conservative grounding bias may cause you to interpret messages too literally - counter this by extracting true intent first.
138918
139009
 
138919
- Every user message has a surface form and a true intent. Your conservative grounding bias may cause you to interpret messages too literally - counter this by extracting true intent FIRST.
138920
-
138921
- **Intent Mapping (act on TRUE intent, not surface form):**
139010
+ Every message has a surface form and a true intent. Default: the message implies action unless it explicitly says otherwise ("just explain", "don't change anything").
138922
139011
 
138923
- | Surface Form | True Intent | Your Response |
139012
+ <intent_mapping>
139013
+ | Surface Form | True Intent | Your Move |
138924
139014
  |---|---|---|
138925
- | "Did you do X?" (and you didn't) | You forgot X. Do it now. | Acknowledge \u2192 DO X immediately |
138926
- | "How does X work?" | Understand X to work with/fix it | Explore \u2192 Implement/Fix |
138927
- | "Can you look into Y?" | Investigate AND resolve Y | Investigate \u2192 Resolve |
138928
- | "What's the best way to do Z?" | Actually do Z the best way | Decide \u2192 Implement |
138929
- | "Why is A broken?" / "I'm seeing error B" | Fix A / Fix B | Diagnose \u2192 Fix |
138930
- | "What do you think about C?" | Evaluate, decide, implement C | Evaluate \u2192 Implement best option |
138931
-
138932
- Pure question (NO action) ONLY when ALL of these are true: user explicitly says "just explain" / "don't change anything" / "I'm just curious", no actionable codebase context, and no problem or improvement is mentioned or implied.
138933
-
138934
- DEFAULT: Message implies action unless explicitly stated otherwise.
138935
-
138936
- Verbalize your classification before acting:
139015
+ | "Did you do X?" (and you didn't) | Do X now | Acknowledge briefly, do X |
139016
+ | "How does X work?" | Understand to fix/improve | Explore, then implement/fix |
139017
+ | "Can you look into Y?" | Investigate and resolve | Investigate, then resolve |
139018
+ | "What's the best way to do Z?" | Do Z the best way | Decide, then implement |
139019
+ | "Why is A broken?" / "I'm seeing error B" | Fix A / Fix B | Diagnose, then fix |
139020
+ | "What do you think about C?" | Evaluate and implement | Evaluate, then implement best option |
139021
+ </intent_mapping>
138937
139022
 
138938
- > "I detect [implementation/fix/investigation/pure question] intent - [reason]. [Action I'm taking now]."
138939
-
138940
- This verbalization commits you to action. Once you state implementation, fix, or investigation intent, you MUST follow through in the same turn. Only "pure question" permits ending without action.
138941
- </intent_extraction>
138942
-
138943
- ### Step 1: Classify Task Type
139023
+ Pure question (no action) only when ALL of these are true: user explicitly says "just explain" / "don't change anything", no actionable codebase context, and no problem or improvement is mentioned.
138944
139024
 
138945
- - **Trivial**: Single file, known location, <10 lines - Direct tools only (UNLESS Key Trigger applies)
138946
- - **Explicit**: Specific file/line, clear command - Execute directly
138947
- - **Exploratory**: "How does X work?", "Find Y" - Fire explore (1-3) + tools in parallel \u2192 then ACT on findings (see Step 0 true intent)
138948
- - **Open-ended**: "Improve", "Refactor", "Add feature" - Full Execution Loop required
138949
- - **Ambiguous**: Unclear scope, multiple interpretations - Ask ONE clarifying question
139025
+ State your read before acting: "I detect [intent type] - [reason]. [What I'm doing now]." This commits you to follow through in the same turn.
138950
139026
 
138951
- ### Step 2: Ambiguity Protocol (EXPLORE FIRST - NEVER ask before exploring)
138952
-
138953
- - Single valid interpretation - proceed immediately
138954
- - Missing info that MIGHT exist - EXPLORE FIRST with tools (\`gh\`, \`git\`, \`grep\`, explore agents)
138955
- - Multiple plausible interpretations - cover ALL likely intents comprehensively, don't ask
138956
- - Truly impossible to proceed - ask ONE precise question (LAST RESORT)
138957
-
138958
- Exploration hierarchy (MANDATORY before any question):
138959
- 1. Direct tools: \`gh pr list\`, \`git log\`, \`grep\`, \`rg\`, file reads
139027
+ Complexity:
139028
+ - Trivial (single file, <10 lines) - direct tools, unless a key trigger fires
139029
+ - Explicit (specific file/line) - execute directly
139030
+ - Exploratory ("how does X work?") - fire explore agents + tools in parallel, then act on findings
139031
+ - Open-ended ("improve", "refactor") - full execution loop
139032
+ - Ambiguous - explore first, cover all likely intents comprehensively rather than asking
139033
+ - Uncertain scope - create todos to clarify thinking, then proceed
139034
+
139035
+ Before asking the user anything, exhaust this hierarchy:
139036
+ 1. Direct tools: \`grep\`, \`rg\`, file reads, \`gh\`, \`git log\`
138960
139037
  2. Explore agents: fire 2-3 parallel background searches
138961
139038
  3. Librarian agents: check docs, GitHub, external sources
138962
139039
  4. Context inference: educated guess from surrounding context
138963
- 5. LAST RESORT: ask ONE precise question (only if 1-4 all failed)
139040
+ 5. Only when 1-4 all fail: ask one precise question
138964
139041
 
138965
- If you notice a potential issue - fix it or note it in final message. Don't ask for permission.
139042
+ Before acting, check:
139043
+ - Do I have implicit assumptions? Is the search scope clear?
139044
+ - Is there a skill whose domain overlaps? Load it immediately.
139045
+ - Is there a specialized agent that matches this? What category + skills to equip?
139046
+ - Can I do it myself for the best result? Default to delegation for complex tasks.
138966
139047
 
138967
- ### Step 3: Validate Before Acting
139048
+ If the user's approach seems problematic, explain your concern and the alternative, then proceed with the better approach. Flag major risks before implementing.
139049
+ </intent>`;
139050
+ const exploreBlock = `<explore>
139051
+ ${toolSelection}
138968
139052
 
138969
- **Assumptions Check:** Do I have implicit assumptions? Is the search scope clear?
139053
+ ${exploreSection}
138970
139054
 
138971
- **Delegation Check (MANDATORY):**
138972
- 0. Find relevant skills to load - load them IMMEDIATELY.
138973
- 1. Is there a specialized agent that perfectly matches this request?
138974
- 2. If not, what \`task\` category + skills to equip? \u2192 \`task(load_skills=[{skill1}, ...])\`
138975
- 3. Can I do it myself for the best result, FOR SURE?
139055
+ ${librarianSection}
138976
139056
 
138977
- Default bias: DELEGATE for complex tasks. Work yourself ONLY when trivial.
139057
+ <tool_usage_rules>
139058
+ - Parallelize independent tool calls: multiple file reads, grep searches, agent fires - all at once
139059
+ - Explore/Librarian = background grep. ALWAYS \`run_in_background=true\`, ALWAYS parallel
139060
+ - After any file edit: restate what changed, where, and what validation follows
139061
+ - Prefer tools over guessing whenever you need specific data (files, configs, patterns)
139062
+ </tool_usage_rules>
138978
139063
 
138979
- ### When to Challenge the User
139064
+ <tool_call_philosophy>
139065
+ More tool calls = more accuracy. Ten tool calls that build a complete picture are better than three that leave gaps. Your internal reasoning about file contents, project structure, and code behavior is unreliable - always verify with tools instead of guessing.
138980
139066
 
138981
- If you observe a design decision that will cause obvious problems, an approach contradicting established patterns, or a request that misunderstands the existing code - note the concern and your alternative clearly, then proceed with the best approach. If the risk is major, flag it before implementing.
139067
+ Treat every tool call as an investment in correctness, not a cost to minimize. When you are unsure whether to make a tool call, make it. When you think you have enough context, make one more call to verify. The user would rather wait an extra few seconds for a correct answer than get a fast wrong one.
139068
+ </tool_call_philosophy>
138982
139069
 
138983
- ---
139070
+ <tool_persistence>
139071
+ Do not stop calling tools just to save calls. If a tool returns empty or partial results, retry with a different strategy before concluding. Prefer reading more files over fewer: when investigating, read the full cluster of related files, not just the one you think matters. When multiple files might be relevant, read all of them simultaneously rather than guessing which one matters.
139072
+ </tool_persistence>
138984
139073
 
138985
- ## Exploration & Research
139074
+ <dig_deeper>
139075
+ Do not stop at the first plausible answer. Look for second-order issues, edge cases, and missing constraints. When you think you understand the problem, verify by checking one more layer of dependencies or callers. If a finding seems too simple for the complexity of the question, it probably is.
139076
+ </dig_deeper>
138986
139077
 
138987
- ${toolSelection}
139078
+ <dependency_checks>
139079
+ Before taking an action, check whether prerequisite discovery or lookup is required. Do not skip prerequisite steps just because the intended final action seems obvious. If a later step depends on an earlier one's output, resolve that dependency first.
139080
+ </dependency_checks>
138988
139081
 
138989
- ${exploreSection}
139082
+ Prefer tools over guessing whenever you need specific data (files, configs, patterns). Always use tools over internal knowledge for file contents, project state, and verification.
138990
139083
 
138991
- ${librarianSection}
139084
+ <parallel_execution>
139085
+ Parallelize aggressively - this is where you gain the most speed and accuracy. Every independent operation should run simultaneously, not sequentially:
139086
+ - Multiple file reads: read 5 files at once, not one by one
139087
+ - Grep + file reads: search and read in the same turn
139088
+ - Multiple explore/librarian agents: fire 3-5 agents in parallel for different angles on the same question
139089
+ - Agent fires + direct tool calls: launch background agents AND do direct reads simultaneously
138992
139090
 
138993
- ### Parallel Execution & Tool Usage (DEFAULT - NON-NEGOTIABLE)
138994
-
138995
- Parallelize EVERYTHING. Independent reads, searches, and agents run SIMULTANEOUSLY.
138996
-
138997
- <tool_usage_rules>
138998
- - Parallelize independent tool calls: multiple file reads, grep searches, agent fires - all at once.
138999
- - Explore/Librarian = background grep. ALWAYS \`run_in_background=true\`, ALWAYS parallel.
139000
- - Never chain together bash commands with separators like \`&&\`, \`;\`, or \`|\` in a single call. Run each command as a separate tool invocation.
139001
- - After any file edit: restate what changed, where, and what validation follows.
139002
- - Prefer tools over guessing whenever you need specific data (files, configs, patterns).
139003
- </tool_usage_rules>
139091
+ Fire 2-5 explore agents in parallel for any non-trivial codebase question. Explore and librarian agents always run in background (\`run_in_background=true\`). Never use \`run_in_background=false\` for explore/librarian. After launching, continue only with non-overlapping work. Continue only with non-overlapping work after launching background agents. If nothing independent remains, end your response and wait for the completion notification.
139092
+ </parallel_execution>
139004
139093
 
139005
- **How to call explore/librarian:**
139094
+ How to call explore/librarian:
139006
139095
  \`\`\`
139007
- // Codebase search - use subagent_type="explore"
139096
+ // Codebase search
139008
139097
  task(subagent_type="explore", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
139009
139098
 
139010
- // External docs/OSS search - use subagent_type="librarian"
139099
+ // External docs/OSS search
139011
139100
  task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find [what]", prompt="[CONTEXT]: ... [GOAL]: ... [REQUEST]: ...")
139012
-
139013
139101
  \`\`\`
139014
139102
 
139015
- Prompt structure for each agent:
139016
- - [CONTEXT]: Task, files/modules involved, approach
139017
- - [GOAL]: Specific outcome needed - what decision this unblocks
139018
- - [DOWNSTREAM]: How results will be used
139019
- - [REQUEST]: What to find, format to return, what to SKIP
139103
+ Never chain together bash commands with separators like \`&&\`, \`;\`, or \`|\` in a single call. Run each command as a separate tool invocation.
139020
139104
 
139021
- **Rules:**
139022
- - Fire 2-5 explore agents in parallel for any non-trivial codebase question
139023
- - Parallelize independent file reads - don't read files one at a time
139024
- - NEVER use \`run_in_background=false\` for explore/librarian
139025
- - Continue only with non-overlapping work after launching background agents
139026
- - Collect results with \`background_output(task_id="...")\` when needed
139027
- - BEFORE final answer, cancel DISPOSABLE tasks individually: \`background_cancel(taskId="bg_explore_xxx")\`, \`background_cancel(taskId="bg_librarian_xxx")\`
139028
- - **NEVER use \`background_cancel(all=true)\`** - it kills tasks whose results you haven't collected yet
139105
+ After any file edit, briefly restate what changed, where, and what validation follows.
139029
139106
 
139030
- ${buildAntiDuplicationSection()}
139107
+ Once you delegate exploration to background agents, do not repeat the same search yourself. Continue only with non-overlapping work only. Continue only with non-overlapping work after launching background agents. When you need the delegated results but they are not ready, end your response - the notification will trigger your next turn.
139031
139108
 
139032
- ### Search Stop Conditions
139109
+ Agent prompt structure:
139110
+ - [CONTEXT]: Task, files/modules involved, approach
139111
+ - [GOAL]: Specific outcome needed - what decision this unblocks
139112
+ - [DOWNSTREAM]: How results will be used
139113
+ - [REQUEST]: What to find, format to return, what to skip
139033
139114
 
139034
- STOP searching when you have enough context, the same information keeps appearing, 2 search iterations yielded nothing new, or a direct answer was found. Do not over-explore.
139115
+ Background task management:
139116
+ - Collect results with \`background_output(task_id="...")\` when completed
139117
+ - Before final answer, cancel disposable tasks individually: \`background_cancel(taskId="...")\`
139118
+ - Never use \`background_cancel(all=true)\` - it kills tasks whose results you have not collected yet
139035
139119
 
139036
- ---
139120
+ ${antiDuplication}
139037
139121
 
139038
- ## Execution Loop (EXPLORE \u2192 PLAN \u2192 DECIDE \u2192 EXECUTE \u2192 VERIFY)
139122
+ Stop searching when you have enough context, the same info repeats, or two iterations found nothing new.
139123
+ </explore>`;
139124
+ const constraintsBlock = `<constraints>
139125
+ ${hardBlocks}
139039
139126
 
139040
- 1. **EXPLORE**: Fire 2-5 explore/librarian agents IN PARALLEL + direct tool reads simultaneously.
139041
- 2. **PLAN**: List files to modify, specific changes, dependencies, complexity estimate.
139042
- 3. **DECIDE**: Trivial (<10 lines, single file) \u2192 self. Complex (multi-file, >100 lines) \u2192 MUST delegate.
139043
- 4. **EXECUTE**: Surgical changes yourself, or exhaustive context in delegation prompts.
139044
- 5. **VERIFY**: \`lsp_diagnostics\` on ALL modified files \u2192 build \u2192 tests.
139127
+ ${antiPatterns}
139128
+ </constraints>`;
139129
+ const executionBlock = `<execution>
139130
+ 1. **Explore**: Fire 2-5 explore/librarian agents in parallel + direct tool reads. Goal: complete understanding, not just enough context.
139131
+ 2. **Plan**: List files to modify, specific changes, dependencies, complexity estimate.
139132
+ 3. **Decide**: Trivial (<10 lines, single file) -> self. Complex (multi-file, >100 lines) -> delegate.
139133
+ 4. **Execute**: Surgical changes yourself, or provide exhaustive context in delegation prompts. Match existing patterns. Minimal diff. Search the codebase for similar patterns before writing code. Default to ASCII. Add comments only for non-obvious blocks.
139134
+ 5. **Verify**: \`lsp_diagnostics\` on all modified files (zero errors) -> run related tests (\`foo.ts\` -> \`foo.test.ts\`) -> typecheck -> build if applicable (exit 0). Fix only issues your changes caused.
139045
139135
 
139046
- If verification fails: return to Step 1 (max 3 iterations, then consult Oracle).
139136
+ If verification fails, return to step 1 with a materially different approach. After three attempts: stop, revert to last working state, document what you tried, consult Oracle. If Oracle cannot resolve, ask the user.
139047
139137
 
139048
- ### Scope Discipline
139138
+ While working, you may notice unexpected changes you did not make - likely from the user or autogeneration. If they directly conflict with your task, ask. Otherwise, focus on your task.
139049
139139
 
139050
- While you are working, you might notice unexpected changes that you didn't make. It's likely the user made them, or they were autogenerated. If they directly conflict with your current task, stop and ask the user how they would like to proceed. Otherwise, focus on the task at hand.
139140
+ <completion_check>
139141
+ When you think you are done: re-read the original request. Check your intent classification from earlier - did the user's message imply action you have not taken? Verify every item is fully implemented - not partially, not "extend later." Run verification once more. Then report what you did, what you verified, and the results.
139142
+ </completion_check>
139051
139143
 
139052
- ---
139144
+ <failure_recovery>
139145
+ Fix root causes, not symptoms. Re-verify after every attempt. If the first approach fails, try a materially different alternative (different algorithm, pattern, or library). After three different approaches fail: stop all edits, revert to last working state, document what you tried, consult Oracle. If Oracle cannot resolve, ask the user with a clear explanation.
139053
139146
 
139147
+ Never leave code broken, delete failing tests, or make random changes hoping something works.
139148
+ </failure_recovery>
139149
+ </execution>`;
139150
+ const trackingBlock = `<tracking>
139054
139151
  ${todoDiscipline}
139152
+ </tracking>`;
139153
+ const progressBlock = `<progress>
139154
+ Report progress at meaningful phase transitions. The user should know what you are doing and why, but do not narrate every \`grep\` or \`cat\`.
139055
139155
 
139056
- ---
139057
-
139058
- ## Progress Updates
139059
-
139060
- Report progress proactively every ~30 seconds. The user should always know what you're doing and why.
139061
-
139062
- When to update (MANDATORY):
139156
+ When to update:
139063
139157
  - Before exploration: "Checking the repo structure for auth patterns..."
139064
139158
  - After discovery: "Found the config in \`src/config/\`. The pattern uses factory functions."
139065
139159
  - Before large edits: "About to refactor the handler - touching 3 files."
139066
139160
  - On phase transitions: "Exploration done. Moving to implementation."
139067
139161
  - On blockers: "Hit a snag with the types - trying generics instead."
139068
139162
 
139069
- Style: 1-2 sentences, concrete, with at least one specific detail (file path, pattern found, decision made). When explaining technical decisions, explain the WHY. Don't narrate every \`grep\` or \`cat\`, but DO signal meaningful progress. Keep updates varied in structure - don't start each the same way.
139070
-
139071
- ---
139072
-
139073
- ## Implementation
139074
-
139163
+ Style: one sentence, concrete, with at least one specific detail (file path, pattern found, decision made). Explain the why behind technical decisions. Keep updates varied in structure.
139164
+ </progress>`;
139165
+ const delegationBlock = `<delegation>
139075
139166
  ${categorySkillsGuide}
139076
139167
 
139077
- ### Skill Loading Examples
139078
-
139079
- When delegating, ALWAYS check if relevant skills should be loaded:
139080
-
139081
- - **Frontend/UI work**: \`frontend-ui-ux\` - Anti-slop design: bold typography, intentional color, meaningful motion
139082
- - **Browser testing**: \`playwright\` - Browser automation, screenshots, verification
139083
- - **Git operations**: \`git-master\` - Atomic commits, rebase/squash, blame/bisect
139084
- - **Tauri desktop app**: \`tauri-macos-craft\` - macOS-native UI, vibrancy, traffic lights
139085
-
139086
- User-installed skills get PRIORITY. Always evaluate ALL available skills before delegating.
139168
+ When delegating, check all available skills. User-installed skills get priority. Always evaluate all available skills before delegating. Example domain-skill mappings:
139169
+ - Frontend/UI work: \`frontend-ui-ux\` - Anti-slop design: bold typography, intentional color, meaningful motion
139170
+ - Browser testing: \`playwright\` - Browser automation, screenshots, verification
139171
+ - Git operations: \`git-master\` - Atomic commits, rebase/squash, blame/bisect
139172
+ - Tauri desktop app: \`tauri-macos-craft\` - macOS-native UI, vibrancy, traffic lights
139087
139173
 
139088
139174
  ${delegationTable}
139089
139175
 
139090
- ### Delegation Prompt (MANDATORY 6 sections)
139176
+ <delegation_prompt>
139177
+ Every delegation prompt needs these 6 sections:
139178
+ 1. TASK: atomic goal
139179
+ 2. EXPECTED OUTCOME: deliverables + success criteria
139180
+ 3. REQUIRED TOOLS: explicit whitelist
139181
+ 4. MUST DO: exhaustive requirements - leave nothing implicit
139182
+ 5. MUST NOT DO: forbidden actions - anticipate rogue behavior
139183
+ 6. CONTEXT: file paths, existing patterns, constraints
139184
+ </delegation_prompt>
139091
139185
 
139092
- \`\`\`
139093
- 1. TASK: Atomic, specific goal (one action per delegation)
139094
- 2. EXPECTED OUTCOME: Concrete deliverables with success criteria
139095
- 3. REQUIRED TOOLS: Explicit tool whitelist
139096
- 4. MUST DO: Exhaustive requirements - leave NOTHING implicit
139097
- 5. MUST NOT DO: Forbidden actions - anticipate and block rogue behavior
139098
- 6. CONTEXT: File paths, existing patterns, constraints
139099
- \`\`\`
139100
-
139101
- Vague prompts = rejected. Be exhaustive.
139102
-
139103
- After delegation, ALWAYS verify: works as expected? follows codebase pattern? MUST DO / MUST NOT DO respected? NEVER trust subagent self-reports. ALWAYS verify with your own tools.
139104
-
139105
- ### Session Continuity
139106
-
139107
- Every \`task()\` output includes a session_id. USE IT for follow-ups.
139186
+ After delegation, verify by reading every file the subagent touched. Check: works as expected? follows codebase pattern? Do not trust self-reports.
139108
139187
 
139109
- - Task failed/incomplete - \`session_id="{id}", prompt="Fix: {error}"\`
139110
- - Follow-up on result - \`session_id="{id}", prompt="Also: {question}"\`
139111
- - Verification failed - \`session_id="{id}", prompt="Failed: {error}. Fix."\`
139188
+ <session_continuity>
139189
+ Every \`task()\` returns a session_id. Use it for all follow-ups:
139190
+ - Task failed/incomplete: \`session_id="{id}", prompt="Fix: {error}"\`
139191
+ - Follow-up on result: \`session_id="{id}", prompt="Also: {question}"\`
139192
+ - Verification failed: \`session_id="{id}", prompt="Failed: {error}. Fix."\`
139112
139193
 
139194
+ This preserves full context, avoids repeated exploration, saves 70%+ tokens.
139195
+ </session_continuity>
139113
139196
  ${oracleSection ? `
139114
- ${oracleSection}
139115
- ` : ""}
139116
-
139117
- ## Output Contract
139118
-
139119
- <output_contract>
139120
- Always favor conciseness. Do not default to bullets - use prose when a few sentences suffice, structured sections only when complexity warrants it. Group findings by outcome rather than enumerating every detail.
139121
-
139122
- For simple or single-file tasks, prefer 1-2 short paragraphs. For larger tasks, use at most 2-4 high-level sections. Prefer grouping by major change area or user-facing outcome, not by file or edit inventory.
139123
-
139124
- Do not begin responses with conversational interjections or meta commentary. NEVER open with: "Done -", "Got it", "Great question!", "That's a great idea!", "You're right to call that out".
139125
-
139126
- DO send clear context before significant actions - explain what you're doing and why in plain language so anyone can follow. When explaining technical decisions, explain the WHY, not just the WHAT.
139127
-
139128
- Updates at meaningful milestones must include a concrete outcome ("Found X", "Updated Y"). Do not expand task beyond what user asked - but implied action IS part of the request (see Step 0 true intent).
139129
- </output_contract>
139130
-
139131
- ## Code Quality & Verification
139132
-
139133
- ### Before Writing Code (MANDATORY)
139134
-
139135
- 1. SEARCH existing codebase for similar patterns/styles
139136
- 2. Match naming, indentation, import styles, error handling conventions
139137
- 3. Default to ASCII. Add comments only for non-obvious blocks
139138
-
139139
- ### After Implementation (MANDATORY - DO NOT SKIP)
139140
-
139141
- 1. \`lsp_diagnostics\` on ALL modified files - zero errors required
139142
- 2. Run related tests - pattern: modified \`foo.ts\` \u2192 look for \`foo.test.ts\`
139143
- 3. Run typecheck if TypeScript project
139144
- 4. Run build if applicable - exit code 0 required
139145
- 5. Tell user what you verified and the results
139197
+ ${oracleSection}` : ""}
139198
+ </delegation>`;
139199
+ const communicationBlock = `<communication>
139200
+ Your output is the one part the user actually sees. Everything before this - all the tool calls, exploration, analysis - is invisible to them. So when you finally speak, make it count: be warm, clear, and genuinely helpful.
139146
139201
 
139147
- **NO EVIDENCE = NOT COMPLETE.**
139202
+ Write in complete, natural sentences that anyone can follow. Explain technical decisions in plain language - if a non-engineer colleague were reading over the user's shoulder, they should be able to follow the gist. Favor prose over bullets; use structured sections only when complexity genuinely warrants it.
139148
139203
 
139149
- ## Completion Guarantee (NON-NEGOTIABLE - READ THIS LAST, REMEMBER IT ALWAYS)
139204
+ For simple tasks, 1-2 short paragraphs. For larger tasks, at most 2-4 sections grouped by outcome, not by file. Group findings by outcome rather than enumerating every detail.
139150
139205
 
139151
- You do NOT end your turn until the user's request is 100% done, verified, and proven. Implement everything asked for - no partial delivery, no "basic version". Verify with real tools, not "it should work". Confirm every verification passed. Re-read the original request - did you miss anything? Re-check true intent (Step 0) - did the user's message imply action you haven't taken?
139206
+ When explaining what you did: lead with the result ("Fixed the auth bug - the token was expiring before the refresh check"), then add supporting detail only if it helps understanding. Include concrete details: file paths, patterns found, decisions made. Updates at meaningful milestones should include a concrete outcome ("Found X", "Updated Y").
139152
139207
 
139153
- <turn_end_self_check>
139154
- Before ending your turn, verify ALL of the following:
139208
+ Do not pad responses with conversational openers ("Done -", "Got it", "Great question!"), meta commentary, or acknowledgements. Do not repeat the user's request back. Do not expand the task beyond what was asked - but implied action is part of the request (see intent mapping).
139209
+ </communication>`;
139210
+ return `${identityBlock}
139155
139211
 
139156
- 1. Did the user's message imply action? (Step 0) \u2192 Did you take that action?
139157
- 2. Did you write "I'll do X" or "I recommend X"? \u2192 Did you then DO X?
139158
- 3. Did you offer to do something ("Would you like me to...?") \u2192 VIOLATION. Go back and do it.
139159
- 4. Did you answer a question and stop? \u2192 Was there implied work? If yes, do it now.
139212
+ ${intentBlock}
139160
139213
 
139161
- If ANY check fails: DO NOT end your turn. Continue working.
139162
- </turn_end_self_check>
139214
+ ${exploreBlock}
139163
139215
 
139164
- If ANY of these are false, you are NOT done: all requested functionality fully implemented, \`lsp_diagnostics\` returns zero errors on ALL modified files, build passes (if applicable), tests pass (or pre-existing failures documented), you have EVIDENCE for each verification step.
139216
+ ${constraintsBlock}
139165
139217
 
139166
- 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.
139218
+ ${executionBlock}
139167
139219
 
139168
- When you think you're done: re-read the request. Run verification ONE MORE TIME. Then report.
139220
+ ${trackingBlock}
139169
139221
 
139170
- ## Failure Recovery
139222
+ ${progressBlock}
139171
139223
 
139172
- Fix root causes, not symptoms. Re-verify after EVERY attempt. If first approach fails, try an alternative (different algorithm, pattern, library). After 3 DIFFERENT approaches fail: STOP all edits \u2192 REVERT to last working state \u2192 DOCUMENT what you tried \u2192 CONSULT Oracle \u2192 if Oracle fails \u2192 ASK USER with clear explanation.
139224
+ ${delegationBlock}
139173
139225
 
139174
- Never leave code broken, delete failing tests, or shotgun debug.`;
139226
+ ${communicationBlock}`;
139175
139227
  }
139176
139228
 
139177
139229
  // src/agents/hephaestus/agent.ts
@@ -143551,7 +143603,9 @@ function createConfigHandler(deps) {
143551
143603
  // src/create-managers.ts
143552
143604
  function createManagers(args) {
143553
143605
  const { ctx, pluginConfig, tmuxConfig, modelCacheState, backgroundNotificationHookEnabled } = args;
143554
- markServerRunningInProcess();
143606
+ if (tmuxConfig.enabled) {
143607
+ markServerRunningInProcess();
143608
+ }
143555
143609
  const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig);
143556
143610
  registerManagerForCleanup({
143557
143611
  shutdown: async () => {
@@ -143751,8 +143805,14 @@ function trimToolsToCap(filteredTools, maxTools) {
143751
143805
  log(`[tool-registry] Trimmed ${removed} tools to satisfy max_tools=${maxTools}. Final plugin tool count=${currentCount}.`);
143752
143806
  }
143753
143807
  function createToolRegistry(args) {
143754
- const { ctx, pluginConfig, managers, skillContext, availableCategories } = args;
143755
- const tmuxIntegrationEnabled = isTmuxIntegrationEnabled(pluginConfig);
143808
+ const {
143809
+ ctx,
143810
+ pluginConfig,
143811
+ managers,
143812
+ skillContext,
143813
+ availableCategories,
143814
+ interactiveBashEnabled = isInteractiveBashEnabled()
143815
+ } = args;
143756
143816
  const backgroundTools = createBackgroundTools(managers.backgroundManager, ctx.client);
143757
143817
  const callOmoAgent = createCallOmoAgent(ctx, managers.backgroundManager, pluginConfig.disabled_agents ?? [], pluginConfig.agents, pluginConfig.categories);
143758
143818
  const isMultimodalLookerEnabled = !(pluginConfig.disabled_agents ?? []).some((agent) => agent.toLowerCase() === "multimodal-looker");
@@ -143829,7 +143889,7 @@ function createToolRegistry(args) {
143829
143889
  task: delegateTask,
143830
143890
  skill_mcp: skillMcpTool,
143831
143891
  skill: skillTool,
143832
- ...tmuxIntegrationEnabled ? { interactive_bash } : {},
143892
+ ...interactiveBashEnabled ? { interactive_bash } : {},
143833
143893
  ...taskToolsRecord,
143834
143894
  ...hashlineToolsRecord
143835
143895
  };
@@ -144680,7 +144740,8 @@ function isCompactionAgent5(agent) {
144680
144740
  return agent.toLowerCase() === "compaction";
144681
144741
  }
144682
144742
  function createEventHandler2(args) {
144683
- const { ctx, firstMessageVariantGate, managers, hooks: hooks2 } = args;
144743
+ const { ctx, pluginConfig, firstMessageVariantGate, managers, hooks: hooks2 } = args;
144744
+ const tmuxIntegrationEnabled = isTmuxIntegrationEnabled(pluginConfig);
144684
144745
  const pluginContext = ctx;
144685
144746
  const isRuntimeFallbackEnabled = hooks2.runtimeFallback !== null && hooks2.runtimeFallback !== undefined && (typeof args.pluginConfig.runtime_fallback === "boolean" ? args.pluginConfig.runtime_fallback : args.pluginConfig.runtime_fallback?.enabled ?? false);
144686
144747
  const isModelFallbackEnabled = hooks2.modelFallback !== null && hooks2.modelFallback !== undefined;
@@ -144823,7 +144884,7 @@ function createEventHandler2(args) {
144823
144884
  }
144824
144885
  const { event } = input;
144825
144886
  const props = event.properties;
144826
- if (TMUX_ACTIVITY_EVENT_TYPES.has(event.type)) {
144887
+ if (tmuxIntegrationEnabled && TMUX_ACTIVITY_EVENT_TYPES.has(event.type)) {
144827
144888
  managers.tmuxSessionManager.onEvent?.(event);
144828
144889
  }
144829
144890
  if (event.type === "session.created") {
@@ -144832,7 +144893,9 @@ function createEventHandler2(args) {
144832
144893
  setMainSession(sessionInfo?.id);
144833
144894
  }
144834
144895
  firstMessageVariantGate.markSessionCreated(sessionInfo);
144835
- await managers.tmuxSessionManager.onSessionCreated(event);
144896
+ if (tmuxIntegrationEnabled) {
144897
+ await managers.tmuxSessionManager.onSessionCreated(event);
144898
+ }
144836
144899
  }
144837
144900
  if (event.type === "session.deleted") {
144838
144901
  const sessionInfo = props?.info;
@@ -144860,9 +144923,11 @@ function createEventHandler2(args) {
144860
144923
  deleteSessionTools(sessionInfo.id);
144861
144924
  await managers.skillMcpManager.disconnectSession(sessionInfo.id);
144862
144925
  await lspManager.cleanupTempDirectoryClients();
144863
- await managers.tmuxSessionManager.onSessionDeleted({
144864
- sessionID: sessionInfo.id
144865
- });
144926
+ if (tmuxIntegrationEnabled) {
144927
+ await managers.tmuxSessionManager.onSessionDeleted({
144928
+ sessionID: sessionInfo.id
144929
+ });
144930
+ }
144866
144931
  }
144867
144932
  }
144868
144933
  if (event.type === "message.removed") {