oh-my-opencode 3.15.2 → 3.15.3

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
@@ -60789,15 +60789,19 @@ function convertSDKMessageToStoredMessage(msg) {
60789
60789
  async function findNearestMessageWithFieldsFromSDK(client, sessionID) {
60790
60790
  try {
60791
60791
  const response = await client.session.messages({ path: { id: sessionID } });
60792
- const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
60793
- for (let i2 = messages.length - 1;i2 >= 0; i2--) {
60794
- const stored = convertSDKMessageToStoredMessage(messages[i2]);
60792
+ const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true }).map((message) => ({
60793
+ stored: convertSDKMessageToStoredMessage(message),
60794
+ createdAt: message.info?.time?.created ?? Number.NEGATIVE_INFINITY,
60795
+ id: typeof message.id === "string" ? message.id : ""
60796
+ })).sort((left, right) => right.createdAt - left.createdAt || right.id.localeCompare(left.id));
60797
+ for (const message of messages) {
60798
+ const stored = message.stored;
60795
60799
  if (stored?.agent && stored.model?.providerID && stored.model?.modelID) {
60796
60800
  return stored;
60797
60801
  }
60798
60802
  }
60799
- for (let i2 = messages.length - 1;i2 >= 0; i2--) {
60800
- const stored = convertSDKMessageToStoredMessage(messages[i2]);
60803
+ for (const message of messages) {
60804
+ const stored = message.stored;
60801
60805
  if (stored?.agent || stored?.model?.providerID && stored?.model?.modelID) {
60802
60806
  return stored;
60803
60807
  }
@@ -60813,7 +60817,15 @@ async function findNearestMessageWithFieldsFromSDK(client, sessionID) {
60813
60817
  async function findFirstMessageWithAgentFromSDK(client, sessionID) {
60814
60818
  try {
60815
60819
  const response = await client.session.messages({ path: { id: sessionID } });
60816
- const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
60820
+ const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true }).sort((left, right) => {
60821
+ const leftTime = left.info?.time?.created ?? Number.POSITIVE_INFINITY;
60822
+ const rightTime = right.info?.time?.created ?? Number.POSITIVE_INFINITY;
60823
+ if (leftTime !== rightTime)
60824
+ return leftTime - rightTime;
60825
+ const leftId = typeof left.id === "string" ? left.id : "";
60826
+ const rightId = typeof right.id === "string" ? right.id : "";
60827
+ return leftId.localeCompare(rightId);
60828
+ });
60817
60829
  for (const msg of messages) {
60818
60830
  const stored = convertSDKMessageToStoredMessage(msg);
60819
60831
  if (stored?.agent) {
@@ -60833,27 +60845,27 @@ function findNearestMessageWithFields(messageDir) {
60833
60845
  return null;
60834
60846
  }
60835
60847
  try {
60836
- const files = readdirSync(messageDir).filter((f) => f.endsWith(".json")).sort().reverse();
60837
- for (const file of files) {
60848
+ const messages = readdirSync(messageDir).filter((f) => f.endsWith(".json")).map((fileName) => {
60838
60849
  try {
60839
- const content = readFileSync6(join12(messageDir, file), "utf-8");
60850
+ const content = readFileSync6(join12(messageDir, fileName), "utf-8");
60840
60851
  const msg = JSON.parse(content);
60841
- if (msg.agent && msg.model?.providerID && msg.model?.modelID) {
60842
- return msg;
60843
- }
60852
+ return {
60853
+ fileName,
60854
+ msg,
60855
+ createdAt: typeof msg.time?.created === "number" ? msg.time.created : Number.NEGATIVE_INFINITY
60856
+ };
60844
60857
  } catch {
60845
- continue;
60858
+ return null;
60859
+ }
60860
+ }).filter((entry) => entry !== null).sort((left, right) => right.createdAt - left.createdAt || right.fileName.localeCompare(left.fileName));
60861
+ for (const entry of messages) {
60862
+ if (entry.msg.agent && entry.msg.model?.providerID && entry.msg.model?.modelID) {
60863
+ return entry.msg;
60846
60864
  }
60847
60865
  }
60848
- for (const file of files) {
60849
- try {
60850
- const content = readFileSync6(join12(messageDir, file), "utf-8");
60851
- const msg = JSON.parse(content);
60852
- if (msg.agent || msg.model?.providerID && msg.model?.modelID) {
60853
- return msg;
60854
- }
60855
- } catch {
60856
- continue;
60866
+ for (const entry of messages) {
60867
+ if (entry.msg.agent || entry.msg.model?.providerID && entry.msg.model?.modelID) {
60868
+ return entry.msg;
60857
60869
  }
60858
60870
  }
60859
60871
  } catch {
@@ -60866,16 +60878,22 @@ function findFirstMessageWithAgent(messageDir) {
60866
60878
  return null;
60867
60879
  }
60868
60880
  try {
60869
- const files = readdirSync(messageDir).filter((f) => f.endsWith(".json")).sort();
60870
- for (const file of files) {
60881
+ const messages = readdirSync(messageDir).filter((f) => f.endsWith(".json")).map((fileName) => {
60871
60882
  try {
60872
- const content = readFileSync6(join12(messageDir, file), "utf-8");
60883
+ const content = readFileSync6(join12(messageDir, fileName), "utf-8");
60873
60884
  const msg = JSON.parse(content);
60874
- if (msg.agent) {
60875
- return msg.agent;
60876
- }
60885
+ return {
60886
+ fileName,
60887
+ msg,
60888
+ createdAt: typeof msg.time?.created === "number" ? msg.time.created : Number.POSITIVE_INFINITY
60889
+ };
60877
60890
  } catch {
60878
- continue;
60891
+ return null;
60892
+ }
60893
+ }).filter((entry) => entry !== null).sort((left, right) => left.createdAt - right.createdAt || left.fileName.localeCompare(right.fileName));
60894
+ for (const entry of messages) {
60895
+ if (entry.msg.agent) {
60896
+ return entry.msg.agent;
60879
60897
  }
60880
60898
  }
60881
60899
  } catch {
@@ -60983,7 +61001,7 @@ function getAgentConfigKey(agentName) {
60983
61001
  return lower;
60984
61002
  return lower;
60985
61003
  }
60986
- function normalizeAgentForPrompt(agentName) {
61004
+ function normalizeAgentForPromptKey(agentName) {
60987
61005
  if (typeof agentName !== "string") {
60988
61006
  return;
60989
61007
  }
@@ -60994,10 +61012,10 @@ function normalizeAgentForPrompt(agentName) {
60994
61012
  const lower = trimmed.toLowerCase();
60995
61013
  const reversed = REVERSE_DISPLAY_NAMES[lower];
60996
61014
  if (reversed !== undefined) {
60997
- return AGENT_DISPLAY_NAMES[reversed] ?? trimmed;
61015
+ return reversed;
60998
61016
  }
60999
61017
  if (AGENT_DISPLAY_NAMES[lower] !== undefined) {
61000
- return AGENT_DISPLAY_NAMES[lower];
61018
+ return lower;
61001
61019
  }
61002
61020
  return trimmed;
61003
61021
  }
@@ -63497,11 +63515,12 @@ async function injectContinuation(args) {
63497
63515
  } : undefined);
63498
63516
  tools = tools ?? previousMessage?.tools;
63499
63517
  }
63500
- if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
63518
+ const promptAgent = normalizeAgentForPromptKey(agentName);
63519
+ if (promptAgent && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(promptAgent))) {
63501
63520
  log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
63502
63521
  return;
63503
63522
  }
63504
- if (!agentName) {
63523
+ if (!promptAgent) {
63505
63524
  const compactionState = sessionStateStore.getExistingState(sessionID);
63506
63525
  if (compactionState && isCompactionGuardActive(compactionState, Date.now())) {
63507
63526
  log(`[${HOOK_NAME}] Skipped: agent unknown after compaction`, { sessionID });
@@ -63532,7 +63551,7 @@ ${todoList}`;
63532
63551
  try {
63533
63552
  log(`[${HOOK_NAME}] Injecting continuation`, {
63534
63553
  sessionID,
63535
- agent: agentName,
63554
+ agent: promptAgent,
63536
63555
  model,
63537
63556
  incompleteCount: freshIncompleteCount
63538
63557
  });
@@ -63540,7 +63559,7 @@ ${todoList}`;
63540
63559
  await ctx.client.session.promptAsync({
63541
63560
  path: { id: sessionID },
63542
63561
  body: {
63543
- agent: agentName,
63562
+ agent: promptAgent,
63544
63563
  ...model !== undefined ? { model } : {},
63545
63564
  ...inheritedTools ? { tools: inheritedTools } : {},
63546
63565
  parts: [createInternalAgentTextPart(prompt)]
@@ -94774,6 +94793,15 @@ function readBoulderState(directory) {
94774
94793
  if (!Array.isArray(parsed.session_ids)) {
94775
94794
  parsed.session_ids = [];
94776
94795
  }
94796
+ if (!parsed.session_origins || typeof parsed.session_origins !== "object" || Array.isArray(parsed.session_origins)) {
94797
+ parsed.session_origins = {};
94798
+ }
94799
+ if (parsed.session_ids.length === 1) {
94800
+ const soleSessionId = parsed.session_ids[0];
94801
+ if (typeof soleSessionId === "string" && parsed.session_origins[soleSessionId] !== "appended" && parsed.session_origins[soleSessionId] !== "direct") {
94802
+ parsed.session_origins[soleSessionId] = "direct";
94803
+ }
94804
+ }
94777
94805
  if (!parsed.task_sessions || typeof parsed.task_sessions !== "object" || Array.isArray(parsed.task_sessions)) {
94778
94806
  parsed.task_sessions = {};
94779
94807
  }
@@ -94795,22 +94823,34 @@ function writeBoulderState(directory, state3) {
94795
94823
  return false;
94796
94824
  }
94797
94825
  }
94798
- function appendSessionId(directory, sessionId) {
94826
+ function appendSessionId(directory, sessionId, origin = "direct") {
94799
94827
  const state3 = readBoulderState(directory);
94800
94828
  if (!state3)
94801
94829
  return null;
94830
+ if (!state3.session_origins || typeof state3.session_origins !== "object" || Array.isArray(state3.session_origins)) {
94831
+ state3.session_origins = {};
94832
+ }
94802
94833
  if (!state3.session_ids?.includes(sessionId)) {
94803
94834
  if (!Array.isArray(state3.session_ids)) {
94804
94835
  state3.session_ids = [];
94805
94836
  }
94806
94837
  const originalSessionIds = [...state3.session_ids];
94838
+ const originalSessionOrigins = { ...state3.session_origins };
94807
94839
  state3.session_ids.push(sessionId);
94840
+ state3.session_origins[sessionId] = origin;
94808
94841
  if (writeBoulderState(directory, state3)) {
94809
94842
  return state3;
94810
94843
  }
94811
94844
  state3.session_ids = originalSessionIds;
94845
+ state3.session_origins = originalSessionOrigins;
94812
94846
  return null;
94813
94847
  }
94848
+ if (!state3.session_origins[sessionId]) {
94849
+ state3.session_origins[sessionId] = origin;
94850
+ if (!writeBoulderState(directory, state3)) {
94851
+ return null;
94852
+ }
94853
+ }
94814
94854
  return state3;
94815
94855
  }
94816
94856
  function clearBoulderState(directory) {
@@ -94899,6 +94939,9 @@ function createBoulderState(planPath, sessionId, agent, worktreePath) {
94899
94939
  active_plan: planPath,
94900
94940
  started_at: new Date().toISOString(),
94901
94941
  session_ids: [sessionId],
94942
+ session_origins: {
94943
+ [sessionId]: "direct"
94944
+ },
94902
94945
  plan_name: getPlanName(planPath),
94903
94946
  ...agent !== undefined ? { agent } : {},
94904
94947
  ...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
@@ -95530,6 +95573,100 @@ function isAbortError(error48) {
95530
95573
  return false;
95531
95574
  }
95532
95575
 
95576
+ // src/hooks/atlas/session-last-agent.ts
95577
+ import { readFileSync as readFileSync44, readdirSync as readdirSync17 } from "fs";
95578
+ import { join as join68 } from "path";
95579
+ function isCompactionAgent2(agent) {
95580
+ return typeof agent === "string" && agent.toLowerCase() === "compaction";
95581
+ }
95582
+ function getLastAgentFromMessageDir(messageDir) {
95583
+ try {
95584
+ const messages = readdirSync17(messageDir).filter((fileName) => fileName.endsWith(".json")).map((fileName) => {
95585
+ try {
95586
+ const content = readFileSync44(join68(messageDir, fileName), "utf-8");
95587
+ const parsed = JSON.parse(content);
95588
+ return {
95589
+ fileName,
95590
+ agent: parsed.agent,
95591
+ createdAt: typeof parsed.time?.created === "number" ? parsed.time.created : Number.NEGATIVE_INFINITY
95592
+ };
95593
+ } catch {
95594
+ return null;
95595
+ }
95596
+ }).filter((message) => message !== null).sort((left, right) => right.createdAt - left.createdAt || right.fileName.localeCompare(left.fileName));
95597
+ for (const message of messages) {
95598
+ if (typeof message.agent === "string" && !isCompactionAgent2(message.agent)) {
95599
+ return message.agent.toLowerCase();
95600
+ }
95601
+ }
95602
+ } catch {
95603
+ return null;
95604
+ }
95605
+ return null;
95606
+ }
95607
+ async function getLastAgentFromSession(sessionID, client) {
95608
+ if (isSqliteBackend() && client) {
95609
+ try {
95610
+ const response = await client.session.messages({ path: { id: sessionID } });
95611
+ const messages = normalizeSDKResponse(response, [], {
95612
+ preferResponseOnMissingData: true
95613
+ }).sort((left, right) => {
95614
+ const leftTime = left.info?.time?.created ?? Number.NEGATIVE_INFINITY;
95615
+ const rightTime = right.info?.time?.created ?? Number.NEGATIVE_INFINITY;
95616
+ if (leftTime !== rightTime) {
95617
+ return rightTime - leftTime;
95618
+ }
95619
+ const leftId = typeof left.id === "string" ? left.id : "";
95620
+ const rightId = typeof right.id === "string" ? right.id : "";
95621
+ return rightId.localeCompare(leftId);
95622
+ });
95623
+ for (const message of messages) {
95624
+ const agent = message.info?.agent;
95625
+ if (typeof agent === "string" && !isCompactionAgent2(agent)) {
95626
+ return agent.toLowerCase();
95627
+ }
95628
+ }
95629
+ } catch {
95630
+ return null;
95631
+ }
95632
+ return null;
95633
+ }
95634
+ const messageDir = getMessageDir(sessionID);
95635
+ if (!messageDir)
95636
+ return null;
95637
+ return getLastAgentFromMessageDir(messageDir);
95638
+ }
95639
+
95640
+ // src/hooks/atlas/boulder-session-lineage.ts
95641
+ init_logger();
95642
+ async function isSessionInBoulderLineage(input) {
95643
+ const visitedSessionIDs = new Set;
95644
+ let currentSessionID = input.sessionID;
95645
+ while (!visitedSessionIDs.has(currentSessionID)) {
95646
+ visitedSessionIDs.add(currentSessionID);
95647
+ const sessionResult = await input.client.session.get({ path: { id: currentSessionID } }).catch((error48) => {
95648
+ log(`[${HOOK_NAME7}] Failed to resolve session lineage`, {
95649
+ sessionID: input.sessionID,
95650
+ currentSessionID,
95651
+ error: error48
95652
+ });
95653
+ return null;
95654
+ });
95655
+ if (!sessionResult || sessionResult.error) {
95656
+ return false;
95657
+ }
95658
+ const parentSessionID = sessionResult.data?.parentID;
95659
+ if (!parentSessionID) {
95660
+ return false;
95661
+ }
95662
+ if (input.boulderSessionIDs.includes(parentSessionID)) {
95663
+ return true;
95664
+ }
95665
+ currentSessionID = parentSessionID;
95666
+ }
95667
+ return false;
95668
+ }
95669
+
95533
95670
  // src/hooks/atlas/idle-event.ts
95534
95671
  init_logger();
95535
95672
 
@@ -95726,9 +95863,17 @@ If you were given **multiple genuinely independent goals** (unrelated tasks, par
95726
95863
  async function resolveRecentPromptContextForSession(ctx, sessionID) {
95727
95864
  try {
95728
95865
  const messagesResp = await ctx.client.session.messages({ path: { id: sessionID } });
95729
- const messages = normalizeSDKResponse(messagesResp, []);
95730
- for (let i2 = messages.length - 1;i2 >= 0; i2--) {
95731
- const info = messages[i2].info;
95866
+ const messages = normalizeSDKResponse(messagesResp, []).sort((left, right) => {
95867
+ const leftTime = left.info?.time?.created ?? Number.NEGATIVE_INFINITY;
95868
+ const rightTime = right.info?.time?.created ?? Number.NEGATIVE_INFINITY;
95869
+ if (leftTime !== rightTime)
95870
+ return rightTime - leftTime;
95871
+ const leftId = typeof left.id === "string" ? left.id : "";
95872
+ const rightId = typeof right.id === "string" ? right.id : "";
95873
+ return rightId.localeCompare(leftId);
95874
+ });
95875
+ for (const message of messages) {
95876
+ const info = message.info;
95732
95877
  const model2 = info?.model;
95733
95878
  const tools2 = normalizePromptTools(info?.tools);
95734
95879
  if (model2?.providerID && model2?.modelID) {
@@ -95772,7 +95917,7 @@ async function injectBoulderContinuation(input) {
95772
95917
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
95773
95918
  if (hasRunningBgTasks) {
95774
95919
  log(`[${HOOK_NAME7}] Skipped injection: background tasks running`, { sessionID });
95775
- return;
95920
+ return "skipped_background_tasks";
95776
95921
  }
95777
95922
  const worktreeContext = worktreePath ? `
95778
95923
 
@@ -95789,7 +95934,7 @@ async function injectBoulderContinuation(input) {
95789
95934
  sessionID,
95790
95935
  agent: continuationAgent ?? agent ?? "unknown"
95791
95936
  });
95792
- return;
95937
+ return "skipped_agent_unavailable";
95793
95938
  }
95794
95939
  try {
95795
95940
  log(`[${HOOK_NAME7}] Injecting boulder continuation`, { sessionID, planName, remaining });
@@ -95798,7 +95943,7 @@ async function injectBoulderContinuation(input) {
95798
95943
  await ctx.client.session.promptAsync({
95799
95944
  path: { id: sessionID },
95800
95945
  body: {
95801
- agent: normalizeAgentForPrompt(continuationAgent) ?? continuationAgent,
95946
+ agent: continuationAgent,
95802
95947
  ...promptContext.model !== undefined ? { model: promptContext.model } : {},
95803
95948
  ...inheritedTools ? { tools: inheritedTools } : {},
95804
95949
  parts: [createInternalAgentTextPart(prompt)]
@@ -95807,6 +95952,7 @@ async function injectBoulderContinuation(input) {
95807
95952
  });
95808
95953
  sessionState.promptFailureCount = 0;
95809
95954
  log(`[${HOOK_NAME7}] Boulder continuation injected`, { sessionID });
95955
+ return "injected";
95810
95956
  } catch (err) {
95811
95957
  sessionState.promptFailureCount += 1;
95812
95958
  sessionState.lastFailureAt = Date.now();
@@ -95815,72 +95961,24 @@ async function injectBoulderContinuation(input) {
95815
95961
  error: String(err),
95816
95962
  promptFailureCount: sessionState.promptFailureCount
95817
95963
  });
95964
+ return "failed";
95818
95965
  }
95819
95966
  }
95820
95967
 
95821
- // src/hooks/atlas/boulder-session-lineage.ts
95822
- init_logger();
95823
- async function isSessionInBoulderLineage(input) {
95824
- const visitedSessionIDs = new Set;
95825
- let currentSessionID = input.sessionID;
95826
- while (!visitedSessionIDs.has(currentSessionID)) {
95827
- visitedSessionIDs.add(currentSessionID);
95828
- const sessionResult = await input.client.session.get({ path: { id: currentSessionID } }).catch((error48) => {
95829
- log(`[${HOOK_NAME7}] Failed to resolve session lineage`, {
95830
- sessionID: input.sessionID,
95831
- currentSessionID,
95832
- error: error48
95833
- });
95834
- return null;
95835
- });
95836
- if (!sessionResult || sessionResult.error) {
95837
- return false;
95838
- }
95839
- const parentSessionID = sessionResult.data?.parentID;
95840
- if (!parentSessionID) {
95841
- return false;
95842
- }
95843
- if (input.boulderSessionIDs.includes(parentSessionID)) {
95844
- return true;
95845
- }
95846
- currentSessionID = parentSessionID;
95847
- }
95848
- return false;
95849
- }
95850
-
95851
95968
  // src/hooks/atlas/resolve-active-boulder-session.ts
95852
95969
  async function resolveActiveBoulderSession(input) {
95853
95970
  const boulderState = readBoulderState(input.directory);
95854
95971
  if (!boulderState) {
95855
95972
  return null;
95856
95973
  }
95974
+ if (!boulderState.session_ids.includes(input.sessionID)) {
95975
+ return null;
95976
+ }
95857
95977
  const progress = getPlanProgress(boulderState.active_plan);
95858
95978
  if (progress.isComplete) {
95859
95979
  return { boulderState, progress, appendedSession: false };
95860
95980
  }
95861
- if (boulderState.session_ids.includes(input.sessionID)) {
95862
- return { boulderState, progress, appendedSession: false };
95863
- }
95864
- if (!subagentSessions.has(input.sessionID)) {
95865
- return null;
95866
- }
95867
- const belongsToActiveBoulder = await isSessionInBoulderLineage({
95868
- client: input.client,
95869
- sessionID: input.sessionID,
95870
- boulderSessionIDs: boulderState.session_ids
95871
- });
95872
- if (!belongsToActiveBoulder) {
95873
- return null;
95874
- }
95875
- const updatedBoulderState = appendSessionId(input.directory, input.sessionID);
95876
- if (!updatedBoulderState?.session_ids.includes(input.sessionID)) {
95877
- return null;
95878
- }
95879
- return {
95880
- boulderState: updatedBoulderState,
95881
- progress,
95882
- appendedSession: true
95883
- };
95981
+ return { boulderState, progress, appendedSession: false };
95884
95982
  }
95885
95983
 
95886
95984
  // src/hooks/atlas/idle-event.ts
@@ -95894,12 +95992,38 @@ function hasRunningBackgroundTasks(sessionID, options) {
95894
95992
  }
95895
95993
  async function injectContinuation2(input) {
95896
95994
  const remaining = input.progress.total - input.progress.completed;
95897
- input.sessionState.lastContinuationInjectedAt = Date.now();
95995
+ if (input.sessionState.isInjectingContinuation) {
95996
+ scheduleRetry({
95997
+ ctx: input.ctx,
95998
+ sessionID: input.sessionID,
95999
+ sessionState: input.sessionState,
96000
+ options: input.options
96001
+ });
96002
+ return;
96003
+ }
96004
+ input.sessionState.isInjectingContinuation = true;
95898
96005
  try {
95899
96006
  const currentBoulder = readBoulderState(input.ctx.directory);
95900
96007
  const currentTask = currentBoulder ? readCurrentTopLevelTask(currentBoulder.active_plan) : null;
95901
96008
  const preferredTaskSession = currentTask ? getTaskSessionState(input.ctx.directory, currentTask.key) : null;
95902
- await injectBoulderContinuation({
96009
+ if (!currentBoulder) {
96010
+ return;
96011
+ }
96012
+ const canContinueSession = await canContinueTrackedBoulderSession({
96013
+ client: input.ctx.client,
96014
+ sessionID: input.sessionID,
96015
+ sessionOrigin: currentBoulder.session_origins?.[input.sessionID],
96016
+ boulderSessionIDs: currentBoulder.session_ids,
96017
+ requiredAgent: currentBoulder.agent
96018
+ });
96019
+ if (!canContinueSession) {
96020
+ log(`[${HOOK_NAME7}] Skipped: tracked descendant agent does not match boulder agent`, {
96021
+ sessionID: input.sessionID,
96022
+ requiredAgent: currentBoulder.agent ?? "atlas"
96023
+ });
96024
+ return;
96025
+ }
96026
+ const result = await injectBoulderContinuation({
95903
96027
  ctx: input.ctx,
95904
96028
  sessionID: input.sessionID,
95905
96029
  planName: input.planName,
@@ -95912,9 +96036,43 @@ async function injectContinuation2(input) {
95912
96036
  backgroundManager: input.options?.backgroundManager,
95913
96037
  sessionState: input.sessionState
95914
96038
  });
96039
+ if (result === "injected") {
96040
+ if (input.sessionState.pendingRetryTimer) {
96041
+ clearTimeout(input.sessionState.pendingRetryTimer);
96042
+ input.sessionState.pendingRetryTimer = undefined;
96043
+ }
96044
+ input.sessionState.lastContinuationInjectedAt = Date.now();
96045
+ return;
96046
+ }
96047
+ if (result === "skipped_background_tasks") {
96048
+ scheduleRetry({
96049
+ ctx: input.ctx,
96050
+ sessionID: input.sessionID,
96051
+ sessionState: input.sessionState,
96052
+ options: input.options
96053
+ });
96054
+ return;
96055
+ }
96056
+ if (result === "failed") {
96057
+ scheduleRetry({
96058
+ ctx: input.ctx,
96059
+ sessionID: input.sessionID,
96060
+ sessionState: input.sessionState,
96061
+ options: input.options
96062
+ });
96063
+ }
95915
96064
  } catch (error48) {
95916
96065
  log(`[${HOOK_NAME7}] Failed to inject boulder continuation`, { sessionID: input.sessionID, error: error48 });
95917
96066
  input.sessionState.promptFailureCount += 1;
96067
+ input.sessionState.lastFailureAt = Date.now();
96068
+ scheduleRetry({
96069
+ ctx: input.ctx,
96070
+ sessionID: input.sessionID,
96071
+ sessionState: input.sessionState,
96072
+ options: input.options
96073
+ });
96074
+ } finally {
96075
+ input.sessionState.isInjectingContinuation = false;
95918
96076
  }
95919
96077
  }
95920
96078
  function scheduleRetry(input) {
@@ -95928,6 +96086,10 @@ function scheduleRetry(input) {
95928
96086
  return;
95929
96087
  if (sessionState.waitingForFinalWaveApproval)
95930
96088
  return;
96089
+ const now = Date.now();
96090
+ if (sessionState.lastContinuationInjectedAt && now - sessionState.lastContinuationInjectedAt < CONTINUATION_COOLDOWN_MS2) {
96091
+ return;
96092
+ }
95931
96093
  const currentBoulder = readBoulderState(ctx.directory);
95932
96094
  if (!currentBoulder)
95933
96095
  return;
@@ -95938,8 +96100,19 @@ function scheduleRetry(input) {
95938
96100
  return;
95939
96101
  if (options?.isContinuationStopped?.(sessionID))
95940
96102
  return;
95941
- if (hasRunningBackgroundTasks(sessionID, options))
96103
+ const canContinueSession = await canContinueTrackedBoulderSession({
96104
+ client: ctx.client,
96105
+ sessionID,
96106
+ sessionOrigin: currentBoulder.session_origins?.[sessionID],
96107
+ boulderSessionIDs: currentBoulder.session_ids,
96108
+ requiredAgent: currentBoulder.agent
96109
+ });
96110
+ if (!canContinueSession)
96111
+ return;
96112
+ if (hasRunningBackgroundTasks(sessionID, options)) {
96113
+ scheduleRetry({ ctx, sessionID, sessionState, options });
95942
96114
  return;
96115
+ }
95943
96116
  await injectContinuation2({
95944
96117
  ctx,
95945
96118
  sessionID,
@@ -95975,27 +96148,19 @@ async function handleAtlasSessionIdle(input) {
95975
96148
  plan: boulderState.plan_name
95976
96149
  });
95977
96150
  }
95978
- if (subagentSessions.has(sessionID)) {
95979
- const sessionAgent = getSessionAgent(sessionID);
95980
- const agentKey = getAgentConfigKey(sessionAgent ?? "");
95981
- const requiredAgentName = boulderState.agent ?? (isAgentRegistered("atlas") ? "atlas" : undefined);
95982
- if (!requiredAgentName || !isAgentRegistered(requiredAgentName)) {
95983
- log(`[${HOOK_NAME7}] Skipped: boulder agent is unavailable for continuation`, {
95984
- sessionID,
95985
- requiredAgent: boulderState.agent ?? "unknown"
95986
- });
95987
- return;
95988
- }
95989
- const requiredAgentKey = getAgentConfigKey(requiredAgentName);
95990
- const agentMatches = agentKey === requiredAgentKey || requiredAgentKey === getAgentConfigKey("atlas") && agentKey === getAgentConfigKey("sisyphus");
95991
- if (!agentMatches) {
95992
- log(`[${HOOK_NAME7}] Skipped: subagent agent does not match boulder agent`, {
95993
- sessionID,
95994
- agent: sessionAgent ?? "unknown",
95995
- requiredAgent: requiredAgentName
95996
- });
95997
- return;
95998
- }
96151
+ const canContinueSession = await canContinueTrackedBoulderSession({
96152
+ client: ctx.client,
96153
+ sessionID,
96154
+ sessionOrigin: boulderState.session_origins?.[sessionID],
96155
+ boulderSessionIDs: boulderState.session_ids,
96156
+ requiredAgent: boulderState.agent
96157
+ });
96158
+ if (!canContinueSession) {
96159
+ log(`[${HOOK_NAME7}] Skipped: tracked descendant agent does not match boulder agent`, {
96160
+ sessionID,
96161
+ requiredAgent: boulderState.agent ?? "atlas"
96162
+ });
96163
+ return;
95999
96164
  }
96000
96165
  const sessionState = getState(sessionID);
96001
96166
  const now = Date.now();
@@ -96022,6 +96187,7 @@ async function handleAtlasSessionIdle(input) {
96022
96187
  sessionState.lastFailureAt = undefined;
96023
96188
  }
96024
96189
  if (hasRunningBackgroundTasks(sessionID, options)) {
96190
+ scheduleRetry({ ctx, sessionID, sessionState, options });
96025
96191
  log(`[${HOOK_NAME7}] Skipped: background tasks running`, { sessionID });
96026
96192
  return;
96027
96193
  }
@@ -96049,6 +96215,30 @@ async function handleAtlasSessionIdle(input) {
96049
96215
  worktreePath: boulderState.worktree_path
96050
96216
  });
96051
96217
  }
96218
+ async function canContinueTrackedBoulderSession(input) {
96219
+ const ancestorSessionIDs = input.boulderSessionIDs.filter((trackedSessionID) => trackedSessionID !== input.sessionID);
96220
+ if (ancestorSessionIDs.length === 0) {
96221
+ return true;
96222
+ }
96223
+ const isTrackedDescendant = await isSessionInBoulderLineage({
96224
+ client: input.client,
96225
+ sessionID: input.sessionID,
96226
+ boulderSessionIDs: ancestorSessionIDs
96227
+ });
96228
+ if (input.sessionOrigin === "direct") {
96229
+ return true;
96230
+ }
96231
+ if (!isTrackedDescendant) {
96232
+ return false;
96233
+ }
96234
+ const sessionAgent = await getLastAgentFromSession(input.sessionID, input.client) ?? getSessionAgent(input.sessionID);
96235
+ if (!sessionAgent) {
96236
+ return false;
96237
+ }
96238
+ const requiredAgentKey = getAgentConfigKey(input.requiredAgent ?? "atlas");
96239
+ const sessionAgentKey = getAgentConfigKey(sessionAgent);
96240
+ return sessionAgentKey === requiredAgentKey || requiredAgentKey === getAgentConfigKey("atlas") && sessionAgentKey === getAgentConfigKey("sisyphus");
96241
+ }
96052
96242
 
96053
96243
  // src/hooks/atlas/event-handler.ts
96054
96244
  function createAtlasEventHandler(input) {
@@ -96138,8 +96328,141 @@ function createAtlasEventHandler(input) {
96138
96328
  // src/hooks/atlas/tool-execute-after.ts
96139
96329
  init_logger();
96140
96330
 
96331
+ // src/hooks/atlas/background-launch-session-tracking.ts
96332
+ init_logger();
96333
+
96334
+ // src/hooks/atlas/subagent-session-id.ts
96335
+ init_logger();
96336
+ function extractSessionIdFromMetadata(metadata) {
96337
+ if (metadata && typeof metadata === "object" && "sessionId" in metadata) {
96338
+ const value = metadata.sessionId;
96339
+ if (typeof value === "string" && value.startsWith("ses_")) {
96340
+ return value;
96341
+ }
96342
+ }
96343
+ return;
96344
+ }
96345
+ function extractSessionIdFromOutput(output) {
96346
+ const taskMetadataBlocks = [...output.matchAll(/<task_metadata>([\s\S]*?)<\/task_metadata>/gi)];
96347
+ const lastTaskMetadataBlock = taskMetadataBlocks.at(-1)?.[1];
96348
+ if (lastTaskMetadataBlock) {
96349
+ const taskMetadataSessionMatch = lastTaskMetadataBlock.match(/session_id:\s*(ses_[a-zA-Z0-9_-]+)/i);
96350
+ if (taskMetadataSessionMatch) {
96351
+ return taskMetadataSessionMatch[1];
96352
+ }
96353
+ }
96354
+ const explicitSessionMatches = [...output.matchAll(/Session ID:\s*(ses_[a-zA-Z0-9_-]+)/g)];
96355
+ return explicitSessionMatches.at(-1)?.[1];
96356
+ }
96357
+ async function validateSubagentSessionId(input) {
96358
+ if (!input.sessionID || input.lineageSessionIDs.length === 0) {
96359
+ return;
96360
+ }
96361
+ const belongsToLineage = await isSessionInBoulderLineage({
96362
+ client: input.client,
96363
+ sessionID: input.sessionID,
96364
+ boulderSessionIDs: input.lineageSessionIDs
96365
+ });
96366
+ if (!belongsToLineage) {
96367
+ log(`[${HOOK_NAME7}] Ignoring extracted session id outside active lineage`, {
96368
+ sessionID: input.sessionID,
96369
+ lineageSessionIDs: input.lineageSessionIDs
96370
+ });
96371
+ return;
96372
+ }
96373
+ return input.sessionID;
96374
+ }
96375
+
96376
+ // src/hooks/atlas/task-context.ts
96377
+ function resolvePreferredSessionId(currentSessionId, trackedSessionId) {
96378
+ return currentSessionId ?? trackedSessionId ?? "<session_id>";
96379
+ }
96380
+ function resolveTaskContext(pendingTaskRef, planPath) {
96381
+ if (!pendingTaskRef) {
96382
+ return {
96383
+ currentTask: readCurrentTopLevelTask(planPath),
96384
+ shouldSkipTaskSessionUpdate: false,
96385
+ shouldIgnoreCurrentSessionId: false
96386
+ };
96387
+ }
96388
+ if (pendingTaskRef.kind === "track") {
96389
+ return {
96390
+ currentTask: pendingTaskRef.task,
96391
+ shouldSkipTaskSessionUpdate: false,
96392
+ shouldIgnoreCurrentSessionId: false
96393
+ };
96394
+ }
96395
+ if (pendingTaskRef.reason === "explicit_resume") {
96396
+ return {
96397
+ currentTask: readCurrentTopLevelTask(planPath),
96398
+ shouldSkipTaskSessionUpdate: true,
96399
+ shouldIgnoreCurrentSessionId: true
96400
+ };
96401
+ }
96402
+ return {
96403
+ currentTask: pendingTaskRef.task,
96404
+ shouldSkipTaskSessionUpdate: true,
96405
+ shouldIgnoreCurrentSessionId: true
96406
+ };
96407
+ }
96408
+
96409
+ // src/hooks/atlas/background-launch-session-tracking.ts
96410
+ async function syncBackgroundLaunchSessionTracking(input) {
96411
+ const { ctx, boulderState, toolInput, toolOutput, pendingTaskRef, metadataSessionId } = input;
96412
+ if (!boulderState) {
96413
+ return;
96414
+ }
96415
+ const extractedSessionId = metadataSessionId ?? extractSessionIdFromOutput(toolOutput.output);
96416
+ const lineageSessionIDs = boulderState.session_ids;
96417
+ const subagentSessionId = await validateSubagentSessionId({
96418
+ client: ctx.client,
96419
+ sessionID: extractedSessionId,
96420
+ lineageSessionIDs
96421
+ });
96422
+ const trackedSessionId = subagentSessionId ?? await resolveFallbackTrackedSessionId({
96423
+ ctx,
96424
+ extractedSessionId,
96425
+ lineageSessionIDs
96426
+ });
96427
+ if (!trackedSessionId) {
96428
+ return;
96429
+ }
96430
+ appendSessionId(ctx.directory, trackedSessionId, "appended");
96431
+ const { currentTask, shouldSkipTaskSessionUpdate } = resolveTaskContext(pendingTaskRef, boulderState.active_plan);
96432
+ if (currentTask && !shouldSkipTaskSessionUpdate) {
96433
+ upsertTaskSessionState(ctx.directory, {
96434
+ taskKey: currentTask.key,
96435
+ taskLabel: currentTask.label,
96436
+ taskTitle: currentTask.title,
96437
+ sessionId: trackedSessionId,
96438
+ agent: typeof toolOutput.metadata?.agent === "string" ? toolOutput.metadata.agent : undefined,
96439
+ category: typeof toolOutput.metadata?.category === "string" ? toolOutput.metadata.category : undefined
96440
+ });
96441
+ }
96442
+ log(`[${HOOK_NAME7}] Background launch session tracked`, {
96443
+ sessionID: toolInput.sessionID,
96444
+ subagentSessionId: trackedSessionId,
96445
+ taskKey: currentTask?.key
96446
+ });
96447
+ }
96448
+ async function resolveFallbackTrackedSessionId(input) {
96449
+ if (!input.extractedSessionId) {
96450
+ return;
96451
+ }
96452
+ try {
96453
+ const session = await input.ctx.client.session.get({ path: { id: input.extractedSessionId } });
96454
+ const parentSessionId = session.data?.parentID;
96455
+ if (typeof parentSessionId === "string" && input.lineageSessionIDs.includes(parentSessionId)) {
96456
+ return input.extractedSessionId;
96457
+ }
96458
+ return;
96459
+ } catch {
96460
+ return;
96461
+ }
96462
+ }
96463
+
96141
96464
  // src/hooks/atlas/final-wave-plan-state.ts
96142
- import { existsSync as existsSync61, readFileSync as readFileSync44 } from "fs";
96465
+ import { existsSync as existsSync61, readFileSync as readFileSync45 } from "fs";
96143
96466
  var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
96144
96467
  var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
96145
96468
  var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
@@ -96151,7 +96474,7 @@ function readFinalWavePlanState(planPath) {
96151
96474
  return null;
96152
96475
  }
96153
96476
  try {
96154
- const content = readFileSync44(planPath, "utf-8");
96477
+ const content = readFileSync45(planPath, "utf-8");
96155
96478
  const lines = content.split(/\r?\n/);
96156
96479
  let section = "other";
96157
96480
  let pendingImplementationTaskCount = 0;
@@ -96220,48 +96543,6 @@ function isSisyphusPath(filePath) {
96220
96543
  return /\.sisyphus[/\\]/.test(filePath);
96221
96544
  }
96222
96545
 
96223
- // src/hooks/atlas/subagent-session-id.ts
96224
- init_logger();
96225
- function extractSessionIdFromMetadata(metadata) {
96226
- if (metadata && typeof metadata === "object" && "sessionId" in metadata) {
96227
- const value = metadata.sessionId;
96228
- if (typeof value === "string" && value.startsWith("ses_")) {
96229
- return value;
96230
- }
96231
- }
96232
- return;
96233
- }
96234
- function extractSessionIdFromOutput(output) {
96235
- const taskMetadataBlocks = [...output.matchAll(/<task_metadata>([\s\S]*?)<\/task_metadata>/gi)];
96236
- const lastTaskMetadataBlock = taskMetadataBlocks.at(-1)?.[1];
96237
- if (lastTaskMetadataBlock) {
96238
- const taskMetadataSessionMatch = lastTaskMetadataBlock.match(/session_id:\s*(ses_[a-zA-Z0-9_-]+)/i);
96239
- if (taskMetadataSessionMatch) {
96240
- return taskMetadataSessionMatch[1];
96241
- }
96242
- }
96243
- const explicitSessionMatches = [...output.matchAll(/Session ID:\s*(ses_[a-zA-Z0-9_-]+)/g)];
96244
- return explicitSessionMatches.at(-1)?.[1];
96245
- }
96246
- async function validateSubagentSessionId(input) {
96247
- if (!input.sessionID || input.lineageSessionIDs.length === 0) {
96248
- return;
96249
- }
96250
- const belongsToLineage = await isSessionInBoulderLineage({
96251
- client: input.client,
96252
- sessionID: input.sessionID,
96253
- boulderSessionIDs: input.lineageSessionIDs
96254
- });
96255
- if (!belongsToLineage) {
96256
- log(`[${HOOK_NAME7}] Ignoring extracted session id outside active lineage`, {
96257
- sessionID: input.sessionID,
96258
- lineageSessionIDs: input.lineageSessionIDs
96259
- });
96260
- return;
96261
- }
96262
- return input.sessionID;
96263
- }
96264
-
96265
96546
  // src/hooks/atlas/verification-reminders.ts
96266
96547
  function buildReuseHint(sessionId) {
96267
96548
  return `
@@ -96442,37 +96723,6 @@ function isWriteOrEditToolName(toolName) {
96442
96723
  }
96443
96724
 
96444
96725
  // src/hooks/atlas/tool-execute-after.ts
96445
- function resolvePreferredSessionId(currentSessionId, trackedSessionId) {
96446
- return currentSessionId ?? trackedSessionId ?? "<session_id>";
96447
- }
96448
- function resolveTaskContext(pendingTaskRef, planPath) {
96449
- if (!pendingTaskRef) {
96450
- return {
96451
- currentTask: readCurrentTopLevelTask(planPath),
96452
- shouldSkipTaskSessionUpdate: false,
96453
- shouldIgnoreCurrentSessionId: false
96454
- };
96455
- }
96456
- if (pendingTaskRef.kind === "track") {
96457
- return {
96458
- currentTask: pendingTaskRef.task,
96459
- shouldSkipTaskSessionUpdate: false,
96460
- shouldIgnoreCurrentSessionId: false
96461
- };
96462
- }
96463
- if (pendingTaskRef.reason === "explicit_resume") {
96464
- return {
96465
- currentTask: readCurrentTopLevelTask(planPath),
96466
- shouldSkipTaskSessionUpdate: true,
96467
- shouldIgnoreCurrentSessionId: true
96468
- };
96469
- }
96470
- return {
96471
- currentTask: pendingTaskRef.task,
96472
- shouldSkipTaskSessionUpdate: true,
96473
- shouldIgnoreCurrentSessionId: true
96474
- };
96475
- }
96476
96726
  function createToolExecuteAfterHandler2(input) {
96477
96727
  const { ctx, pendingFilePaths, pendingTaskRefs, autoCommit, getState } = input;
96478
96728
  return async (toolInput, toolOutput) => {
@@ -96510,12 +96760,20 @@ function createToolExecuteAfterHandler2(input) {
96510
96760
  if (toolInput.callID) {
96511
96761
  pendingTaskRefs.delete(toolInput.callID);
96512
96762
  }
96763
+ const boulderState = readBoulderState(ctx.directory);
96513
96764
  const isBackgroundLaunch = outputStr.includes("Background task launched") || outputStr.includes("Background task continued") || outputStr.includes("Background delegate launched") || outputStr.includes("Background agent task launched");
96514
96765
  if (isBackgroundLaunch) {
96766
+ await syncBackgroundLaunchSessionTracking({
96767
+ ctx,
96768
+ boulderState,
96769
+ toolInput,
96770
+ toolOutput,
96771
+ pendingTaskRef,
96772
+ metadataSessionId
96773
+ });
96515
96774
  return;
96516
96775
  }
96517
96776
  if (toolOutput.output && typeof toolOutput.output === "string") {
96518
- const boulderState = readBoulderState(ctx.directory);
96519
96777
  const worktreePath = boulderState?.worktree_path?.trim();
96520
96778
  const verificationDirectory = worktreePath ? worktreePath : ctx.directory;
96521
96779
  const gitStats = collectGitDiffStats(verificationDirectory);
@@ -96530,14 +96788,7 @@ function createToolExecuteAfterHandler2(input) {
96530
96788
  } = resolveTaskContext(pendingTaskRef, boulderState.active_plan);
96531
96789
  const trackedTaskSession = currentTask ? getTaskSessionState(ctx.directory, currentTask.key) : null;
96532
96790
  const sessionState = toolInput.sessionID ? getState(toolInput.sessionID) : undefined;
96533
- if (toolInput.sessionID && !boulderState.session_ids?.includes(toolInput.sessionID)) {
96534
- appendSessionId(ctx.directory, toolInput.sessionID);
96535
- log(`[${HOOK_NAME7}] Appended session to boulder`, {
96536
- sessionID: toolInput.sessionID,
96537
- plan: boulderState.plan_name
96538
- });
96539
- }
96540
- const lineageSessionIDs = toolInput.sessionID && !boulderState.session_ids.includes(toolInput.sessionID) ? [...boulderState.session_ids, toolInput.sessionID] : boulderState.session_ids;
96791
+ const lineageSessionIDs = boulderState.session_ids;
96541
96792
  const subagentSessionId = await validateSubagentSessionId({
96542
96793
  client: ctx.client,
96543
96794
  sessionID: extractedSessionId,
@@ -97030,7 +97281,7 @@ function clearSessionModel(sessionID) {
97030
97281
  }
97031
97282
 
97032
97283
  // src/hooks/compaction-context-injector/session-id.ts
97033
- function isCompactionAgent2(agent) {
97284
+ function isCompactionAgent3(agent) {
97034
97285
  return agent?.trim().toLowerCase() === "compaction";
97035
97286
  }
97036
97287
  function resolveSessionID(props) {
@@ -97039,7 +97290,7 @@ function resolveSessionID(props) {
97039
97290
 
97040
97291
  // src/hooks/compaction-context-injector/validated-model.ts
97041
97292
  function resolveValidatedModel(info) {
97042
- if (isCompactionAgent2(info?.agent)) {
97293
+ if (isCompactionAgent3(info?.agent)) {
97043
97294
  return;
97044
97295
  }
97045
97296
  const providerID = info?.model?.providerID ?? info?.providerID;
@@ -97073,7 +97324,7 @@ async function resolveSessionPromptConfig(ctx, sessionID) {
97073
97324
  });
97074
97325
  for (let index = messages.length - 1;index >= 0; index--) {
97075
97326
  const info = messages[index].info;
97076
- if (!promptConfig.agent && info?.agent && !isCompactionAgent2(info.agent)) {
97327
+ if (!promptConfig.agent && info?.agent && !isCompactionAgent3(info.agent)) {
97077
97328
  promptConfig.agent = info.agent;
97078
97329
  }
97079
97330
  if (!promptConfig.model) {
@@ -97164,7 +97415,7 @@ function trackAssistantOutput(state3, messageID) {
97164
97415
  init_logger();
97165
97416
 
97166
97417
  // src/hooks/compaction-context-injector/recovery-prompt-config.ts
97167
- function isCompactionAgent3(agent) {
97418
+ function isCompactionAgent4(agent) {
97168
97419
  return agent?.trim().toLowerCase() === "compaction";
97169
97420
  }
97170
97421
  function matchesExpectedModel(actualModel, expectedModel) {
@@ -97197,7 +97448,7 @@ function createExpectedRecoveryPromptConfig(checkpoint, currentPromptConfig) {
97197
97448
  }
97198
97449
  function isPromptConfigRecovered(actualPromptConfig, expectedPromptConfig) {
97199
97450
  const actualAgent = actualPromptConfig.agent;
97200
- const agentMatches = typeof actualAgent === "string" && !isCompactionAgent3(actualAgent) && actualAgent.toLowerCase() === expectedPromptConfig.agent.toLowerCase();
97451
+ const agentMatches = typeof actualAgent === "string" && !isCompactionAgent4(actualAgent) && actualAgent.toLowerCase() === expectedPromptConfig.agent.toLowerCase();
97201
97452
  return agentMatches && matchesExpectedModel(actualPromptConfig.model, expectedPromptConfig.model) && matchesExpectedTools(actualPromptConfig.tools, expectedPromptConfig.tools);
97202
97453
  }
97203
97454
 
@@ -98151,12 +98402,12 @@ import * as fs18 from "fs";
98151
98402
  import * as path11 from "path";
98152
98403
  // src/shared/migrate-legacy-config-file.ts
98153
98404
  init_logger();
98154
- import { existsSync as existsSync62, readFileSync as readFileSync45, renameSync as renameSync4, rmSync as rmSync3 } from "fs";
98155
- import { join as join68, dirname as dirname20, basename as basename9 } from "path";
98405
+ import { existsSync as existsSync62, readFileSync as readFileSync46, renameSync as renameSync4, rmSync as rmSync3 } from "fs";
98406
+ import { join as join69, dirname as dirname20, basename as basename9 } from "path";
98156
98407
  function buildCanonicalPath(legacyPath) {
98157
98408
  const dir = dirname20(legacyPath);
98158
98409
  const ext = basename9(legacyPath).includes(".jsonc") ? ".jsonc" : ".json";
98159
- return join68(dir, `${CONFIG_BASENAME}${ext}`);
98410
+ return join69(dir, `${CONFIG_BASENAME}${ext}`);
98160
98411
  }
98161
98412
  function archiveLegacyConfigFile(legacyPath) {
98162
98413
  const backupPath = `${legacyPath}.bak`;
@@ -98196,7 +98447,7 @@ function migrateLegacyConfigFile(legacyPath) {
98196
98447
  if (existsSync62(canonicalPath))
98197
98448
  return false;
98198
98449
  try {
98199
- const content = readFileSync45(legacyPath, "utf-8");
98450
+ const content = readFileSync46(legacyPath, "utf-8");
98200
98451
  writeFileAtomically(canonicalPath, content);
98201
98452
  const archivedLegacyConfig = archiveLegacyConfigFile(legacyPath);
98202
98453
  log("[migrateLegacyConfigFile] Migrated legacy config to canonical path", {
@@ -99679,7 +99930,7 @@ function createRuntimeFallbackHook(ctx, options) {
99679
99930
  }
99680
99931
  // src/hooks/write-existing-file-guard/hook.ts
99681
99932
  import { existsSync as existsSync65, realpathSync as realpathSync7 } from "fs";
99682
- import { basename as basename11, dirname as dirname22, isAbsolute as isAbsolute10, join as join70, normalize as normalize2, relative as relative8, resolve as resolve12 } from "path";
99933
+ import { basename as basename11, dirname as dirname22, isAbsolute as isAbsolute10, join as join71, normalize as normalize2, relative as relative8, resolve as resolve12 } from "path";
99683
99934
 
99684
99935
  // src/hooks/write-existing-file-guard/tool-execute-before-handler.ts
99685
99936
  import { existsSync as existsSync64 } from "fs";
@@ -99854,7 +100105,7 @@ function toCanonicalPath2(absolutePath) {
99854
100105
  } else {
99855
100106
  const absoluteDir = dirname22(absolutePath);
99856
100107
  const resolvedDir = existsSync65(absoluteDir) ? realpathSync7.native(absoluteDir) : absoluteDir;
99857
- canonicalPath = join70(resolvedDir, basename11(absolutePath));
100108
+ canonicalPath = join71(resolvedDir, basename11(absolutePath));
99858
100109
  }
99859
100110
  return normalize2(canonicalPath);
99860
100111
  }
@@ -100869,12 +101120,12 @@ function createWebFetchRedirectGuardHook(_ctx) {
100869
101120
  init_logger();
100870
101121
 
100871
101122
  // src/hooks/legacy-plugin-toast/auto-migrate.ts
100872
- import { existsSync as existsSync66, readFileSync as readFileSync47 } from "fs";
100873
- import { join as join71 } from "path";
101123
+ import { existsSync as existsSync66, readFileSync as readFileSync48 } from "fs";
101124
+ import { join as join72 } from "path";
100874
101125
  function detectOpenCodeConfigPath(overrideConfigDir) {
100875
101126
  if (overrideConfigDir) {
100876
- const jsoncPath = join71(overrideConfigDir, "opencode.jsonc");
100877
- const jsonPath = join71(overrideConfigDir, "opencode.json");
101127
+ const jsoncPath = join72(overrideConfigDir, "opencode.jsonc");
101128
+ const jsonPath = join72(overrideConfigDir, "opencode.json");
100878
101129
  if (existsSync66(jsoncPath))
100879
101130
  return jsoncPath;
100880
101131
  if (existsSync66(jsonPath))
@@ -100893,7 +101144,7 @@ function autoMigrateLegacyPluginEntry(overrideConfigDir) {
100893
101144
  if (!configPath)
100894
101145
  return { migrated: false, from: null, to: null, configPath: null };
100895
101146
  try {
100896
- const content = readFileSync47(configPath, "utf-8");
101147
+ const content = readFileSync48(configPath, "utf-8");
100897
101148
  const parseResult = parseJsoncSafe(content);
100898
101149
  if (!parseResult.data?.plugin)
100899
101150
  return { migrated: false, from: null, to: null, configPath };
@@ -101287,13 +101538,13 @@ var DEFAULT_MAX_SYMBOLS = 200;
101287
101538
  var DEFAULT_MAX_DIAGNOSTICS = 200;
101288
101539
  var DEFAULT_MAX_DIRECTORY_FILES = 50;
101289
101540
  // src/tools/lsp/server-config-loader.ts
101290
- import { existsSync as existsSync67, readFileSync as readFileSync48 } from "fs";
101291
- import { join as join72 } from "path";
101541
+ import { existsSync as existsSync67, readFileSync as readFileSync49 } from "fs";
101542
+ import { join as join73 } from "path";
101292
101543
  function loadJsonFile(path12) {
101293
101544
  if (!existsSync67(path12))
101294
101545
  return null;
101295
101546
  try {
101296
- return parseJsonc(readFileSync48(path12, "utf-8"));
101547
+ return parseJsonc(readFileSync49(path12, "utf-8"));
101297
101548
  } catch {
101298
101549
  return null;
101299
101550
  }
@@ -101302,9 +101553,9 @@ function getConfigPaths3() {
101302
101553
  const cwd = process.cwd();
101303
101554
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
101304
101555
  return {
101305
- project: detectPluginConfigFile(join72(cwd, ".opencode")).path,
101556
+ project: detectPluginConfigFile(join73(cwd, ".opencode")).path,
101306
101557
  user: detectPluginConfigFile(configDir).path,
101307
- opencode: detectConfigFile(join72(configDir, "opencode")).path
101558
+ opencode: detectConfigFile(join73(configDir, "opencode")).path
101308
101559
  };
101309
101560
  }
101310
101561
  function loadAllConfigs() {
@@ -101374,19 +101625,19 @@ function getMergedServers() {
101374
101625
 
101375
101626
  // src/tools/lsp/server-installation.ts
101376
101627
  import { existsSync as existsSync68 } from "fs";
101377
- import { delimiter, join as join74 } from "path";
101628
+ import { delimiter, join as join75 } from "path";
101378
101629
 
101379
101630
  // src/tools/lsp/server-path-bases.ts
101380
- import { join as join73 } from "path";
101631
+ import { join as join74 } from "path";
101381
101632
  function getLspServerAdditionalPathBases(workingDirectory) {
101382
101633
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
101383
- const dataDir = join73(getDataDir(), "opencode");
101634
+ const dataDir = join74(getDataDir(), "opencode");
101384
101635
  return [
101385
- join73(workingDirectory, "node_modules", ".bin"),
101386
- join73(configDir, "bin"),
101387
- join73(configDir, "node_modules", ".bin"),
101388
- join73(dataDir, "bin"),
101389
- join73(dataDir, "bin", "node_modules", ".bin")
101636
+ join74(workingDirectory, "node_modules", ".bin"),
101637
+ join74(configDir, "bin"),
101638
+ join74(configDir, "node_modules", ".bin"),
101639
+ join74(dataDir, "bin"),
101640
+ join74(dataDir, "bin", "node_modules", ".bin")
101390
101641
  ];
101391
101642
  }
101392
101643
 
@@ -101417,14 +101668,14 @@ function isServerInstalled(command) {
101417
101668
  const paths = pathEnv.split(delimiter);
101418
101669
  for (const p of paths) {
101419
101670
  for (const suffix of exts) {
101420
- if (existsSync68(join74(p, cmd + suffix))) {
101671
+ if (existsSync68(join75(p, cmd + suffix))) {
101421
101672
  return true;
101422
101673
  }
101423
101674
  }
101424
101675
  }
101425
101676
  for (const base of getLspServerAdditionalPathBases(process.cwd())) {
101426
101677
  for (const suffix of exts) {
101427
- if (existsSync68(join74(base, cmd + suffix))) {
101678
+ if (existsSync68(join75(base, cmd + suffix))) {
101428
101679
  return true;
101429
101680
  }
101430
101681
  }
@@ -101620,7 +101871,7 @@ function spawnProcess(command, options) {
101620
101871
  return proc;
101621
101872
  }
101622
101873
  // src/tools/lsp/lsp-client.ts
101623
- import { readFileSync as readFileSync49 } from "fs";
101874
+ import { readFileSync as readFileSync50 } from "fs";
101624
101875
  import { extname as extname4, resolve as resolve13 } from "path";
101625
101876
  import { pathToFileURL as pathToFileURL2 } from "url";
101626
101877
 
@@ -101892,7 +102143,7 @@ class LSPClient extends LSPClientConnection {
101892
102143
  async openFile(filePath) {
101893
102144
  const absPath = resolve13(filePath);
101894
102145
  const uri = pathToFileURL2(absPath).href;
101895
- const text = readFileSync49(absPath, "utf-8");
102146
+ const text = readFileSync50(absPath, "utf-8");
101896
102147
  if (!this.openedFiles.has(absPath)) {
101897
102148
  const ext = extname4(absPath);
101898
102149
  const languageId = getLanguageId(ext);
@@ -102444,10 +102695,10 @@ function formatApplyResult(result) {
102444
102695
  `);
102445
102696
  }
102446
102697
  // src/tools/lsp/workspace-edit.ts
102447
- import { readFileSync as readFileSync50, writeFileSync as writeFileSync19 } from "fs";
102698
+ import { readFileSync as readFileSync51, writeFileSync as writeFileSync19 } from "fs";
102448
102699
  function applyTextEditsToFile(filePath, edits) {
102449
102700
  try {
102450
- let content = readFileSync50(filePath, "utf-8");
102701
+ let content = readFileSync51(filePath, "utf-8");
102451
102702
  const lines = content.split(`
102452
102703
  `);
102453
102704
  const sortedEdits = [...edits].sort((a, b) => {
@@ -102513,7 +102764,7 @@ function applyWorkspaceEdit(edit) {
102513
102764
  try {
102514
102765
  const oldPath = uriToPath(change.oldUri);
102515
102766
  const newPath = uriToPath(change.newUri);
102516
- const content = readFileSync50(oldPath, "utf-8");
102767
+ const content = readFileSync51(oldPath, "utf-8");
102517
102768
  writeFileSync19(newPath, content, "utf-8");
102518
102769
  __require("fs").unlinkSync(oldPath);
102519
102770
  result.filesModified.push(newPath);
@@ -114996,8 +115247,8 @@ var lsp_symbols = tool({
114996
115247
  import { resolve as resolve16 } from "path";
114997
115248
 
114998
115249
  // src/tools/lsp/directory-diagnostics.ts
114999
- import { existsSync as existsSync71, lstatSync as lstatSync2, readdirSync as readdirSync17 } from "fs";
115000
- import { extname as extname6, join as join75, resolve as resolve15 } from "path";
115250
+ import { existsSync as existsSync71, lstatSync as lstatSync2, readdirSync as readdirSync18 } from "fs";
115251
+ import { extname as extname6, join as join76, resolve as resolve15 } from "path";
115001
115252
  var SKIP_DIRECTORIES = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
115002
115253
  function collectFilesWithExtension(dir, extension, maxFiles) {
115003
115254
  const files = [];
@@ -115006,14 +115257,14 @@ function collectFilesWithExtension(dir, extension, maxFiles) {
115006
115257
  return;
115007
115258
  let entries = [];
115008
115259
  try {
115009
- entries = readdirSync17(currentDir);
115260
+ entries = readdirSync18(currentDir);
115010
115261
  } catch {
115011
115262
  return;
115012
115263
  }
115013
115264
  for (const entry of entries) {
115014
115265
  if (files.length >= maxFiles)
115015
115266
  return;
115016
- const fullPath = join75(currentDir, entry);
115267
+ const fullPath = join76(currentDir, entry);
115017
115268
  let stat;
115018
115269
  try {
115019
115270
  stat = lstatSync2(fullPath);
@@ -115115,8 +115366,8 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
115115
115366
  }
115116
115367
 
115117
115368
  // src/tools/lsp/infer-extension.ts
115118
- import { readdirSync as readdirSync18, lstatSync as lstatSync3 } from "fs";
115119
- import { extname as extname7, join as join76 } from "path";
115369
+ import { readdirSync as readdirSync19, lstatSync as lstatSync3 } from "fs";
115370
+ import { extname as extname7, join as join77 } from "path";
115120
115371
  var SKIP_DIRECTORIES2 = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
115121
115372
  var MAX_SCAN_ENTRIES = 500;
115122
115373
  function inferExtensionFromDirectory(directory) {
@@ -115127,14 +115378,14 @@ function inferExtensionFromDirectory(directory) {
115127
115378
  return;
115128
115379
  let entries;
115129
115380
  try {
115130
- entries = readdirSync18(dir);
115381
+ entries = readdirSync19(dir);
115131
115382
  } catch {
115132
115383
  return;
115133
115384
  }
115134
115385
  for (const entry of entries) {
115135
115386
  if (scanned >= MAX_SCAN_ENTRIES)
115136
115387
  return;
115137
- const fullPath = join76(dir, entry);
115388
+ const fullPath = join77(dir, entry);
115138
115389
  let stat;
115139
115390
  try {
115140
115391
  stat = lstatSync3(fullPath);
@@ -115299,12 +115550,12 @@ var DEFAULT_MAX_MATCHES = 500;
115299
115550
 
115300
115551
  // src/tools/ast-grep/sg-cli-path.ts
115301
115552
  import { createRequire as createRequire4 } from "module";
115302
- import { dirname as dirname23, join as join78 } from "path";
115553
+ import { dirname as dirname23, join as join79 } from "path";
115303
115554
  import { existsSync as existsSync73, statSync as statSync10 } from "fs";
115304
115555
 
115305
115556
  // src/tools/ast-grep/downloader.ts
115306
115557
  import { existsSync as existsSync72 } from "fs";
115307
- import { join as join77 } from "path";
115558
+ import { join as join78 } from "path";
115308
115559
  import { homedir as homedir14 } from "os";
115309
115560
  import { createRequire as createRequire3 } from "module";
115310
115561
  init_logger();
@@ -115331,12 +115582,12 @@ var PLATFORM_MAP2 = {
115331
115582
  function getCacheDir3() {
115332
115583
  if (process.platform === "win32") {
115333
115584
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
115334
- const base2 = localAppData || join77(homedir14(), "AppData", "Local");
115335
- return join77(base2, "oh-my-opencode", "bin");
115585
+ const base2 = localAppData || join78(homedir14(), "AppData", "Local");
115586
+ return join78(base2, "oh-my-opencode", "bin");
115336
115587
  }
115337
115588
  const xdgCache = process.env.XDG_CACHE_HOME;
115338
- const base = xdgCache || join77(homedir14(), ".cache");
115339
- return join77(base, "oh-my-opencode", "bin");
115589
+ const base = xdgCache || join78(homedir14(), ".cache");
115590
+ return join78(base, "oh-my-opencode", "bin");
115340
115591
  }
115341
115592
  function getBinaryName3() {
115342
115593
  return process.platform === "win32" ? "sg.exe" : "sg";
@@ -115353,7 +115604,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
115353
115604
  }
115354
115605
  const cacheDir = getCacheDir3();
115355
115606
  const binaryName = getBinaryName3();
115356
- const binaryPath = join77(cacheDir, binaryName);
115607
+ const binaryPath = join78(cacheDir, binaryName);
115357
115608
  if (existsSync72(binaryPath)) {
115358
115609
  return binaryPath;
115359
115610
  }
@@ -115362,7 +115613,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
115362
115613
  const downloadUrl = `https://github.com/${REPO2}/releases/download/${version3}/${assetName}`;
115363
115614
  log(`[oh-my-opencode] Downloading ast-grep binary...`);
115364
115615
  try {
115365
- const archivePath = join77(cacheDir, assetName);
115616
+ const archivePath = join78(cacheDir, assetName);
115366
115617
  ensureCacheDir(cacheDir);
115367
115618
  await downloadArchive(downloadUrl, archivePath);
115368
115619
  await extractZipArchive(archivePath, cacheDir);
@@ -115416,7 +115667,7 @@ function findSgCliPathSync() {
115416
115667
  const require2 = createRequire4(import.meta.url);
115417
115668
  const cliPackageJsonPath = require2.resolve("@ast-grep/cli/package.json");
115418
115669
  const cliDirectory = dirname23(cliPackageJsonPath);
115419
- const sgPath = join78(cliDirectory, binaryName);
115670
+ const sgPath = join79(cliDirectory, binaryName);
115420
115671
  if (existsSync73(sgPath) && isValidBinary(sgPath)) {
115421
115672
  return sgPath;
115422
115673
  }
@@ -115428,7 +115679,7 @@ function findSgCliPathSync() {
115428
115679
  const packageJsonPath = require2.resolve(`${platformPackage}/package.json`);
115429
115680
  const packageDirectory = dirname23(packageJsonPath);
115430
115681
  const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
115431
- const binaryPath = join78(packageDirectory, astGrepBinaryName);
115682
+ const binaryPath = join79(packageDirectory, astGrepBinaryName);
115432
115683
  if (existsSync73(binaryPath) && isValidBinary(binaryPath)) {
115433
115684
  return binaryPath;
115434
115685
  }
@@ -115830,18 +116081,18 @@ var {spawn: spawn18 } = globalThis.Bun;
115830
116081
 
115831
116082
  // src/tools/grep/constants.ts
115832
116083
  import { existsSync as existsSync77 } from "fs";
115833
- import { join as join80, dirname as dirname24 } from "path";
116084
+ import { join as join81, dirname as dirname24 } from "path";
115834
116085
  import { spawnSync as spawnSync4 } from "child_process";
115835
116086
 
115836
116087
  // src/tools/grep/downloader.ts
115837
- import { existsSync as existsSync76, readdirSync as readdirSync19 } from "fs";
115838
- import { join as join79 } from "path";
116088
+ import { existsSync as existsSync76, readdirSync as readdirSync20 } from "fs";
116089
+ import { join as join80 } from "path";
115839
116090
  function findFileRecursive(dir, filename) {
115840
116091
  try {
115841
- const entries = readdirSync19(dir, { withFileTypes: true, recursive: true });
116092
+ const entries = readdirSync20(dir, { withFileTypes: true, recursive: true });
115842
116093
  for (const entry of entries) {
115843
116094
  if (entry.isFile() && entry.name === filename) {
115844
- return join79(entry.parentPath ?? dir, entry.name);
116095
+ return join80(entry.parentPath ?? dir, entry.name);
115845
116096
  }
115846
116097
  }
115847
116098
  } catch {
@@ -115862,11 +116113,11 @@ function getPlatformKey() {
115862
116113
  }
115863
116114
  function getInstallDir() {
115864
116115
  const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
115865
- return join79(homeDir, ".cache", "oh-my-opencode", "bin");
116116
+ return join80(homeDir, ".cache", "oh-my-opencode", "bin");
115866
116117
  }
115867
116118
  function getRgPath() {
115868
116119
  const isWindows2 = process.platform === "win32";
115869
- return join79(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
116120
+ return join80(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
115870
116121
  }
115871
116122
  async function extractTarGz2(archivePath, destDir) {
115872
116123
  const platformKey = getPlatformKey();
@@ -115883,7 +116134,7 @@ async function extractZip2(archivePath, destDir) {
115883
116134
  const binaryName = process.platform === "win32" ? "rg.exe" : "rg";
115884
116135
  const foundPath = findFileRecursive(destDir, binaryName);
115885
116136
  if (foundPath) {
115886
- const destPath = join79(destDir, binaryName);
116137
+ const destPath = join80(destDir, binaryName);
115887
116138
  if (foundPath !== destPath) {
115888
116139
  const { renameSync: renameSync5 } = await import("fs");
115889
116140
  renameSync5(foundPath, destPath);
@@ -115904,7 +116155,7 @@ async function downloadAndInstallRipgrep() {
115904
116155
  ensureCacheDir(installDir);
115905
116156
  const filename = `ripgrep-${RG_VERSION}-${config4.platform}.${config4.extension}`;
115906
116157
  const url3 = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${filename}`;
115907
- const archivePath = join79(installDir, filename);
116158
+ const archivePath = join80(installDir, filename);
115908
116159
  try {
115909
116160
  await downloadArchive(url3, archivePath);
115910
116161
  if (config4.extension === "tar.gz") {
@@ -115950,11 +116201,11 @@ function getOpenCodeBundledRg() {
115950
116201
  const isWindows2 = process.platform === "win32";
115951
116202
  const rgName = isWindows2 ? "rg.exe" : "rg";
115952
116203
  const candidates = [
115953
- join80(getDataDir(), "opencode", "bin", rgName),
115954
- join80(execDir, rgName),
115955
- join80(execDir, "bin", rgName),
115956
- join80(execDir, "..", "bin", rgName),
115957
- join80(execDir, "..", "libexec", rgName)
116204
+ join81(getDataDir(), "opencode", "bin", rgName),
116205
+ join81(execDir, rgName),
116206
+ join81(execDir, "bin", rgName),
116207
+ join81(execDir, "..", "bin", rgName),
116208
+ join81(execDir, "..", "libexec", rgName)
115958
116209
  ];
115959
116210
  for (const candidate of candidates) {
115960
116211
  if (existsSync77(candidate)) {
@@ -117066,9 +117317,9 @@ function createSkillTool(options = {}) {
117066
117317
  }
117067
117318
  var skill = createSkillTool();
117068
117319
  // src/tools/session-manager/constants.ts
117069
- import { join as join81 } from "path";
117070
- var TODO_DIR2 = join81(getClaudeConfigDir(), "todos");
117071
- var TRANSCRIPT_DIR2 = join81(getClaudeConfigDir(), "transcripts");
117320
+ import { join as join82 } from "path";
117321
+ var TODO_DIR2 = join82(getClaudeConfigDir(), "todos");
117322
+ var TRANSCRIPT_DIR2 = join82(getClaudeConfigDir(), "transcripts");
117072
117323
  var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
117073
117324
 
117074
117325
  Returns a list of available session IDs with metadata including message count, date range, and agents used.
@@ -117143,7 +117394,7 @@ Has Transcript: Yes (234 entries)`;
117143
117394
  // src/tools/session-manager/file-storage.ts
117144
117395
  import { existsSync as existsSync78 } from "fs";
117145
117396
  import { readdir, readFile } from "fs/promises";
117146
- import { join as join82 } from "path";
117397
+ import { join as join83 } from "path";
117147
117398
  async function getFileMainSessions(directory) {
117148
117399
  if (!existsSync78(SESSION_STORAGE))
117149
117400
  return [];
@@ -117153,13 +117404,13 @@ async function getFileMainSessions(directory) {
117153
117404
  for (const projectDir of projectDirs) {
117154
117405
  if (!projectDir.isDirectory())
117155
117406
  continue;
117156
- const projectPath = join82(SESSION_STORAGE, projectDir.name);
117407
+ const projectPath = join83(SESSION_STORAGE, projectDir.name);
117157
117408
  const sessionFiles = await readdir(projectPath);
117158
117409
  for (const file3 of sessionFiles) {
117159
117410
  if (!file3.endsWith(".json"))
117160
117411
  continue;
117161
117412
  try {
117162
- const content = await readFile(join82(projectPath, file3), "utf-8");
117413
+ const content = await readFile(join83(projectPath, file3), "utf-8");
117163
117414
  const meta3 = JSON.parse(content);
117164
117415
  if (meta3.parentID)
117165
117416
  continue;
@@ -117186,7 +117437,7 @@ async function getFileAllSessions() {
117186
117437
  for (const entry of entries) {
117187
117438
  if (!entry.isDirectory())
117188
117439
  continue;
117189
- const sessionPath = join82(dir, entry.name);
117440
+ const sessionPath = join83(dir, entry.name);
117190
117441
  const files = await readdir(sessionPath);
117191
117442
  if (files.some((file3) => file3.endsWith(".json"))) {
117192
117443
  sessions.push(entry.name);
@@ -117215,7 +117466,7 @@ async function getFileSessionMessages(sessionID) {
117215
117466
  if (!file3.endsWith(".json"))
117216
117467
  continue;
117217
117468
  try {
117218
- const content = await readFile(join82(messageDir, file3), "utf-8");
117469
+ const content = await readFile(join83(messageDir, file3), "utf-8");
117219
117470
  const meta3 = JSON.parse(content);
117220
117471
  const parts = await readParts2(meta3.id);
117221
117472
  messages.push({
@@ -117241,7 +117492,7 @@ async function getFileSessionMessages(sessionID) {
117241
117492
  });
117242
117493
  }
117243
117494
  async function readParts2(messageID) {
117244
- const partDir = join82(PART_STORAGE, messageID);
117495
+ const partDir = join83(PART_STORAGE, messageID);
117245
117496
  if (!existsSync78(partDir))
117246
117497
  return [];
117247
117498
  const parts = [];
@@ -117251,7 +117502,7 @@ async function readParts2(messageID) {
117251
117502
  if (!file3.endsWith(".json"))
117252
117503
  continue;
117253
117504
  try {
117254
- const content = await readFile(join82(partDir, file3), "utf-8");
117505
+ const content = await readFile(join83(partDir, file3), "utf-8");
117255
117506
  parts.push(JSON.parse(content));
117256
117507
  } catch {
117257
117508
  continue;
@@ -117270,7 +117521,7 @@ async function getFileSessionTodos(sessionID) {
117270
117521
  const todoFiles = allFiles.filter((file3) => file3 === `${sessionID}.json`);
117271
117522
  for (const file3 of todoFiles) {
117272
117523
  try {
117273
- const content = await readFile(join82(TODO_DIR2, file3), "utf-8");
117524
+ const content = await readFile(join83(TODO_DIR2, file3), "utf-8");
117274
117525
  const data = JSON.parse(content);
117275
117526
  if (!Array.isArray(data))
117276
117527
  continue;
@@ -117292,7 +117543,7 @@ async function getFileSessionTodos(sessionID) {
117292
117543
  async function getFileSessionTranscript(sessionID) {
117293
117544
  if (!existsSync78(TRANSCRIPT_DIR2))
117294
117545
  return 0;
117295
- const transcriptFile = join82(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
117546
+ const transcriptFile = join83(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
117296
117547
  if (!existsSync78(transcriptFile))
117297
117548
  return 0;
117298
117549
  try {
@@ -119567,9 +119818,9 @@ async function resolveMultimodalLookerAgentMetadata(ctx) {
119567
119818
 
119568
119819
  // src/tools/look-at/image-converter.ts
119569
119820
  import * as childProcess from "child_process";
119570
- import { existsSync as existsSync79, mkdtempSync, readFileSync as readFileSync51, rmSync as rmSync4, unlinkSync as unlinkSync11, writeFileSync as writeFileSync20 } from "fs";
119821
+ import { existsSync as existsSync79, mkdtempSync, readFileSync as readFileSync52, rmSync as rmSync4, unlinkSync as unlinkSync11, writeFileSync as writeFileSync20 } from "fs";
119571
119822
  import { tmpdir as tmpdir7 } from "os";
119572
- import { dirname as dirname27, join as join83 } from "path";
119823
+ import { dirname as dirname27, join as join84 } from "path";
119573
119824
  var SUPPORTED_FORMATS = new Set([
119574
119825
  "image/jpeg",
119575
119826
  "image/png",
@@ -119610,8 +119861,8 @@ function convertImageToJpeg(inputPath, mimeType) {
119610
119861
  if (!existsSync79(inputPath)) {
119611
119862
  throw new Error(`File not found: ${inputPath}`);
119612
119863
  }
119613
- const tempDir = mkdtempSync(join83(tmpdir7(), "opencode-img-"));
119614
- const outputPath = join83(tempDir, "converted.jpg");
119864
+ const tempDir = mkdtempSync(join84(tmpdir7(), "opencode-img-"));
119865
+ const outputPath = join84(tempDir, "converted.jpg");
119615
119866
  log(`[image-converter] Converting ${mimeType} to JPEG: ${inputPath}`);
119616
119867
  try {
119617
119868
  if (process.platform === "darwin") {
@@ -119676,9 +119927,9 @@ function cleanupConvertedImage(filePath) {
119676
119927
  }
119677
119928
  }
119678
119929
  function convertBase64ImageToJpeg(base64Data, mimeType) {
119679
- const tempDir = mkdtempSync(join83(tmpdir7(), "opencode-b64-"));
119930
+ const tempDir = mkdtempSync(join84(tmpdir7(), "opencode-b64-"));
119680
119931
  const inputExt = mimeType.split("/")[1] || "bin";
119681
- const inputPath = join83(tempDir, `input.${inputExt}`);
119932
+ const inputPath = join84(tempDir, `input.${inputExt}`);
119682
119933
  const tempFiles = [inputPath];
119683
119934
  try {
119684
119935
  const cleanBase64 = base64Data.replace(/^data:[^;]+;base64,/, "");
@@ -119687,7 +119938,7 @@ function convertBase64ImageToJpeg(base64Data, mimeType) {
119687
119938
  log(`[image-converter] Converting Base64 ${mimeType} to JPEG`);
119688
119939
  const outputPath = convertImageToJpeg(inputPath, mimeType);
119689
119940
  tempFiles.push(outputPath);
119690
- const convertedBuffer = readFileSync51(outputPath);
119941
+ const convertedBuffer = readFileSync52(outputPath);
119691
119942
  const convertedBase64 = convertedBuffer.toString("base64");
119692
119943
  log(`[image-converter] Base64 conversion successful`);
119693
119944
  return { base64: convertedBase64, tempFiles };
@@ -120139,6 +120390,11 @@ function formatDetailedError(error92, ctx) {
120139
120390
  `);
120140
120391
  }
120141
120392
 
120393
+ // src/tools/delegate-task/resolve-call-id.ts
120394
+ function resolveCallID(ctx) {
120395
+ return ctx.callID ?? ctx.callId ?? ctx.call_id;
120396
+ }
120397
+
120142
120398
  // src/tools/delegate-task/background-continuation.ts
120143
120399
  async function executeBackgroundContinuation(args, ctx, executorCtx, parentContext) {
120144
120400
  const { manager } = executorCtx;
@@ -120166,8 +120422,9 @@ async function executeBackgroundContinuation(args, ctx, executorCtx, parentConte
120166
120422
  }
120167
120423
  };
120168
120424
  await ctx.metadata?.(bgContMeta);
120169
- if (ctx.callID) {
120170
- storeToolMetadata(ctx.sessionID, ctx.callID, bgContMeta);
120425
+ const callID = resolveCallID(ctx);
120426
+ if (callID) {
120427
+ storeToolMetadata(ctx.sessionID, callID, bgContMeta);
120171
120428
  }
120172
120429
  return `Background task continued.
120173
120430
 
@@ -120486,8 +120743,9 @@ async function executeSyncContinuation(args, ctx, executorCtx, deps = syncContin
120486
120743
  }
120487
120744
  };
120488
120745
  await ctx.metadata?.(syncContMeta);
120489
- if (ctx.callID) {
120490
- storeToolMetadata(ctx.sessionID, ctx.callID, syncContMeta);
120746
+ const callID = resolveCallID(ctx);
120747
+ if (callID) {
120748
+ storeToolMetadata(ctx.sessionID, callID, syncContMeta);
120491
120749
  }
120492
120750
  const allowTask = isPlanFamily(resumeAgent);
120493
120751
  const tddEnabled = sisyphusAgentConfig?.tdd;
@@ -120631,8 +120889,9 @@ Task ID: ${task.id}`;
120631
120889
  }
120632
120890
  };
120633
120891
  await ctx.metadata?.(bgTaskMeta);
120634
- if (ctx.callID) {
120635
- storeToolMetadata(ctx.sessionID, ctx.callID, bgTaskMeta);
120892
+ const callID = resolveCallID(ctx);
120893
+ if (callID) {
120894
+ storeToolMetadata(ctx.sessionID, callID, bgTaskMeta);
120636
120895
  }
120637
120896
  const startTime = new Date;
120638
120897
  const timingCfg = getTimingConfig();
@@ -120874,8 +121133,9 @@ Task ID: ${task.id}`;
120874
121133
  metadata
120875
121134
  };
120876
121135
  await ctx.metadata?.(unstableMeta);
120877
- if (ctx.callID) {
120878
- storeToolMetadata(ctx.sessionID, ctx.callID, unstableMeta);
121136
+ const callID = resolveCallID(ctx);
121137
+ if (callID) {
121138
+ storeToolMetadata(ctx.sessionID, callID, unstableMeta);
120879
121139
  }
120880
121140
  const taskMetadataBlock = sessionId ? `
120881
121141
 
@@ -121103,8 +121363,9 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
121103
121363
  }
121104
121364
  };
121105
121365
  await ctx.metadata?.(syncTaskMeta);
121106
- if (ctx.callID) {
121107
- storeToolMetadata(ctx.sessionID, ctx.callID, syncTaskMeta);
121366
+ const callID = resolveCallID(ctx);
121367
+ if (callID) {
121368
+ storeToolMetadata(ctx.sessionID, callID, syncTaskMeta);
121108
121369
  }
121109
121370
  const promptError = await deps.sendSyncPrompt(client2, {
121110
121371
  sessionID,
@@ -121582,7 +121843,7 @@ Available categories: ${categoryNames.join(", ")}`
121582
121843
  };
121583
121844
  }
121584
121845
  const resolvedModel = actualModel?.toLowerCase();
121585
- const isUnstableAgent = resolved.config.is_unstable_agent ?? (resolvedModel ? resolvedModel.includes("gemini") || resolvedModel.includes("minimax") || resolvedModel.includes("kimi") : false);
121846
+ const isUnstableAgent = resolved.config.is_unstable_agent ?? (resolvedModel ? resolvedModel.includes("gemini") || resolvedModel.includes("minimax") : false);
121586
121847
  const defaultProviderID = categoryModel?.providerID ?? parseModelString(actualModel ?? "")?.providerID ?? "opencode";
121587
121848
  const configuredFallbackChain = buildFallbackChainFromModels(normalizedConfiguredFallbackModels, defaultProviderID);
121588
121849
  const effectiveEntry = matchedFallback && categoryModel ? fallbackEntry ?? (configuredFallbackChain ? findMostSpecificFallbackEntry(categoryModel.providerID, categoryModel.modelID, configuredFallbackChain) : undefined) : undefined;
@@ -121846,6 +122107,9 @@ function createDelegateTask(options) {
121846
122107
  await ctx.metadata?.({
121847
122108
  title: args.description
121848
122109
  });
122110
+ if (!args.description || typeof args.description !== "string") {
122111
+ throw new Error(`Invalid arguments: 'description' parameter is REQUIRED. Provide a short (3-5 words) task description.`);
122112
+ }
121849
122113
  if (args.run_in_background === undefined) {
121850
122114
  throw new Error(`Invalid arguments: 'run_in_background' parameter is REQUIRED. Specify run_in_background=false for task delegation, or run_in_background=true for parallel exploration.`);
121851
122115
  }
@@ -121965,7 +122229,7 @@ function createDelegateTask(options) {
121965
122229
  // src/tools/delegate-task/index.ts
121966
122230
  init_constants();
121967
122231
  // src/tools/task/task-create.ts
121968
- import { join as join85 } from "path";
122232
+ import { join as join86 } from "path";
121969
122233
 
121970
122234
  // src/tools/task/types.ts
121971
122235
  var TaskStatusSchema = exports_external.enum(["pending", "in_progress", "completed", "deleted"]);
@@ -122019,18 +122283,18 @@ var TaskDeleteInputSchema = exports_external.object({
122019
122283
  });
122020
122284
 
122021
122285
  // src/features/claude-tasks/storage.ts
122022
- import { join as join84, dirname as dirname28, basename as basename13, isAbsolute as isAbsolute11 } from "path";
122023
- import { existsSync as existsSync80, mkdirSync as mkdirSync16, readFileSync as readFileSync52, writeFileSync as writeFileSync21, renameSync as renameSync5, unlinkSync as unlinkSync12, readdirSync as readdirSync20 } from "fs";
122286
+ import { join as join85, dirname as dirname28, basename as basename13, isAbsolute as isAbsolute11 } from "path";
122287
+ import { existsSync as existsSync80, mkdirSync as mkdirSync16, readFileSync as readFileSync53, writeFileSync as writeFileSync21, renameSync as renameSync5, unlinkSync as unlinkSync12, readdirSync as readdirSync21 } from "fs";
122024
122288
  import { randomUUID as randomUUID3 } from "crypto";
122025
122289
  function getTaskDir(config4 = {}) {
122026
122290
  const tasksConfig = config4.sisyphus?.tasks;
122027
122291
  const storagePath = tasksConfig?.storage_path;
122028
122292
  if (storagePath) {
122029
- return isAbsolute11(storagePath) ? storagePath : join84(process.cwd(), storagePath);
122293
+ return isAbsolute11(storagePath) ? storagePath : join85(process.cwd(), storagePath);
122030
122294
  }
122031
122295
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
122032
122296
  const listId = resolveTaskListId(config4);
122033
- return join84(configDir, "tasks", listId);
122297
+ return join85(configDir, "tasks", listId);
122034
122298
  }
122035
122299
  function sanitizePathSegment(value) {
122036
122300
  return value.replace(/[^a-zA-Z0-9_-]/g, "-") || "default";
@@ -122057,7 +122321,7 @@ function readJsonSafe(filePath, schema2) {
122057
122321
  if (!existsSync80(filePath)) {
122058
122322
  return null;
122059
122323
  }
122060
- const content = readFileSync52(filePath, "utf-8");
122324
+ const content = readFileSync53(filePath, "utf-8");
122061
122325
  const parsed = JSON.parse(content);
122062
122326
  const result = schema2.safeParse(parsed);
122063
122327
  if (!result.success) {
@@ -122089,7 +122353,7 @@ function generateTaskId() {
122089
122353
  return `T-${randomUUID3()}`;
122090
122354
  }
122091
122355
  function acquireLock(dirPath) {
122092
- const lockPath = join84(dirPath, ".lock");
122356
+ const lockPath = join85(dirPath, ".lock");
122093
122357
  const lockId = randomUUID3();
122094
122358
  const createLock = (timestamp2) => {
122095
122359
  writeFileSync21(lockPath, JSON.stringify({ id: lockId, timestamp: timestamp2 }), {
@@ -122099,7 +122363,7 @@ function acquireLock(dirPath) {
122099
122363
  };
122100
122364
  const isStale = () => {
122101
122365
  try {
122102
- const lockContent = readFileSync52(lockPath, "utf-8");
122366
+ const lockContent = readFileSync53(lockPath, "utf-8");
122103
122367
  const lockData = JSON.parse(lockContent);
122104
122368
  const lockAge = Date.now() - lockData.timestamp;
122105
122369
  return lockAge > STALE_LOCK_THRESHOLD_MS;
@@ -122139,7 +122403,7 @@ function acquireLock(dirPath) {
122139
122403
  try {
122140
122404
  if (!existsSync80(lockPath))
122141
122405
  return;
122142
- const lockContent = readFileSync52(lockPath, "utf-8");
122406
+ const lockContent = readFileSync53(lockPath, "utf-8");
122143
122407
  const lockData = JSON.parse(lockContent);
122144
122408
  if (lockData.id !== lockId)
122145
122409
  return;
@@ -122302,7 +122566,7 @@ async function handleCreate(args, config4, ctx, context) {
122302
122566
  threadID: context.sessionID
122303
122567
  };
122304
122568
  const validatedTask = TaskObjectSchema.parse(task);
122305
- writeJsonAtomic(join85(taskDir, `${taskId}.json`), validatedTask);
122569
+ writeJsonAtomic(join86(taskDir, `${taskId}.json`), validatedTask);
122306
122570
  await syncTaskTodoUpdate(ctx, validatedTask, context.sessionID);
122307
122571
  return JSON.stringify({
122308
122572
  task: {
@@ -122324,7 +122588,7 @@ async function handleCreate(args, config4, ctx, context) {
122324
122588
  }
122325
122589
  }
122326
122590
  // src/tools/task/task-get.ts
122327
- import { join as join86 } from "path";
122591
+ import { join as join87 } from "path";
122328
122592
  var TASK_ID_PATTERN = /^T-[A-Za-z0-9-]+$/;
122329
122593
  function parseTaskId(id) {
122330
122594
  if (!TASK_ID_PATTERN.test(id))
@@ -122349,7 +122613,7 @@ Returns null if the task does not exist or the file is invalid.`,
122349
122613
  return JSON.stringify({ error: "invalid_task_id" });
122350
122614
  }
122351
122615
  const taskDir = getTaskDir(config4);
122352
- const taskPath = join86(taskDir, `${taskId}.json`);
122616
+ const taskPath = join87(taskDir, `${taskId}.json`);
122353
122617
  const task = readJsonSafe(taskPath, TaskObjectSchema);
122354
122618
  return JSON.stringify({ task: task ?? null });
122355
122619
  } catch (error92) {
@@ -122362,8 +122626,8 @@ Returns null if the task does not exist or the file is invalid.`,
122362
122626
  });
122363
122627
  }
122364
122628
  // src/tools/task/task-list.ts
122365
- import { join as join87 } from "path";
122366
- import { existsSync as existsSync81, readdirSync as readdirSync21 } from "fs";
122629
+ import { join as join88 } from "path";
122630
+ import { existsSync as existsSync81, readdirSync as readdirSync22 } from "fs";
122367
122631
  function createTaskList(config4) {
122368
122632
  return tool({
122369
122633
  description: `List all active tasks with summary information.
@@ -122377,13 +122641,13 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
122377
122641
  if (!existsSync81(taskDir)) {
122378
122642
  return JSON.stringify({ tasks: [] });
122379
122643
  }
122380
- const files = readdirSync21(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
122644
+ const files = readdirSync22(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
122381
122645
  if (files.length === 0) {
122382
122646
  return JSON.stringify({ tasks: [] });
122383
122647
  }
122384
122648
  const allTasks = [];
122385
122649
  for (const fileId of files) {
122386
- const task = readJsonSafe(join87(taskDir, `${fileId}.json`), TaskObjectSchema);
122650
+ const task = readJsonSafe(join88(taskDir, `${fileId}.json`), TaskObjectSchema);
122387
122651
  if (task) {
122388
122652
  allTasks.push(task);
122389
122653
  }
@@ -122411,7 +122675,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
122411
122675
  });
122412
122676
  }
122413
122677
  // src/tools/task/task-update.ts
122414
- import { join as join88 } from "path";
122678
+ import { join as join89 } from "path";
122415
122679
  var TASK_ID_PATTERN2 = /^T-[A-Za-z0-9-]+$/;
122416
122680
  function parseTaskId2(id) {
122417
122681
  if (!TASK_ID_PATTERN2.test(id))
@@ -122459,7 +122723,7 @@ async function handleUpdate(args, config4, ctx, context) {
122459
122723
  return JSON.stringify({ error: "task_lock_unavailable" });
122460
122724
  }
122461
122725
  try {
122462
- const taskPath = join88(taskDir, `${taskId}.json`);
122726
+ const taskPath = join89(taskDir, `${taskId}.json`);
122463
122727
  const task = readJsonSafe(taskPath, TaskObjectSchema);
122464
122728
  if (!task) {
122465
122729
  return JSON.stringify({ error: "task_not_found" });
@@ -124883,14 +125147,15 @@ function formatDuration3(start, end) {
124883
125147
  // src/features/background-agent/background-task-notification-template.ts
124884
125148
  function buildBackgroundTaskNotificationText(input) {
124885
125149
  const { task, duration: duration5, statusText, allComplete, remainingCount, completedTasks } = input;
125150
+ const safeDescription = (t) => t.description || t.id;
124886
125151
  const errorInfo = task.error ? `
124887
125152
  **Error:** ${task.error}` : "";
124888
125153
  if (allComplete) {
124889
125154
  const succeededTasks = completedTasks.filter((t) => t.status === "completed");
124890
125155
  const failedTasks = completedTasks.filter((t) => t.status !== "completed");
124891
- const succeededText = succeededTasks.length > 0 ? succeededTasks.map((t) => `- \`${t.id}\`: ${t.description}`).join(`
125156
+ const succeededText = succeededTasks.length > 0 ? succeededTasks.map((t) => `- \`${t.id}\`: ${safeDescription(t)}`).join(`
124892
125157
  `) : "";
124893
- const failedText = failedTasks.length > 0 ? failedTasks.map((t) => `- \`${t.id}\`: ${t.description} [${t.status.toUpperCase()}]${t.error ? ` - ${t.error}` : ""}`).join(`
125158
+ const failedText = failedTasks.length > 0 ? failedTasks.map((t) => `- \`${t.id}\`: ${safeDescription(t)} [${t.status.toUpperCase()}]${t.error ? ` - ${t.error}` : ""}`).join(`
124894
125159
  `) : "";
124895
125160
  const hasFailures = failedTasks.length > 0;
124896
125161
  const header = hasFailures ? `[ALL BACKGROUND TASKS FINISHED - ${failedTasks.length} FAILED]` : "[ALL BACKGROUND TASKS COMPLETE]";
@@ -124907,7 +125172,7 @@ ${failedText}
124907
125172
  `;
124908
125173
  }
124909
125174
  if (!body) {
124910
- body = `- \`${task.id}\`: ${task.description} [${task.status.toUpperCase()}]${task.error ? ` - ${task.error}` : ""}
125175
+ body = `- \`${task.id}\`: ${safeDescription(task)} [${task.status.toUpperCase()}]${task.error ? ` - ${task.error}` : ""}
124911
125176
  `;
124912
125177
  }
124913
125178
  return `<system-reminder>
@@ -124924,7 +125189,7 @@ Use \`background_output(task_id="<id>")\` to retrieve each result.${hasFailures
124924
125189
  return `<system-reminder>
124925
125190
  [BACKGROUND TASK ${statusText}]
124926
125191
  **ID:** \`${task.id}\`
124927
- **Description:** ${task.description}
125192
+ **Description:** ${safeDescription(task)}
124928
125193
  **Duration:** ${duration5}${errorInfo}
124929
125194
 
124930
125195
  **${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.
@@ -125202,16 +125467,16 @@ function unregisterManagerForCleanup(manager) {
125202
125467
  }
125203
125468
 
125204
125469
  // src/features/background-agent/compaction-aware-message-resolver.ts
125205
- import { readdirSync as readdirSync22, readFileSync as readFileSync53 } from "fs";
125206
- import { join as join89 } from "path";
125207
- function isCompactionAgent4(agent) {
125470
+ import { readdirSync as readdirSync23, readFileSync as readFileSync54 } from "fs";
125471
+ import { join as join90 } from "path";
125472
+ function isCompactionAgent5(agent) {
125208
125473
  return agent?.trim().toLowerCase() === "compaction";
125209
125474
  }
125210
125475
  function hasFullAgentAndModel(message) {
125211
- return !!message.agent && !isCompactionAgent4(message.agent) && !!message.model?.providerID && !!message.model?.modelID;
125476
+ return !!message.agent && !isCompactionAgent5(message.agent) && !!message.model?.providerID && !!message.model?.modelID;
125212
125477
  }
125213
125478
  function hasPartialAgentOrModel(message) {
125214
- const hasAgent = !!message.agent && !isCompactionAgent4(message.agent);
125479
+ const hasAgent = !!message.agent && !isCompactionAgent5(message.agent);
125215
125480
  const hasModel = !!message.model?.providerID && !!message.model?.modelID;
125216
125481
  return hasAgent || hasModel || !!message.tools;
125217
125482
  }
@@ -125237,7 +125502,7 @@ function convertSessionMessageToStoredMessage(message) {
125237
125502
  function mergeStoredMessages(messages, sessionID) {
125238
125503
  const merged = {};
125239
125504
  for (const message of messages) {
125240
- if (!message || isCompactionAgent4(message.agent)) {
125505
+ if (!message || isCompactionAgent5(message.agent)) {
125241
125506
  continue;
125242
125507
  }
125243
125508
  if (!merged.agent && message.agent) {
@@ -125278,11 +125543,11 @@ function resolvePromptContextFromSessionMessages(messages, sessionID) {
125278
125543
  }
125279
125544
  function findNearestMessageExcludingCompaction(messageDir, sessionID) {
125280
125545
  try {
125281
- const files = readdirSync22(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
125546
+ const files = readdirSync23(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
125282
125547
  const messages = [];
125283
125548
  for (const file3 of files) {
125284
125549
  try {
125285
- const content = readFileSync53(join89(messageDir, file3), "utf-8");
125550
+ const content = readFileSync54(join90(messageDir, file3), "utf-8");
125286
125551
  messages.push(JSON.parse(content));
125287
125552
  } catch {
125288
125553
  continue;
@@ -125368,7 +125633,7 @@ function handleSessionIdleBackgroundEvent(args) {
125368
125633
  }
125369
125634
 
125370
125635
  // src/features/background-agent/manager.ts
125371
- import { join as join90 } from "path";
125636
+ import { join as join91 } from "path";
125372
125637
 
125373
125638
  // src/features/background-agent/remove-task-toast-tracking.ts
125374
125639
  function removeTaskToastTracking(taskId) {
@@ -127022,7 +127287,7 @@ ${originalText}`;
127022
127287
  parentSessionID: task.parentSessionID
127023
127288
  });
127024
127289
  }
127025
- const messageDir = join90(MESSAGE_STORAGE, task.parentSessionID);
127290
+ const messageDir = join91(MESSAGE_STORAGE, task.parentSessionID);
127026
127291
  const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir, task.parentSessionID) : null;
127027
127292
  agent = currentMessage?.agent ?? task.parentAgent;
127028
127293
  model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
@@ -127350,11 +127615,11 @@ ${originalText}`;
127350
127615
  }
127351
127616
  }
127352
127617
  // src/features/mcp-oauth/storage.ts
127353
- import { chmodSync as chmodSync2, existsSync as existsSync82, mkdirSync as mkdirSync17, readFileSync as readFileSync54, unlinkSync as unlinkSync13, writeFileSync as writeFileSync22 } from "fs";
127354
- import { dirname as dirname29, join as join91 } from "path";
127618
+ import { chmodSync as chmodSync2, existsSync as existsSync82, mkdirSync as mkdirSync17, readFileSync as readFileSync55, unlinkSync as unlinkSync13, writeFileSync as writeFileSync22 } from "fs";
127619
+ import { dirname as dirname29, join as join92 } from "path";
127355
127620
  var STORAGE_FILE_NAME = "mcp-oauth.json";
127356
127621
  function getMcpOauthStoragePath() {
127357
- return join91(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
127622
+ return join92(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
127358
127623
  }
127359
127624
  function normalizeHost(serverHost) {
127360
127625
  let host = serverHost.trim();
@@ -127395,7 +127660,7 @@ function readStore() {
127395
127660
  return null;
127396
127661
  }
127397
127662
  try {
127398
- const content = readFileSync54(filePath, "utf-8");
127663
+ const content = readFileSync55(filePath, "utf-8");
127399
127664
  return JSON.parse(content);
127400
127665
  } catch {
127401
127666
  return null;
@@ -133900,18 +134165,18 @@ class TmuxSessionManager {
133900
134165
  var SESSION_TIMEOUT_MS3 = 10 * 60 * 1000;
133901
134166
  var MIN_STABILITY_TIME_MS4 = 10 * 1000;
133902
134167
  // src/features/claude-code-mcp-loader/loader.ts
133903
- import { existsSync as existsSync83, readFileSync as readFileSync55 } from "fs";
133904
- import { join as join92 } from "path";
134168
+ import { existsSync as existsSync83, readFileSync as readFileSync56 } from "fs";
134169
+ import { join as join93 } from "path";
133905
134170
  import { homedir as homedir15 } from "os";
133906
134171
  init_logger();
133907
134172
  function getMcpConfigPaths() {
133908
134173
  const claudeConfigDir = getClaudeConfigDir();
133909
134174
  const cwd = process.cwd();
133910
134175
  return [
133911
- { path: join92(homedir15(), ".claude.json"), scope: "user" },
133912
- { path: join92(claudeConfigDir, ".mcp.json"), scope: "user" },
133913
- { path: join92(cwd, ".mcp.json"), scope: "project" },
133914
- { path: join92(cwd, ".claude", ".mcp.json"), scope: "local" }
134176
+ { path: join93(homedir15(), ".claude.json"), scope: "user" },
134177
+ { path: join93(claudeConfigDir, ".mcp.json"), scope: "user" },
134178
+ { path: join93(cwd, ".mcp.json"), scope: "project" },
134179
+ { path: join93(cwd, ".claude", ".mcp.json"), scope: "local" }
133915
134180
  ];
133916
134181
  }
133917
134182
  async function loadMcpConfigFile(filePath) {
@@ -133934,7 +134199,7 @@ function getSystemMcpServerNames() {
133934
134199
  if (!existsSync83(path13))
133935
134200
  continue;
133936
134201
  try {
133937
- const content = readFileSync55(path13, "utf-8");
134202
+ const content = readFileSync56(path13, "utf-8");
133938
134203
  const config4 = JSON.parse(content);
133939
134204
  if (!config4?.mcpServers)
133940
134205
  continue;
@@ -138984,7 +139249,7 @@ function buildHephaestusPrompt3(availableAgents = [], availableTools = [], avail
138984
139249
  const librarianSection = buildLibrarianSection(availableAgents);
138985
139250
  const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
138986
139251
  const delegationTable = buildDelegationTable(availableAgents);
138987
- const oracleSection = buildOracleSection(availableAgents);
139252
+ const hasOracle = availableAgents.some((agent) => agent.name === "oracle");
138988
139253
  const hardBlocks = buildHardBlocksSection();
138989
139254
  const antiPatterns = buildAntiPatternsSection();
138990
139255
  const antiDuplication = buildAntiDuplicationSection();
@@ -139193,8 +139458,23 @@ Every \`task()\` returns a session_id. Use it for all follow-ups:
139193
139458
 
139194
139459
  This preserves full context, avoids repeated exploration, saves 70%+ tokens.
139195
139460
  </session_continuity>
139196
- ${oracleSection ? `
139197
- ${oracleSection}` : ""}
139461
+ ${hasOracle ? `
139462
+ <oracle>
139463
+ Oracle is a read-only reasoning model, available as a last-resort escalation path when you are genuinely stuck.
139464
+
139465
+ Consult Oracle only when:
139466
+ - You have tried 2+ materially different approaches and all failed
139467
+ - You have documented what you tried and why each approach failed
139468
+ - The problem requires architectural insight beyond what codebase exploration provides
139469
+
139470
+ Do not consult Oracle:
139471
+ - Before attempting the fix yourself (try first, escalate later)
139472
+ - For questions answerable from code you have already read
139473
+ - For routine decisions, even complex ones you can reason through
139474
+ - On your first or second attempt at any task
139475
+
139476
+ If you do consult Oracle, announce "Consulting Oracle for [reason]" before invocation. Collect Oracle results before your final answer. Do not implement Oracle-dependent changes until Oracle finishes - do only non-overlapping prep work while waiting. Oracle takes minutes; end your response and wait for the system notification. Never poll, never cancel Oracle.
139477
+ </oracle>` : ""}
139198
139478
  </delegation>`;
139199
139479
  const communicationBlock = `<communication>
139200
139480
  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.
@@ -139286,7 +139566,7 @@ function createHephaestusAgent2(model, availableAgents, availableToolNames, avai
139286
139566
  }
139287
139567
  createHephaestusAgent2.mode = MODE10;
139288
139568
  // src/agents/builtin-agents/resolve-file-uri.ts
139289
- import { existsSync as existsSync84, readFileSync as readFileSync56 } from "fs";
139569
+ import { existsSync as existsSync84, readFileSync as readFileSync57 } from "fs";
139290
139570
  import { homedir as homedir16 } from "os";
139291
139571
  import { isAbsolute as isAbsolute12, resolve as resolve20 } from "path";
139292
139572
  init_logger();
@@ -139315,7 +139595,7 @@ function resolvePromptAppend(promptAppend, configDir) {
139315
139595
  return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
139316
139596
  }
139317
139597
  try {
139318
- return readFileSync56(filePath, "utf8");
139598
+ return readFileSync57(filePath, "utf8");
139319
139599
  } catch {
139320
139600
  return `[WARNING: Could not read file: ${promptAppend}]`;
139321
139601
  }
@@ -140538,8 +140818,8 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
140538
140818
  return result;
140539
140819
  }
140540
140820
  // src/features/claude-code-agent-loader/loader.ts
140541
- import { existsSync as existsSync85, readdirSync as readdirSync23, readFileSync as readFileSync57 } from "fs";
140542
- import { join as join93, basename as basename14 } from "path";
140821
+ import { existsSync as existsSync85, readdirSync as readdirSync24, readFileSync as readFileSync58 } from "fs";
140822
+ import { join as join94, basename as basename14 } from "path";
140543
140823
  function parseToolsConfig2(toolsStr) {
140544
140824
  if (!toolsStr)
140545
140825
  return;
@@ -140556,15 +140836,15 @@ function loadAgentsFromDir(agentsDir, scope) {
140556
140836
  if (!existsSync85(agentsDir)) {
140557
140837
  return [];
140558
140838
  }
140559
- const entries = readdirSync23(agentsDir, { withFileTypes: true });
140839
+ const entries = readdirSync24(agentsDir, { withFileTypes: true });
140560
140840
  const agents = [];
140561
140841
  for (const entry of entries) {
140562
140842
  if (!isMarkdownFile(entry))
140563
140843
  continue;
140564
- const agentPath = join93(agentsDir, entry.name);
140844
+ const agentPath = join94(agentsDir, entry.name);
140565
140845
  const agentName = basename14(entry.name, ".md");
140566
140846
  try {
140567
- const content = readFileSync57(agentPath, "utf-8");
140847
+ const content = readFileSync58(agentPath, "utf-8");
140568
140848
  const { data, body } = parseFrontmatter(content);
140569
140849
  const name = data.name || agentName;
140570
140850
  const originalDescription = data.description || "";
@@ -140594,7 +140874,7 @@ function loadAgentsFromDir(agentsDir, scope) {
140594
140874
  return agents;
140595
140875
  }
140596
140876
  function loadUserAgents() {
140597
- const userAgentsDir = join93(getClaudeConfigDir(), "agents");
140877
+ const userAgentsDir = join94(getClaudeConfigDir(), "agents");
140598
140878
  const agents = loadAgentsFromDir(userAgentsDir, "user");
140599
140879
  const result = {};
140600
140880
  for (const agent of agents) {
@@ -140603,7 +140883,7 @@ function loadUserAgents() {
140603
140883
  return result;
140604
140884
  }
140605
140885
  function loadProjectAgents(directory) {
140606
- const projectAgentsDir = join93(directory ?? process.cwd(), ".claude", "agents");
140886
+ const projectAgentsDir = join94(directory ?? process.cwd(), ".claude", "agents");
140607
140887
  const agents = loadAgentsFromDir(projectAgentsDir, "project");
140608
140888
  const result = {};
140609
140889
  for (const agent of agents) {
@@ -143093,7 +143373,7 @@ async function applyAgentConfig(params) {
143093
143373
  }
143094
143374
  // src/features/claude-code-command-loader/loader.ts
143095
143375
  import { promises as fs19 } from "fs";
143096
- import { join as join94, basename as basename15 } from "path";
143376
+ import { join as join95, basename as basename15 } from "path";
143097
143377
  init_logger();
143098
143378
  async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix = "") {
143099
143379
  try {
@@ -143124,7 +143404,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
143124
143404
  if (entry.isDirectory()) {
143125
143405
  if (entry.name.startsWith("."))
143126
143406
  continue;
143127
- const subDirPath = join94(commandsDir, entry.name);
143407
+ const subDirPath = join95(commandsDir, entry.name);
143128
143408
  const subPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
143129
143409
  const subCommands = await loadCommandsFromDir(subDirPath, scope, visited, subPrefix);
143130
143410
  commands3.push(...subCommands);
@@ -143132,7 +143412,7 @@ async function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix
143132
143412
  }
143133
143413
  if (!isMarkdownFile(entry))
143134
143414
  continue;
143135
- const commandPath = join94(commandsDir, entry.name);
143415
+ const commandPath = join95(commandsDir, entry.name);
143136
143416
  const baseCommandName = basename15(entry.name, ".md");
143137
143417
  const commandName = prefix ? `${prefix}/${baseCommandName}` : baseCommandName;
143138
143418
  try {
@@ -143191,12 +143471,12 @@ function commandsToRecord(commands3) {
143191
143471
  return result;
143192
143472
  }
143193
143473
  async function loadUserCommands() {
143194
- const userCommandsDir = join94(getClaudeConfigDir(), "commands");
143474
+ const userCommandsDir = join95(getClaudeConfigDir(), "commands");
143195
143475
  const commands3 = await loadCommandsFromDir(userCommandsDir, "user");
143196
143476
  return commandsToRecord(commands3);
143197
143477
  }
143198
143478
  async function loadProjectCommands(directory) {
143199
- const projectCommandsDir = join94(directory ?? process.cwd(), ".claude", "commands");
143479
+ const projectCommandsDir = join95(directory ?? process.cwd(), ".claude", "commands");
143200
143480
  const commands3 = await loadCommandsFromDir(projectCommandsDir, "project");
143201
143481
  return commandsToRecord(commands3);
143202
143482
  }
@@ -144182,10 +144462,10 @@ function createChatHeadersHandler(args) {
144182
144462
 
144183
144463
  // src/plugin/ultrawork-db-model-override.ts
144184
144464
  import { Database } from "bun:sqlite";
144185
- import { join as join95 } from "path";
144465
+ import { join as join96 } from "path";
144186
144466
  import { existsSync as existsSync86 } from "fs";
144187
144467
  function getDbPath() {
144188
- return join95(getDataDir(), "opencode", "opencode.db");
144468
+ return join96(getDataDir(), "opencode", "opencode.db");
144189
144469
  }
144190
144470
  var MAX_MICROTASK_RETRIES = 10;
144191
144471
  function tryUpdateMessageModel(db, messageId, targetModel, variant) {
@@ -144736,7 +145016,7 @@ function applyUserConfiguredFallbackChain(sessionID, agentName, currentProviderI
144736
145016
  setSessionFallbackChain(sessionID, fallbackChain);
144737
145017
  }
144738
145018
  }
144739
- function isCompactionAgent5(agent) {
145019
+ function isCompactionAgent6(agent) {
144740
145020
  return agent.toLowerCase() === "compaction";
144741
145021
  }
144742
145022
  function createEventHandler2(args) {
@@ -144941,7 +145221,7 @@ function createEventHandler2(args) {
144941
145221
  const agent = info?.agent;
144942
145222
  const role = info?.role;
144943
145223
  if (sessionID && role === "user") {
144944
- const isCompactionMessage = agent ? isCompactionAgent5(agent) : false;
145224
+ const isCompactionMessage = agent ? isCompactionAgent6(agent) : false;
144945
145225
  if (agent && !isCompactionMessage) {
144946
145226
  updateSessionAgent(sessionID, agent);
144947
145227
  }