oh-my-opencode 4.1.0 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -54573,6 +54573,13 @@ function createInternalAgentTextPart(text) {
54573
54573
  ${OMO_INTERNAL_INITIATOR_MARKER}`
54574
54574
  };
54575
54575
  }
54576
+ function createInternalAgentContinuationTextPart(text) {
54577
+ return {
54578
+ ...createInternalAgentTextPart(text),
54579
+ synthetic: true,
54580
+ metadata: { compaction_continue: true }
54581
+ };
54582
+ }
54576
54583
  var OMO_INTERNAL_INITIATOR_MARKER = "<!-- OMO_INTERNAL_INITIATOR -->", INTERNAL_INITIATOR_MARKER_PATTERN;
54577
54584
  var init_internal_initiator_marker = __esm(() => {
54578
54585
  INTERNAL_INITIATOR_MARKER_PATTERN = /\n*<!--\s*OMO_INTERNAL_INITIATOR\s*-->\s*/g;
@@ -55991,6 +55998,7 @@ __export(exports_shared, {
55991
55998
  createSystemDirective: () => createSystemDirective,
55992
55999
  createModelCapabilitiesCacheStore: () => createModelCapabilitiesCacheStore,
55993
56000
  createInternalAgentTextPart: () => createInternalAgentTextPart,
56001
+ createInternalAgentContinuationTextPart: () => createInternalAgentContinuationTextPart,
55994
56002
  createDynamicTruncator: () => createDynamicTruncator,
55995
56003
  createConnectedProvidersCacheStore: () => createConnectedProvidersCacheStore,
55996
56004
  createAgentToolRestrictions: () => createAgentToolRestrictions,
@@ -58319,18 +58327,18 @@ function serializeRuntimeState(runtimeState) {
58319
58327
  return `${JSON.stringify(parsedRuntimeState, null, 2)}
58320
58328
  `;
58321
58329
  }
58322
- function isRecord11(value) {
58330
+ function isRecord12(value) {
58323
58331
  return typeof value === "object" && value !== null && !Array.isArray(value);
58324
58332
  }
58325
58333
  function stripLegacyRuntimeStateMemberFields(member) {
58326
- if (!isRecord11(member)) {
58334
+ if (!isRecord12(member)) {
58327
58335
  return member;
58328
58336
  }
58329
58337
  const { delegateTaskCallsUsed: _delegateTaskCallsUsed, ...memberWithoutLegacyFields } = member;
58330
58338
  return memberWithoutLegacyFields;
58331
58339
  }
58332
58340
  function stripLegacyRuntimeStateFields(rawState) {
58333
- if (!isRecord11(rawState)) {
58341
+ if (!isRecord12(rawState)) {
58334
58342
  return rawState;
58335
58343
  }
58336
58344
  const members = rawState["members"];
@@ -58604,10 +58612,13 @@ function clearState(directory, customPath) {
58604
58612
  return false;
58605
58613
  }
58606
58614
  }
58607
- function incrementIteration(directory, customPath) {
58615
+ function incrementIteration(directory, customPath, expected) {
58608
58616
  const state3 = readState(directory, customPath);
58609
58617
  if (!state3)
58610
58618
  return null;
58619
+ if (expected && (state3.iteration !== expected.iteration || state3.session_id !== expected.sessionID)) {
58620
+ return null;
58621
+ }
58611
58622
  state3.iteration += 1;
58612
58623
  if (writeState(directory, state3, customPath)) {
58613
58624
  return state3;
@@ -69633,6 +69644,48 @@ init_logger();
69633
69644
  init_opencode_storage_detection();
69634
69645
  init_agent_display_names();
69635
69646
 
69647
+ // src/hooks/shared/session-idle-settle.ts
69648
+ var DEFAULT_SESSION_IDLE_SETTLE_MS = 150;
69649
+ function settleAfterSessionIdle(ms = DEFAULT_SESSION_IDLE_SETTLE_MS) {
69650
+ return ms > 0 ? new Promise((resolve8) => setTimeout(resolve8, ms)) : Promise.resolve();
69651
+ }
69652
+ var ACTIVE_SESSION_STATUSES = new Set(["busy", "retry", "running"]);
69653
+ function isRecord8(value) {
69654
+ return typeof value === "object" && value !== null;
69655
+ }
69656
+ function getSessionStatusPayload(response) {
69657
+ if (isRecord8(response) && isRecord8(response.data)) {
69658
+ return response.data;
69659
+ }
69660
+ if (isRecord8(response)) {
69661
+ return response;
69662
+ }
69663
+ return {};
69664
+ }
69665
+ function isActiveSessionStatusType(statusType) {
69666
+ return ACTIVE_SESSION_STATUSES.has(statusType);
69667
+ }
69668
+ async function isSessionActive(client, sessionID) {
69669
+ if (typeof client.session?.status !== "function") {
69670
+ return false;
69671
+ }
69672
+ try {
69673
+ const statusResult = await client.session.status();
69674
+ const status = getSessionStatusPayload(statusResult)[sessionID];
69675
+ if (!isRecord8(status)) {
69676
+ return false;
69677
+ }
69678
+ const statusType = status.type;
69679
+ return typeof statusType === "string" && isActiveSessionStatusType(statusType);
69680
+ } catch {
69681
+ return false;
69682
+ }
69683
+ }
69684
+ async function shouldPromptAfterSessionIdle(client, sessionID, settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS) {
69685
+ await settleAfterSessionIdle(settleMs);
69686
+ return !await isSessionActive(client, sessionID);
69687
+ }
69688
+
69636
69689
  // src/hooks/todo-continuation-enforcer/message-directory.ts
69637
69690
  init_opencode_message_dir();
69638
69691
 
@@ -69906,6 +69959,10 @@ ${todoList}`;
69906
69959
  log(`[${HOOK_NAME}] Skipped injection: session was cancelled before prompt`, { sessionID });
69907
69960
  return;
69908
69961
  }
69962
+ if (await isSessionActive(ctx.client, sessionID)) {
69963
+ log(`[${HOOK_NAME}] Skipped injection: session is active before prompt`, { sessionID });
69964
+ return;
69965
+ }
69909
69966
  if (injectionState) {
69910
69967
  injectionState.inFlight = true;
69911
69968
  }
@@ -69926,7 +69983,7 @@ ${todoList}`;
69926
69983
  ...launchModel ? { model: launchModel } : {},
69927
69984
  ...launchVariant ? { variant: launchVariant } : {},
69928
69985
  ...inheritedTools ? { tools: inheritedTools } : {},
69929
- parts: [createInternalAgentTextPart(prompt)]
69986
+ parts: [createInternalAgentContinuationTextPart(prompt)]
69930
69987
  },
69931
69988
  query: { directory: ctx.directory }
69932
69989
  });
@@ -71025,12 +71082,12 @@ async function playSessionNotificationSound(ctx, platform2, soundPath) {
71025
71082
  }
71026
71083
 
71027
71084
  // src/hooks/session-notification-event-properties.ts
71028
- function isRecord8(value) {
71085
+ function isRecord9(value) {
71029
71086
  return typeof value === "object" && value !== null;
71030
71087
  }
71031
71088
  function getEventInfo(properties) {
71032
71089
  const info = properties?.info;
71033
- return isRecord8(info) ? info : undefined;
71090
+ return isRecord9(info) ? info : undefined;
71034
71091
  }
71035
71092
  function getSessionID(properties) {
71036
71093
  const sessionID = properties?.sessionID;
@@ -71047,7 +71104,7 @@ function getSessionID(properties) {
71047
71104
  if (typeof infoSessionId === "string" && infoSessionId.length > 0)
71048
71105
  return infoSessionId;
71049
71106
  const part = properties?.part;
71050
- if (isRecord8(part)) {
71107
+ if (isRecord9(part)) {
71051
71108
  const partSessionID = part.sessionID;
71052
71109
  if (typeof partSessionID === "string" && partSessionID.length > 0)
71053
71110
  return partSessionID;
@@ -71068,13 +71125,13 @@ function getEventToolName(properties) {
71068
71125
  }
71069
71126
  function getQuestionText(properties) {
71070
71127
  const args = properties?.args;
71071
- if (!isRecord8(args))
71128
+ if (!isRecord9(args))
71072
71129
  return "";
71073
71130
  const questions = args.questions;
71074
71131
  if (!Array.isArray(questions) || questions.length === 0)
71075
71132
  return "";
71076
71133
  const firstQuestion = questions[0];
71077
- if (!isRecord8(firstQuestion))
71134
+ if (!isRecord9(firstQuestion))
71078
71135
  return "";
71079
71136
  const questionText = firstQuestion.question;
71080
71137
  return typeof questionText === "string" ? questionText : "";
@@ -72304,7 +72361,7 @@ async function resumeSession(client, config) {
72304
72361
  await client.session.promptAsync({
72305
72362
  path: { id: config.sessionID },
72306
72363
  body: {
72307
- parts: [createInternalAgentTextPart(RECOVERY_RESUME_TEXT)],
72364
+ parts: [createInternalAgentContinuationTextPart(RECOVERY_RESUME_TEXT)],
72308
72365
  agent: config.agent,
72309
72366
  ...launchModel ? { model: launchModel } : {},
72310
72367
  ...launchVariant ? { variant: launchVariant } : {},
@@ -74039,6 +74096,12 @@ async function runAggressiveTruncationStrategy(params) {
74039
74096
  clearSessionState(params.autoCompactState, params.sessionID);
74040
74097
  setTimeout(async () => {
74041
74098
  try {
74099
+ if (await isSessionActive(params.client, params.sessionID)) {
74100
+ log("[auto-compact] skipped delayed auto prompt because session became active", {
74101
+ sessionID: params.sessionID
74102
+ });
74103
+ return;
74104
+ }
74042
74105
  const sdkMessage = await findNearestMessageWithFieldsFromSDK(params.client, params.sessionID);
74043
74106
  const previousMessage = sdkMessage ?? (() => {
74044
74107
  const messageDir = getMessageDir(params.sessionID);
@@ -74060,7 +74123,12 @@ async function runAggressiveTruncationStrategy(params) {
74060
74123
  },
74061
74124
  query: { directory: params.directory }
74062
74125
  });
74063
- } catch {}
74126
+ } catch (error) {
74127
+ log("[auto-compact] delayed auto prompt failed", {
74128
+ sessionID: params.sessionID,
74129
+ error: String(error)
74130
+ });
74131
+ }
74064
74132
  }, 500);
74065
74133
  return { handled: true, nextTruncateAttempt };
74066
74134
  }
@@ -75408,6 +75476,12 @@ function getNextReachableFallback(sessionID, state3) {
75408
75476
  }
75409
75477
 
75410
75478
  // src/hooks/model-fallback/fallback-state-controller.ts
75479
+ function canonicalizeModelIDForDuplicateCheck(modelID) {
75480
+ return modelID.toLowerCase().replace(/\./g, "-");
75481
+ }
75482
+ function isSameFailedModel(state3, providerID, modelID) {
75483
+ return state3.providerID.toLowerCase() === providerID.toLowerCase() && canonicalizeModelIDForDuplicateCheck(state3.modelID) === canonicalizeModelIDForDuplicateCheck(modelID);
75484
+ }
75411
75485
  function createModelFallbackStateController(input) {
75412
75486
  const { pendingModelFallbacks, lastToastKey, sessionFallbackChains } = input;
75413
75487
  function setSessionFallbackChain(sessionID, fallbackChain) {
@@ -75446,6 +75520,10 @@ function createModelFallbackStateController(input) {
75446
75520
  log(`[model-fallback] Pending fallback already armed for session: ${sessionID}`);
75447
75521
  return false;
75448
75522
  }
75523
+ if (existing.attemptCount > 0 && isSameFailedModel(existing, currentProviderID, currentModelID)) {
75524
+ log(`[model-fallback] Ignoring duplicate fallback arm for already handled model in session: ${sessionID}`);
75525
+ return false;
75526
+ }
75449
75527
  existing.providerID = currentProviderID;
75450
75528
  existing.modelID = currentModelID;
75451
75529
  existing.pending = true;
@@ -76690,7 +76768,7 @@ ${result.stderr.trim()}`);
76690
76768
 
76691
76769
  // src/hooks/claude-code-hooks/handlers/tool-execute-after-handler.ts
76692
76770
  init_shared();
76693
- function isRecord9(value) {
76771
+ function isRecord10(value) {
76694
76772
  return typeof value === "object" && value !== null && !Array.isArray(value);
76695
76773
  }
76696
76774
  function getStringValue(record, key) {
@@ -76703,7 +76781,7 @@ function getNumberValue(record, key) {
76703
76781
  }
76704
76782
  function buildTranscriptToolOutput(outputText, metadata) {
76705
76783
  const compactOutput = { output: outputText };
76706
- if (!isRecord9(metadata)) {
76784
+ if (!isRecord10(metadata)) {
76707
76785
  return compactOutput;
76708
76786
  }
76709
76787
  const filePath = getStringValue(metadata, "filePath") ?? getStringValue(metadata, "path") ?? getStringValue(metadata, "file");
@@ -76725,7 +76803,7 @@ function buildTranscriptToolOutput(outputText, metadata) {
76725
76803
  }
76726
76804
  }
76727
76805
  const filediff = metadata.filediff;
76728
- if (isRecord9(filediff)) {
76806
+ if (isRecord10(filediff)) {
76729
76807
  const additions = getNumberValue(filediff, "additions");
76730
76808
  const deletions = getNumberValue(filediff, "deletions");
76731
76809
  if (additions !== undefined || deletions !== undefined) {
@@ -78695,14 +78773,14 @@ var defaultDeps3 = {
78695
78773
  runBackgroundUpdateCheck,
78696
78774
  log
78697
78775
  };
78698
- var isRecord10 = (value) => {
78776
+ var isRecord11 = (value) => {
78699
78777
  return typeof value === "object" && value !== null;
78700
78778
  };
78701
78779
  var getParentID = (properties) => {
78702
- if (!isRecord10(properties))
78780
+ if (!isRecord11(properties))
78703
78781
  return;
78704
78782
  const { info } = properties;
78705
- if (!isRecord10(info))
78783
+ if (!isRecord11(info))
78706
78784
  return;
78707
78785
  const { parentID } = info;
78708
78786
  return typeof parentID === "string" && parentID.length > 0 ? parentID : undefined;
@@ -80171,6 +80249,31 @@ function detectBannedCommand(command) {
80171
80249
  }
80172
80250
  return;
80173
80251
  }
80252
+ function detectWindowsShellType(shellPath) {
80253
+ if (!shellPath) {
80254
+ return;
80255
+ }
80256
+ const shellName = shellPath.replace(/\\/g, "/").split("/").pop()?.toLowerCase();
80257
+ if (shellName === "cmd" || shellName === "cmd.exe") {
80258
+ return "cmd";
80259
+ }
80260
+ if (shellName === "powershell" || shellName === "powershell.exe" || shellName === "pwsh" || shellName === "pwsh.exe") {
80261
+ return "powershell";
80262
+ }
80263
+ return;
80264
+ }
80265
+ function detectCommandShellType() {
80266
+ if (process.platform === "win32" && process.env.SHELL) {
80267
+ const shellType = detectWindowsShellType(process.env.SHELL);
80268
+ if (shellType) {
80269
+ return shellType;
80270
+ }
80271
+ }
80272
+ if (process.platform === "win32" && !process.env.SHELL && !process.env.MSYSTEM) {
80273
+ return detectWindowsShellType(process.env.ComSpec) ?? "cmd";
80274
+ }
80275
+ return detectShellType();
80276
+ }
80174
80277
  function createNonInteractiveEnvHook(_ctx) {
80175
80278
  return {
80176
80279
  "tool.execute.before": async (input, output) => {
@@ -80189,7 +80292,7 @@ function createNonInteractiveEnvHook(_ctx) {
80189
80292
  if (!isGitCommand) {
80190
80293
  return;
80191
80294
  }
80192
- const shellType = detectShellType();
80295
+ const shellType = detectCommandShellType();
80193
80296
  const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, shellType);
80194
80297
  if (command.trim().startsWith(envPrefix.trim())) {
80195
80298
  return;
@@ -81111,8 +81214,8 @@ function createLoopStateController(options) {
81111
81214
  clear() {
81112
81215
  return clearState(directory, stateDir);
81113
81216
  },
81114
- incrementIteration() {
81115
- return incrementIteration(directory, stateDir);
81217
+ incrementIteration(expected) {
81218
+ return incrementIteration(directory, stateDir, expected);
81116
81219
  },
81117
81220
  setSessionID(sessionID) {
81118
81221
  const state3 = readState(directory, stateDir);
@@ -81125,11 +81228,14 @@ function createLoopStateController(options) {
81125
81228
  }
81126
81229
  return state3;
81127
81230
  },
81128
- setMessageCountAtStart(sessionID, messageCountAtStart) {
81231
+ setMessageCountAtStart(sessionID, messageCountAtStart, expectedStartedAt) {
81129
81232
  const state3 = readState(directory, stateDir);
81130
81233
  if (!state3 || state3.session_id !== sessionID) {
81131
81234
  return null;
81132
81235
  }
81236
+ if (state3.iteration !== 1 || state3.verification_pending || state3.message_count_at_start !== undefined || expectedStartedAt !== undefined && state3.started_at !== expectedStartedAt) {
81237
+ return null;
81238
+ }
81133
81239
  state3.message_count_at_start = messageCountAtStart;
81134
81240
  if (!writeState(directory, state3, stateDir)) {
81135
81241
  return null;
@@ -81366,7 +81472,7 @@ async function injectContinuationPrompt(ctx, options) {
81366
81472
  ...launchModel ? { model: launchModel } : {},
81367
81473
  ...launchVariant ? { variant: launchVariant } : {},
81368
81474
  ...inheritedTools ? { tools: inheritedTools } : {},
81369
- parts: [createInternalAgentTextPart(options.prompt)]
81475
+ parts: [createInternalAgentContinuationTextPart(options.prompt)]
81370
81476
  },
81371
81477
  query: { directory: options.directory }
81372
81478
  });
@@ -81496,7 +81602,7 @@ function resolveToolCallID(ctx) {
81496
81602
  }
81497
81603
  // src/features/tool-metadata-store/task-metadata-contract.ts
81498
81604
  init_logger();
81499
- function isRecord12(value) {
81605
+ function isRecord13(value) {
81500
81606
  return typeof value === "object" && value !== null;
81501
81607
  }
81502
81608
  function readString2(value) {
@@ -81581,7 +81687,7 @@ function parseTaskMetadataBlock(text) {
81581
81687
  return parsed;
81582
81688
  }
81583
81689
  function extractTaskLink(metadata, outputText) {
81584
- if (isRecord12(metadata)) {
81690
+ if (isRecord13(metadata)) {
81585
81691
  const metadataLink = {
81586
81692
  sessionId: readSessionIdFromMetadata(metadata),
81587
81693
  taskId: readTaskIdFromMetadata(metadata),
@@ -81879,9 +81985,9 @@ async function continueIteration(ctx, state3, options) {
81879
81985
  previousSessionID: options.previousSessionID,
81880
81986
  newSessionID
81881
81987
  });
81882
- return { status: "dispatched" };
81988
+ return { status: "dispatch_rejected", error: "state commit failed after reset dispatch" };
81883
81989
  }
81884
- return { status: "dispatched" };
81990
+ return { status: "dispatched", sessionID: newSessionID };
81885
81991
  }
81886
81992
  try {
81887
81993
  const promptResult = await injectContinuationPrompt(ctx, {
@@ -81896,7 +82002,7 @@ async function continueIteration(ctx, state3, options) {
81896
82002
  } catch (error) {
81897
82003
  return { status: "dispatch_rejected", error };
81898
82004
  }
81899
- return { status: "dispatched" };
82005
+ return { status: "dispatched", sessionID: options.previousSessionID };
81900
82006
  }
81901
82007
 
81902
82008
  // src/hooks/ralph-loop/pending-verification-handler.ts
@@ -82352,6 +82458,10 @@ function createRalphLoopEventHandler(ctx, options) {
82352
82458
  });
82353
82459
  return;
82354
82460
  }
82461
+ if (await isSessionActive(ctx.client, sessionID)) {
82462
+ log(`[${HOOK_NAME3}] Skipped: session became active during settle window`, { sessionID });
82463
+ return;
82464
+ }
82355
82465
  if (stateAfterSettle.verification_pending) {
82356
82466
  log(`[${HOOK_NAME3}] Skipped: state entered verification_pending during settle window`, { sessionID });
82357
82467
  return;
@@ -82390,11 +82500,21 @@ function createRalphLoopEventHandler(ctx, options) {
82390
82500
  })) {
82391
82501
  return;
82392
82502
  }
82393
- const committed = options.loopState.incrementIteration();
82503
+ const committed = options.loopState.incrementIteration({
82504
+ iteration: stateBeforeCommit.iteration,
82505
+ sessionID: result.sessionID
82506
+ });
82394
82507
  if (committed) {
82395
82508
  showIterationToast(ctx, committed);
82396
82509
  } else {
82397
82510
  log(`[${HOOK_NAME3}] Dispatch succeeded but iteration commit failed`, { sessionID });
82511
+ options.loopState.clear();
82512
+ showToastBestEffort3(ctx, {
82513
+ title: "Ralph Loop Failed",
82514
+ message: "Dispatch succeeded but iteration commit failed",
82515
+ variant: "warning",
82516
+ duration: 5000
82517
+ });
82398
82518
  }
82399
82519
  return;
82400
82520
  }
@@ -82485,6 +82605,10 @@ function createRalphLoopEventHandler(ctx, options) {
82485
82605
  });
82486
82606
  return;
82487
82607
  }
82608
+ if (await isSessionActive(ctx.client, sessionID)) {
82609
+ log(`[${HOOK_NAME3}] Skipped: session became active during settle window`, { sessionID });
82610
+ return;
82611
+ }
82488
82612
  if (stateAfterSettle.verification_pending) {
82489
82613
  log(`[${HOOK_NAME3}] Skipped: state entered verification_pending during settle window`, { sessionID });
82490
82614
  return;
@@ -82518,12 +82642,22 @@ function createRalphLoopEventHandler(ctx, options) {
82518
82642
  })) {
82519
82643
  return;
82520
82644
  }
82521
- const committed = options.loopState.incrementIteration();
82645
+ const committed = options.loopState.incrementIteration({
82646
+ iteration: stateBeforeCommit.iteration,
82647
+ sessionID: result.sessionID
82648
+ });
82522
82649
  if (committed) {
82523
82650
  showIterationToast(ctx, committed);
82524
82651
  runtimeErrorRetriedSessions.set(sessionID, committed.iteration);
82525
82652
  } else {
82526
82653
  log(`[${HOOK_NAME3}] Dispatch succeeded but iteration commit failed after runtime error`, { sessionID });
82654
+ options.loopState.clear();
82655
+ showToastBestEffort3(ctx, {
82656
+ title: "Ralph Loop Failed",
82657
+ message: "Dispatch succeeded but iteration commit failed",
82658
+ variant: "warning",
82659
+ duration: 5000
82660
+ });
82527
82661
  }
82528
82662
  return;
82529
82663
  }
@@ -82584,12 +82718,14 @@ function createRalphLoopHook(ctx, options) {
82584
82718
  if (!startSuccess || typeof loopOptions?.messageCountAtStart === "number") {
82585
82719
  return startSuccess;
82586
82720
  }
82721
+ const startedState = loopState.getState();
82722
+ const expectedStartedAt = startedState?.session_id === sessionID ? startedState.started_at : undefined;
82587
82723
  ctx.client.session.messages({
82588
82724
  path: { id: sessionID },
82589
82725
  query: { directory: ctx.directory }
82590
82726
  }).then((messagesResponse) => {
82591
82727
  const messageCountAtStart = getMessageCountFromResponse2(messagesResponse);
82592
- loopState.setMessageCountAtStart(sessionID, messageCountAtStart);
82728
+ loopState.setMessageCountAtStart(sessionID, messageCountAtStart, expectedStartedAt);
82593
82729
  }).catch(() => {});
82594
82730
  return startSuccess;
82595
82731
  },
@@ -89383,7 +89519,7 @@ function createProcessedCommandStore() {
89383
89519
 
89384
89520
  // src/hooks/auto-slash-command/hook.ts
89385
89521
  var COMMAND_EXECUTE_FALLBACK_DEDUP_TTL_MS = 100;
89386
- function isRecord13(value) {
89522
+ function isRecord14(value) {
89387
89523
  return typeof value === "object" && value !== null;
89388
89524
  }
89389
89525
  function getDeletedSessionID(properties) {
@@ -89401,7 +89537,7 @@ function getCommandExecutionEventID(input) {
89401
89537
  "commandId"
89402
89538
  ];
89403
89539
  const recordInput = input;
89404
- if (!isRecord13(recordInput)) {
89540
+ if (!isRecord14(recordInput)) {
89405
89541
  return null;
89406
89542
  }
89407
89543
  for (const key of candidateKeys) {
@@ -91211,12 +91347,6 @@ init_shared();
91211
91347
  init_agent_display_names();
91212
91348
  init_logger();
91213
91349
 
91214
- // src/hooks/shared/session-idle-settle.ts
91215
- var DEFAULT_SESSION_IDLE_SETTLE_MS = 150;
91216
- function settleAfterSessionIdle(ms = DEFAULT_SESSION_IDLE_SETTLE_MS) {
91217
- return ms > 0 ? new Promise((resolve14) => setTimeout(resolve14, ms)) : Promise.resolve();
91218
- }
91219
-
91220
91350
  // src/hooks/atlas/boulder-continuation-injector.ts
91221
91351
  init_logger();
91222
91352
  init_shared();
@@ -91500,6 +91630,10 @@ async function injectBoulderContinuation(input) {
91500
91630
  return "skipped_agent_unavailable";
91501
91631
  }
91502
91632
  try {
91633
+ if (await isSessionActive(ctx.client, sessionID)) {
91634
+ log(`[${HOOK_NAME7}] Skipped injection: session is active`, { sessionID });
91635
+ return "skipped_active_session";
91636
+ }
91503
91637
  log(`[${HOOK_NAME7}] Injecting boulder continuation`, { sessionID, planName, remaining });
91504
91638
  const promptContext = await resolveRecentPromptContextForSession(ctx, sessionID);
91505
91639
  const inheritedTools = resolveInheritedPromptTools(sessionID, promptContext.tools);
@@ -91512,7 +91646,7 @@ async function injectBoulderContinuation(input) {
91512
91646
  ...launchModel ? { model: launchModel } : {},
91513
91647
  ...launchVariant ? { variant: launchVariant } : {},
91514
91648
  ...inheritedTools ? { tools: inheritedTools } : {},
91515
- parts: [createInternalAgentTextPart(prompt)]
91649
+ parts: [createInternalAgentContinuationTextPart(prompt)]
91516
91650
  },
91517
91651
  query: { directory: ctx.directory }
91518
91652
  });
@@ -91760,11 +91894,15 @@ async function handleAtlasSessionIdle(input) {
91760
91894
  const prompt = BOULDER_COMPLETE_PROMPT.replace(/{PLAN_NAME}/g, work.plan_name).replace(/{ELAPSED_HUMAN}/g, elapsedHuman).replace(/{TASK_BREAKDOWN}/g, taskBreakdown.length > 0 ? taskBreakdown : "- (no task timings)");
91761
91895
  const atlasAgent = resolveRegisteredAgentName(boulderState.agent ?? (isAgentRegistered("atlas") ? "atlas" : undefined));
91762
91896
  if (atlasAgent && isAgentRegistered(atlasAgent)) {
91897
+ if (!await shouldPromptAfterSessionIdle(ctx.client, sessionID, options?.idleSettleMs)) {
91898
+ log(`[${HOOK_NAME7}] Boulder completion nudge skipped because session is active`, { sessionID });
91899
+ return;
91900
+ }
91763
91901
  await ctx.client.session.promptAsync({
91764
91902
  path: { id: sessionID },
91765
91903
  body: {
91766
91904
  agent: atlasAgent,
91767
- parts: [createInternalAgentTextPart(prompt)]
91905
+ parts: [createInternalAgentContinuationTextPart(prompt)]
91768
91906
  },
91769
91907
  query: { directory: ctx.directory }
91770
91908
  });
@@ -91842,7 +91980,10 @@ async function handleAtlasSessionIdle(input) {
91842
91980
  });
91843
91981
  return;
91844
91982
  }
91845
- await settleAfterSessionIdle(options?.idleSettleMs);
91983
+ if (!await shouldPromptAfterSessionIdle(ctx.client, sessionID, options?.idleSettleMs)) {
91984
+ log(`[${HOOK_NAME7}] Skipped: session became active during idle settle`, { sessionID });
91985
+ return;
91986
+ }
91846
91987
  await injectContinuation2({
91847
91988
  ctx,
91848
91989
  sessionID,
@@ -93483,7 +93624,7 @@ function createRecoveryLogic(ctx, getTailState) {
93483
93624
  agent: launchAgent ?? expectedPromptConfig.agent,
93484
93625
  ...model ? { model } : {},
93485
93626
  ...tools ? { tools } : {},
93486
- parts: [createInternalAgentTextPart(AGENT_RECOVERY_PROMPT)]
93627
+ parts: [createInternalAgentContinuationTextPart(AGENT_RECOVERY_PROMPT)]
93487
93628
  },
93488
93629
  query: { directory: ctx.directory }
93489
93630
  });
@@ -93889,16 +94030,16 @@ var THINKING_SUMMARY_MAX_CHARS = 500;
93889
94030
  function hasData(value) {
93890
94031
  return typeof value === "object" && value !== null && "data" in value;
93891
94032
  }
93892
- function isRecord14(value) {
94033
+ function isRecord15(value) {
93893
94034
  return typeof value === "object" && value !== null;
93894
94035
  }
93895
94036
  function getMessageInfo(value) {
93896
- if (!isRecord14(value))
94037
+ if (!isRecord15(value))
93897
94038
  return;
93898
- if (!isRecord14(value.info))
94039
+ if (!isRecord15(value.info))
93899
94040
  return;
93900
94041
  const info = value.info;
93901
- const modelValue = isRecord14(info.model) ? info.model : undefined;
94042
+ const modelValue = isRecord15(info.model) ? info.model : undefined;
93902
94043
  const model = modelValue && typeof modelValue.providerID === "string" && typeof modelValue.modelID === "string" ? {
93903
94044
  providerID: modelValue.providerID,
93904
94045
  modelID: modelValue.modelID,
@@ -93910,7 +94051,7 @@ function getMessageInfo(value) {
93910
94051
  model,
93911
94052
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
93912
94053
  modelID: typeof info.modelID === "string" ? info.modelID : undefined,
93913
- tools: isRecord14(info.tools) ? Object.entries(info.tools).reduce((acc, [key, value2]) => {
94054
+ tools: isRecord15(info.tools) ? Object.entries(info.tools).reduce((acc, [key, value2]) => {
93914
94055
  if (value2 === true || value2 === false || value2 === "allow" || value2 === "deny" || value2 === "ask") {
93915
94056
  acc[key] = value2;
93916
94057
  }
@@ -93919,11 +94060,11 @@ function getMessageInfo(value) {
93919
94060
  };
93920
94061
  }
93921
94062
  function getMessageParts(value) {
93922
- if (!isRecord14(value))
94063
+ if (!isRecord15(value))
93923
94064
  return [];
93924
94065
  if (!Array.isArray(value.parts))
93925
94066
  return [];
93926
- return value.parts.filter(isRecord14).map((part) => ({
94067
+ return value.parts.filter(isRecord15).map((part) => ({
93927
94068
  type: typeof part.type === "string" ? part.type : undefined,
93928
94069
  text: typeof part.text === "string" ? part.text : undefined,
93929
94070
  thinking: typeof part.thinking === "string" ? part.thinking : undefined
@@ -94108,7 +94249,10 @@ function createUnstableAgentBabysitterHook(ctx, options) {
94108
94249
  try {
94109
94250
  const launchModel = model ? { providerID: model.providerID, modelID: model.modelID } : undefined;
94110
94251
  const launchVariant = model?.variant;
94111
- await settleAfterSessionIdle(options.idleSettleMs);
94252
+ if (!await shouldPromptAfterSessionIdle(ctx.client, mainSessionID, options.idleSettleMs)) {
94253
+ log(`[${HOOK_NAME10}] Reminder skipped because main session is active`, { taskId: task.id, sessionID: mainSessionID });
94254
+ continue;
94255
+ }
94112
94256
  await ctx.client.session.promptAsync({
94113
94257
  path: { id: mainSessionID },
94114
94258
  body: {
@@ -94736,11 +94880,11 @@ function buildRetryModelPayload(model, agentSettings) {
94736
94880
  }
94737
94881
 
94738
94882
  // src/hooks/runtime-fallback/session-messages.ts
94739
- function isRecord15(value) {
94883
+ function isRecord16(value) {
94740
94884
  return typeof value === "object" && value !== null;
94741
94885
  }
94742
94886
  function isSessionMessage(value) {
94743
- return isRecord15(value);
94887
+ return isRecord16(value);
94744
94888
  }
94745
94889
  function isSessionMessageArray(value) {
94746
94890
  return Array.isArray(value) && value.every(isSessionMessage);
@@ -94749,7 +94893,7 @@ function extractSessionMessages(messagesResponse) {
94749
94893
  if (isSessionMessageArray(messagesResponse)) {
94750
94894
  return messagesResponse;
94751
94895
  }
94752
- if (!isRecord15(messagesResponse)) {
94896
+ if (!isRecord16(messagesResponse)) {
94753
94897
  return;
94754
94898
  }
94755
94899
  const data = messagesResponse.data;
@@ -95301,6 +95445,18 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
95301
95445
 
95302
95446
  // src/hooks/runtime-fallback/event-handler.ts
95303
95447
  init_event_session_id();
95448
+ function resolveEventModel(props) {
95449
+ const model = props?.model;
95450
+ if (typeof model === "string") {
95451
+ return model;
95452
+ }
95453
+ const providerID = props?.providerID;
95454
+ const modelID = props?.modelID;
95455
+ if (typeof providerID === "string" && typeof modelID === "string") {
95456
+ return `${providerID}/${modelID}`;
95457
+ }
95458
+ return;
95459
+ }
95304
95460
  function createEventHandler(deps, helpers) {
95305
95461
  const { config, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, sessionStatusRetryKeys } = deps;
95306
95462
  const sessionStatusHandler = createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys);
@@ -95405,6 +95561,18 @@ function createEventHandler(deps, helpers) {
95405
95561
  });
95406
95562
  return;
95407
95563
  }
95564
+ if (sessionAwaitingFallbackResult.has(sessionID)) {
95565
+ const pendingFallbackModel = sessionStates.get(sessionID)?.pendingFallbackModel;
95566
+ const eventModel = resolveEventModel(props);
95567
+ if (!pendingFallbackModel || eventModel !== pendingFallbackModel) {
95568
+ log(`[${HOOK_NAME11}] session.error skipped - awaiting fallback result`, {
95569
+ sessionID,
95570
+ pendingFallbackModel,
95571
+ eventModel
95572
+ });
95573
+ return;
95574
+ }
95575
+ }
95408
95576
  sessionAwaitingFallbackResult.delete(sessionID);
95409
95577
  helpers.clearSessionFallbackTimeout(sessionID);
95410
95578
  log(`[${HOOK_NAME11}] session.error received`, {
@@ -101171,7 +101339,7 @@ function normalizeToolArgSchemas(toolDefinition) {
101171
101339
  return toolDefinition;
101172
101340
  }
101173
101341
  var UNSUPPORTED_SCHEMA_KEYWORDS = new Set(["contentEncoding", "contentMediaType"]);
101174
- function isRecord16(value) {
101342
+ function isRecord17(value) {
101175
101343
  return typeof value === "object" && value !== null && !Array.isArray(value);
101176
101344
  }
101177
101345
  function normalizeJsonSchemaRef(value) {
@@ -101184,7 +101352,7 @@ function sanitizeJsonSchema(value, depth = 0, isPropertyName = false) {
101184
101352
  if (Array.isArray(value)) {
101185
101353
  return value.map((item) => sanitizeJsonSchema(item, depth + 1, false));
101186
101354
  }
101187
- if (!isRecord16(value)) {
101355
+ if (!isRecord17(value)) {
101188
101356
  return value;
101189
101357
  }
101190
101358
  const sanitized = {};
@@ -102344,6 +102512,9 @@ var BLOCKED_TMUX_SUBCOMMANDS = [
102344
102512
  "pipe-pane",
102345
102513
  "pipep"
102346
102514
  ];
102515
+ var PROHIBITED_TMUX_SUBCOMMANDS = [
102516
+ "kill-server"
102517
+ ];
102347
102518
  var INTERACTIVE_BASH_DESCRIPTION = `WARNING: This is TMUX ONLY. Pass tmux subcommands directly (without 'tmux' prefix).
102348
102519
 
102349
102520
  Examples: new-session -d -s omo-dev, send-keys -t omo-dev "vim" Enter
@@ -102352,6 +102523,7 @@ For TUI apps needing ongoing interaction (vim, htop, pudb). One-shot commands \u
102352
102523
 
102353
102524
  // src/tools/interactive-bash/tools.ts
102354
102525
  init_tmux_path_resolver();
102526
+ var GLOBAL_TMUX_OPTIONS_WITH_ARGS = new Set(["-L", "-S", "-f", "-c", "-T"]);
102355
102527
  function resolveTmuxExecutable2(tmuxPath2) {
102356
102528
  if (!isCmuxCompatEnvironment()) {
102357
102529
  return [tmuxPath2];
@@ -102396,6 +102568,71 @@ function tokenizeCommand2(cmd) {
102396
102568
  tokens.push(current);
102397
102569
  return tokens;
102398
102570
  }
102571
+ function findSubcommandIndex(parts) {
102572
+ let index = 0;
102573
+ while (index < parts.length) {
102574
+ const part = parts[index] ?? "";
102575
+ if (part === "--") {
102576
+ return index + 1 < parts.length ? index + 1 : -1;
102577
+ }
102578
+ if (GLOBAL_TMUX_OPTIONS_WITH_ARGS.has(part)) {
102579
+ index += 2;
102580
+ continue;
102581
+ }
102582
+ if (part.startsWith("-")) {
102583
+ index++;
102584
+ continue;
102585
+ }
102586
+ return index;
102587
+ }
102588
+ return -1;
102589
+ }
102590
+ function getTargetSessionName(parts) {
102591
+ const sessionIdx = parts.findIndex((p) => p === "-t" || p.startsWith("-t"));
102592
+ if (sessionIdx === -1) {
102593
+ return "omo-session";
102594
+ }
102595
+ const sessionToken = parts[sessionIdx] ?? "";
102596
+ const nextToken = parts[sessionIdx + 1];
102597
+ if (sessionToken === "-t" && nextToken) {
102598
+ return nextToken;
102599
+ }
102600
+ if (sessionToken.startsWith("-t")) {
102601
+ return sessionToken.slice(2);
102602
+ }
102603
+ return "omo-session";
102604
+ }
102605
+ function buildBlockedTmuxCommandMessage(command, parts) {
102606
+ const sessionName = getTargetSessionName(parts);
102607
+ return `Error: '${command}' is blocked in interactive_bash.
102608
+
102609
+ **USE BASH TOOL INSTEAD:**
102610
+
102611
+ \`\`\`bash
102612
+ # Capture terminal output
102613
+ tmux capture-pane -p -t ${sessionName}
102614
+
102615
+ # Or capture with history (last 1000 lines)
102616
+ tmux capture-pane -p -t ${sessionName} -S -1000
102617
+ \`\`\`
102618
+
102619
+ The Bash tool can execute these commands directly. Do NOT retry with interactive_bash.`;
102620
+ }
102621
+ function buildProhibitedTmuxCommandMessage(command) {
102622
+ return `Error: '${command}' is prohibited in interactive_bash.
102623
+
102624
+ NEVER EVER run tmux kill-server from interactive_bash.
102625
+
102626
+ It terminates the entire tmux server, destroying every tmux session and pane that the user, Codex, or other agents may be using.
102627
+
102628
+ Use scoped cleanup only:
102629
+
102630
+ \`\`\`bash
102631
+ tmux kill-session -t <session-name>
102632
+ \`\`\`
102633
+
102634
+ If you created an omo-* session, kill only that exact session. Do not retry kill-server with Bash or any other tool.`;
102635
+ }
102399
102636
  var interactive_bash = tool({
102400
102637
  description: INTERACTIVE_BASH_DESCRIPTION,
102401
102638
  args: {
@@ -102408,30 +102645,14 @@ var interactive_bash = tool({
102408
102645
  if (parts.length === 0) {
102409
102646
  return "Error: Empty tmux command";
102410
102647
  }
102411
- const subcommand = parts[0].toLowerCase();
102648
+ const subcommandIndex = findSubcommandIndex(parts);
102649
+ const rawSubcommand = subcommandIndex === -1 ? "" : parts[subcommandIndex];
102650
+ const subcommand = rawSubcommand.toLowerCase();
102651
+ if (PROHIBITED_TMUX_SUBCOMMANDS.includes(subcommand)) {
102652
+ return buildProhibitedTmuxCommandMessage(rawSubcommand);
102653
+ }
102412
102654
  if (BLOCKED_TMUX_SUBCOMMANDS.includes(subcommand)) {
102413
- const sessionIdx = parts.findIndex((p) => p === "-t" || p.startsWith("-t"));
102414
- let sessionName = "omo-session";
102415
- if (sessionIdx !== -1) {
102416
- if (parts[sessionIdx] === "-t" && parts[sessionIdx + 1]) {
102417
- sessionName = parts[sessionIdx + 1];
102418
- } else if (parts[sessionIdx].startsWith("-t")) {
102419
- sessionName = parts[sessionIdx].slice(2);
102420
- }
102421
- }
102422
- return `Error: '${parts[0]}' is blocked in interactive_bash.
102423
-
102424
- **USE BASH TOOL INSTEAD:**
102425
-
102426
- \`\`\`bash
102427
- # Capture terminal output
102428
- tmux capture-pane -p -t ${sessionName}
102429
-
102430
- # Or capture with history (last 1000 lines)
102431
- tmux capture-pane -p -t ${sessionName} -S -1000
102432
- \`\`\`
102433
-
102434
- The Bash tool can execute these commands directly. Do NOT retry with interactive_bash.`;
102655
+ return buildBlockedTmuxCommandMessage(rawSubcommand, parts);
102435
102656
  }
102436
102657
  const proc = spawnWithWindowsHide([...resolveTmuxExecutable2(tmuxPath2), ...parts], {
102437
102658
  stdout: "pipe",
@@ -102902,7 +103123,7 @@ async function formatFullSession(task, client2, options) {
102902
103123
  }
102903
103124
 
102904
103125
  // src/features/background-agent/error-classifier.ts
102905
- function isRecord17(value) {
103126
+ function isRecord18(value) {
102906
103127
  return typeof value === "object" && value !== null;
102907
103128
  }
102908
103129
  function isAbortedSessionError(error) {
@@ -102928,7 +103149,7 @@ function getErrorText(error) {
102928
103149
  return "";
102929
103150
  }
102930
103151
  function extractErrorName2(error) {
102931
- if (isRecord17(error) && typeof error["name"] === "string")
103152
+ if (isRecord18(error) && typeof error["name"] === "string")
102932
103153
  return error["name"];
102933
103154
  if (error instanceof Error)
102934
103155
  return error.name;
@@ -102939,11 +103160,11 @@ function extractErrorMessage(error) {
102939
103160
  return;
102940
103161
  if (typeof error === "string")
102941
103162
  return error;
102942
- if (isRecord17(error)) {
103163
+ if (isRecord18(error)) {
102943
103164
  const dataRaw = error["data"];
102944
103165
  const candidates = [
102945
103166
  dataRaw,
102946
- isRecord17(dataRaw) ? dataRaw["error"] : undefined,
103167
+ isRecord18(dataRaw) ? dataRaw["error"] : undefined,
102947
103168
  error["error"],
102948
103169
  error["cause"],
102949
103170
  error
@@ -102951,7 +103172,7 @@ function extractErrorMessage(error) {
102951
103172
  for (const candidate of candidates) {
102952
103173
  if (typeof candidate === "string" && candidate.length > 0)
102953
103174
  return candidate;
102954
- if (isRecord17(candidate) && typeof candidate["message"] === "string" && candidate["message"].length > 0) {
103175
+ if (isRecord18(candidate) && typeof candidate["message"] === "string" && candidate["message"].length > 0) {
102955
103176
  return candidate["message"];
102956
103177
  }
102957
103178
  }
@@ -102966,10 +103187,10 @@ function extractErrorMessage(error) {
102966
103187
  }
102967
103188
  function getSessionErrorMessage(properties) {
102968
103189
  const errorRaw = properties["error"];
102969
- if (!isRecord17(errorRaw))
103190
+ if (!isRecord18(errorRaw))
102970
103191
  return;
102971
103192
  const dataRaw = errorRaw["data"];
102972
- if (isRecord17(dataRaw)) {
103193
+ if (isRecord18(dataRaw)) {
102973
103194
  const message2 = dataRaw["message"];
102974
103195
  if (typeof message2 === "string")
102975
103196
  return message2;
@@ -103745,11 +103966,12 @@ function mergeWithClaudeCodeAgents(serverAgents, directory) {
103745
103966
  const toAgentInfoList = (record) => Object.entries(record).map(([name, config2]) => ({
103746
103967
  name,
103747
103968
  mode: config2.mode,
103969
+ hidden: config2.hidden,
103748
103970
  model: config2.model
103749
103971
  }));
103750
103972
  const mergedAgentMap = new Map;
103751
103973
  const addIfAbsent = (agent) => {
103752
- const key = agent.name.toLowerCase();
103974
+ const key = stripAgentListSortPrefix(agent.name).trim().toLowerCase();
103753
103975
  if (!mergedAgentMap.has(key)) {
103754
103976
  mergedAgentMap.set(key, agent);
103755
103977
  }
@@ -103782,10 +104004,10 @@ function findPrimaryAgentMatch(agents, requestedAgentName) {
103782
104004
  return agents.find((agent) => agent.mode === "primary" && matchesRequestedAgent(agent, requestedAgentName));
103783
104005
  }
103784
104006
  function findCallableAgentMatch(agents, requestedAgentName) {
103785
- return agents.find((agent) => isTaskCallableAgentMode(agent.mode) && matchesRequestedAgent(agent, requestedAgentName));
104007
+ return agents.find((agent) => isTaskCallableAgentMode(agent.mode) && agent.hidden !== true && matchesRequestedAgent(agent, requestedAgentName));
103786
104008
  }
103787
104009
  function listCallableAgentNames(agents) {
103788
- return agents.filter((agent) => isTaskCallableAgentMode(agent.mode)).map((agent) => stripAgentListSortPrefix(agent.name)).sort().join(", ");
104010
+ return agents.filter((agent) => isTaskCallableAgentMode(agent.mode) && agent.hidden !== true).map((agent) => stripAgentListSortPrefix(agent.name)).sort().join(", ");
103789
104011
  }
103790
104012
 
103791
104013
  // src/tools/call-omo-agent/background-executor.ts
@@ -105345,7 +105567,7 @@ init_logger();
105345
105567
  init_shared();
105346
105568
  var NON_TERMINAL_FINISH_REASONS = new Set(["tool-calls", "unknown"]);
105347
105569
  var PENDING_TOOL_PART_TYPES = new Set(["tool", "tool_use", "tool-call"]);
105348
- var ACTIVE_SESSION_STATUSES = new Set(["busy", "retry", "running"]);
105570
+ var ACTIVE_SESSION_STATUSES2 = new Set(["busy", "retry", "running"]);
105349
105571
  function wait(milliseconds) {
105350
105572
  const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
105351
105573
  const typedArray = new Int32Array(sharedBuffer);
@@ -105363,7 +105585,7 @@ function abortSyncSession(client2, sessionID, reason) {
105363
105585
  });
105364
105586
  }
105365
105587
  function isActiveSessionStatus(status) {
105366
- return status !== undefined && ACTIVE_SESSION_STATUSES.has(status.type);
105588
+ return status !== undefined && ACTIVE_SESSION_STATUSES2.has(status.type);
105367
105589
  }
105368
105590
  async function fetchSessionMessages(client2, sessionID) {
105369
105591
  const messagesResult = await client2.session.messages({ path: { id: sessionID } });
@@ -111697,10 +111919,10 @@ async function verifySessionExists(client2, sessionID, directory) {
111697
111919
 
111698
111920
  // src/features/background-agent/session-status-classifier.ts
111699
111921
  init_shared();
111700
- var ACTIVE_SESSION_STATUSES2 = new Set(["busy", "retry", "running"]);
111922
+ var ACTIVE_SESSION_STATUSES3 = new Set(["busy", "retry", "running"]);
111701
111923
  var KNOWN_TERMINAL_STATUSES = new Set(["idle", "interrupted"]);
111702
111924
  function isActiveSessionStatus2(type2) {
111703
- if (ACTIVE_SESSION_STATUSES2.has(type2)) {
111925
+ if (ACTIVE_SESSION_STATUSES3.has(type2)) {
111704
111926
  return true;
111705
111927
  }
111706
111928
  if (!KNOWN_TERMINAL_STATUSES.has(type2)) {
@@ -112000,6 +112222,7 @@ function createSubagentDepthLimitError(input) {
112000
112222
  }
112001
112223
 
112002
112224
  // src/features/background-agent/manager.ts
112225
+ var PENDING_PARENT_WAKE_RETRY_MS = 1000;
112003
112226
  function resolveMessagePartInfo(properties) {
112004
112227
  if (!properties || typeof properties !== "object") {
112005
112228
  return;
@@ -112062,6 +112285,8 @@ class BackgroundManager {
112062
112285
  completedTaskSummaries = new Map;
112063
112286
  idleDeferralTimers = new Map;
112064
112287
  notificationQueueByParent = new Map;
112288
+ pendingParentWakes = new Map;
112289
+ pendingParentWakeTimers = new Map;
112065
112290
  observedOutputSessions = new Set;
112066
112291
  observedIncompleteTodosBySession = new Map;
112067
112292
  rootDescendantCounts;
@@ -113031,6 +113256,12 @@ The fallback retry session is now created and can be inspected directly.
113031
113256
  if (event.type === "session.idle") {
113032
113257
  if (!props || typeof props !== "object")
113033
113258
  return;
113259
+ const sessionID = resolveSessionEventID(props);
113260
+ if (sessionID) {
113261
+ this.enqueueNotificationForParent(sessionID, () => this.flushPendingParentWake(sessionID)).catch((error) => {
113262
+ log("[background-agent] Failed to flush pending parent wake:", { sessionID, error });
113263
+ });
113264
+ }
113034
113265
  handleSessionIdleBackgroundEvent({
113035
113266
  properties: props,
113036
113267
  findBySession: (id) => {
@@ -113041,7 +113272,7 @@ The fallback retry session is now created and can be inspected directly.
113041
113272
  validateSessionHasOutput: (id) => this.validateSessionHasOutput(id),
113042
113273
  checkSessionTodos: (id) => this.checkSessionTodos(id),
113043
113274
  tryCompleteTask: (task, source) => this.tryCompleteTask(task, source),
113044
- emitIdleEvent: (sessionID) => this.handleEvent({ type: "session.idle", properties: { sessionID } })
113275
+ emitIdleEvent: (sessionID2) => this.handleEvent({ type: "session.idle", properties: { sessionID: sessionID2 } })
113045
113276
  });
113046
113277
  }
113047
113278
  if (event.type === "session.error") {
@@ -113610,7 +113841,7 @@ ${originalText}`;
113610
113841
  }, this.directory);
113611
113842
  const messages = normalizeSDKResponse(messagesResp, []);
113612
113843
  promptContext = resolvePromptContextFromSessionMessages(messages, task.parentSessionId);
113613
- const normalizedTools = isRecord17(promptContext?.tools) ? normalizePromptTools(promptContext.tools) : undefined;
113844
+ const normalizedTools = isRecord18(promptContext?.tools) ? normalizePromptTools(promptContext.tools) : undefined;
113614
113845
  if (promptContext?.agent || promptContext?.model || normalizedTools) {
113615
113846
  agent = promptContext?.agent ?? task.parentAgent;
113616
113847
  model = promptContext?.model?.providerID && promptContext.model.modelID ? { providerID: promptContext.model.providerID, modelID: promptContext.model.modelID } : undefined;
@@ -113644,30 +113875,41 @@ ${originalText}`;
113644
113875
  ...variant !== undefined ? { variant } : {},
113645
113876
  ...resolvedTools ? { tools: resolvedTools } : {}
113646
113877
  };
113647
- try {
113648
- await promptAsyncInDirectory(this.client, {
113649
- path: { id: task.parentSessionId },
113650
- body: {
113651
- noReply: !shouldReply,
113652
- ...parentPromptContext,
113653
- parts: [createInternalAgentTextPart(notification2)]
113654
- }
113655
- }, this.directory);
113656
- log("[background-agent] Sent notification to parent session:", {
113878
+ const shouldDeferNotification = await this.isSessionActive(task.parentSessionId);
113879
+ if (shouldDeferNotification) {
113880
+ this.queuePendingParentWake(task.parentSessionId, notification2, parentPromptContext, shouldReply);
113881
+ log("[background-agent] Deferred notification until parent session is idle:", {
113657
113882
  taskId: task.id,
113658
113883
  allComplete,
113659
113884
  isTaskFailure,
113660
- noReply: !shouldReply
113885
+ shouldReply
113661
113886
  });
113662
- } catch (error) {
113663
- if (isAbortedSessionError(error)) {
113664
- log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
113887
+ } else {
113888
+ try {
113889
+ await promptAsyncInDirectory(this.client, {
113890
+ path: { id: task.parentSessionId },
113891
+ body: {
113892
+ noReply: !shouldReply,
113893
+ ...parentPromptContext,
113894
+ parts: [createInternalAgentTextPart(notification2)]
113895
+ }
113896
+ }, this.directory);
113897
+ log("[background-agent] Sent notification to parent session:", {
113665
113898
  taskId: task.id,
113666
- parentSessionID: task.parentSessionId
113899
+ allComplete,
113900
+ isTaskFailure,
113901
+ noReply: !shouldReply
113667
113902
  });
113668
- this.queuePendingNotification(task.parentSessionId, notification2);
113669
- } else {
113670
- log("[background-agent] Failed to send notification:", error);
113903
+ } catch (error) {
113904
+ if (isAbortedSessionError(error)) {
113905
+ log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
113906
+ taskId: task.id,
113907
+ parentSessionID: task.parentSessionId
113908
+ });
113909
+ this.queuePendingNotification(task.parentSessionId, notification2);
113910
+ } else {
113911
+ log("[background-agent] Failed to send notification:", error);
113912
+ }
113671
113913
  }
113672
113914
  }
113673
113915
  } else {
@@ -113680,6 +113922,80 @@ ${originalText}`;
113680
113922
  this.scheduleTaskRemoval(task.id);
113681
113923
  }
113682
113924
  }
113925
+ async isSessionActive(sessionID) {
113926
+ return isSessionActive(this.client, sessionID);
113927
+ }
113928
+ queuePendingParentWake(sessionID, notification2, promptContext, shouldReply) {
113929
+ const pendingWake = this.pendingParentWakes.get(sessionID);
113930
+ if (pendingWake) {
113931
+ pendingWake.notifications.push(notification2);
113932
+ pendingWake.promptContext = promptContext;
113933
+ pendingWake.shouldReply = pendingWake.shouldReply || shouldReply;
113934
+ } else {
113935
+ this.pendingParentWakes.set(sessionID, {
113936
+ promptContext,
113937
+ notifications: [notification2],
113938
+ shouldReply
113939
+ });
113940
+ }
113941
+ this.schedulePendingParentWakeFlush(sessionID);
113942
+ }
113943
+ async flushPendingParentWake(sessionID) {
113944
+ const pendingWake = this.pendingParentWakes.get(sessionID);
113945
+ if (!pendingWake) {
113946
+ this.clearPendingParentWakeTimer(sessionID);
113947
+ return;
113948
+ }
113949
+ if (await this.isSessionActive(sessionID)) {
113950
+ this.schedulePendingParentWakeFlush(sessionID);
113951
+ return;
113952
+ }
113953
+ this.pendingParentWakes.delete(sessionID);
113954
+ this.clearPendingParentWakeTimer(sessionID);
113955
+ await settleAfterSessionIdle();
113956
+ if (await this.isSessionActive(sessionID)) {
113957
+ this.pendingParentWakes.set(sessionID, pendingWake);
113958
+ this.schedulePendingParentWakeFlush(sessionID);
113959
+ return;
113960
+ }
113961
+ const notificationContent = pendingWake.notifications.join(`
113962
+
113963
+ `);
113964
+ try {
113965
+ await promptAsyncInDirectory(this.client, {
113966
+ path: { id: sessionID },
113967
+ body: {
113968
+ noReply: !pendingWake.shouldReply,
113969
+ ...pendingWake.promptContext,
113970
+ parts: [createInternalAgentTextPart(notificationContent)]
113971
+ }
113972
+ }, this.directory);
113973
+ log("[background-agent] Sent deferred parent wake:", { sessionID });
113974
+ } catch (error) {
113975
+ this.queuePendingNotification(sessionID, notificationContent);
113976
+ log("[background-agent] Failed to send deferred parent wake:", { sessionID, error });
113977
+ }
113978
+ }
113979
+ schedulePendingParentWakeFlush(sessionID) {
113980
+ if (this.pendingParentWakeTimers.has(sessionID)) {
113981
+ return;
113982
+ }
113983
+ const timer = setTimeout(() => {
113984
+ this.pendingParentWakeTimers.delete(sessionID);
113985
+ this.enqueueNotificationForParent(sessionID, () => this.flushPendingParentWake(sessionID)).catch((error) => {
113986
+ log("[background-agent] Failed to retry pending parent wake:", { sessionID, error });
113987
+ });
113988
+ }, PENDING_PARENT_WAKE_RETRY_MS);
113989
+ this.pendingParentWakeTimers.set(sessionID, timer);
113990
+ }
113991
+ clearPendingParentWakeTimer(sessionID) {
113992
+ const timer = this.pendingParentWakeTimers.get(sessionID);
113993
+ if (!timer) {
113994
+ return;
113995
+ }
113996
+ clearTimeout(timer);
113997
+ this.pendingParentWakeTimers.delete(sessionID);
113998
+ }
113683
113999
  hasRunningTasks() {
113684
114000
  for (const task of this.tasks.values()) {
113685
114001
  if (task.status === "running")
@@ -113949,6 +114265,10 @@ ${originalText}`;
113949
114265
  clearTimeout(timer);
113950
114266
  }
113951
114267
  this.idleDeferralTimers.clear();
114268
+ for (const timer of this.pendingParentWakeTimers.values()) {
114269
+ clearTimeout(timer);
114270
+ }
114271
+ this.pendingParentWakeTimers.clear();
113952
114272
  for (const sessionID of trackedSessionIDs) {
113953
114273
  subagentSessions.delete(sessionID);
113954
114274
  SessionCategoryRegistry.remove(sessionID);
@@ -113959,6 +114279,7 @@ ${originalText}`;
113959
114279
  this.notifications.clear();
113960
114280
  this.pendingNotifications.clear();
113961
114281
  this.pendingByParent.clear();
114282
+ this.pendingParentWakes.clear();
113962
114283
  this.notificationQueueByParent.clear();
113963
114284
  this.rootDescendantCounts.clear();
113964
114285
  this.queuesByKey.clear();
@@ -114213,7 +114534,7 @@ async function getOrRegisterClient(options) {
114213
114534
  }
114214
114535
  }
114215
114536
  function parseRegistrationResponse(data) {
114216
- if (!isRecord18(data))
114537
+ if (!isRecord19(data))
114217
114538
  return null;
114218
114539
  const clientId = data.client_id;
114219
114540
  if (typeof clientId !== "string" || clientId.length === 0)
@@ -114224,7 +114545,7 @@ function parseRegistrationResponse(data) {
114224
114545
  }
114225
114546
  return { clientId };
114226
114547
  }
114227
- function isRecord18(value) {
114548
+ function isRecord19(value) {
114228
114549
  return typeof value === "object" && value !== null;
114229
114550
  }
114230
114551
 
@@ -137318,11 +137639,11 @@ async function createTools(args) {
137318
137639
  // src/plugin/chat-params.ts
137319
137640
  init_shared();
137320
137641
  var SAFE_MAX_OUTPUT_TOKENS_FALLBACK = 4096;
137321
- function isRecord19(value) {
137642
+ function isRecord20(value) {
137322
137643
  return typeof value === "object" && value !== null;
137323
137644
  }
137324
137645
  function buildChatParamsInput(raw) {
137325
- if (!isRecord19(raw))
137646
+ if (!isRecord20(raw))
137326
137647
  return null;
137327
137648
  const sessionID = raw.sessionID;
137328
137649
  const agent = raw.agent;
@@ -137331,16 +137652,16 @@ function buildChatParamsInput(raw) {
137331
137652
  const message = raw.message;
137332
137653
  if (typeof sessionID !== "string")
137333
137654
  return null;
137334
- if (!isRecord19(model))
137655
+ if (!isRecord20(model))
137335
137656
  return null;
137336
- if (!isRecord19(provider))
137657
+ if (!isRecord20(provider))
137337
137658
  return null;
137338
- if (!isRecord19(message))
137659
+ if (!isRecord20(message))
137339
137660
  return null;
137340
137661
  let agentName;
137341
137662
  if (typeof agent === "string") {
137342
137663
  agentName = agent;
137343
- } else if (isRecord19(agent)) {
137664
+ } else if (isRecord20(agent)) {
137344
137665
  const name = agent.name;
137345
137666
  if (typeof name === "string") {
137346
137667
  agentName = name;
@@ -137367,12 +137688,12 @@ function buildChatParamsInput(raw) {
137367
137688
  };
137368
137689
  }
137369
137690
  function isChatParamsOutput(raw) {
137370
- if (!isRecord19(raw))
137691
+ if (!isRecord20(raw))
137371
137692
  return false;
137372
- if (!isRecord19(raw.options)) {
137693
+ if (!isRecord20(raw.options)) {
137373
137694
  raw.options = {};
137374
137695
  }
137375
- return isRecord19(raw.options);
137696
+ return isRecord20(raw.options);
137376
137697
  }
137377
137698
  function createChatParamsHandler(args) {
137378
137699
  return async (input, output) => {
@@ -137412,7 +137733,7 @@ function createChatParamsHandler(args) {
137412
137733
  temperature: typeof output.temperature === "number" ? output.temperature : undefined,
137413
137734
  topP: typeof output.topP === "number" ? output.topP : undefined,
137414
137735
  maxTokens: typeof output.maxOutputTokens === "number" ? output.maxOutputTokens : undefined,
137415
- thinking: isRecord19(output.options.thinking) ? output.options.thinking : undefined
137736
+ thinking: isRecord20(output.options.thinking) ? output.options.thinking : undefined
137416
137737
  },
137417
137738
  capabilities
137418
137739
  });
@@ -137469,20 +137790,20 @@ function createChatParamsHandler(args) {
137469
137790
  init_shared();
137470
137791
  var INTERNAL_MARKER_CACHE_LIMIT = 1000;
137471
137792
  var internalMarkerCache = new Map;
137472
- function isRecord20(value) {
137793
+ function isRecord21(value) {
137473
137794
  return typeof value === "object" && value !== null;
137474
137795
  }
137475
137796
  function buildChatHeadersInput(raw) {
137476
- if (!isRecord20(raw))
137797
+ if (!isRecord21(raw))
137477
137798
  return null;
137478
137799
  const sessionID = raw.sessionID;
137479
137800
  const provider = raw.provider;
137480
137801
  const message = raw.message;
137481
137802
  if (typeof sessionID !== "string")
137482
137803
  return null;
137483
- if (!isRecord20(provider) || typeof provider.id !== "string")
137804
+ if (!isRecord21(provider) || typeof provider.id !== "string")
137484
137805
  return null;
137485
- if (!isRecord20(message))
137806
+ if (!isRecord21(message))
137486
137807
  return null;
137487
137808
  return {
137488
137809
  sessionID,
@@ -137494,12 +137815,12 @@ function buildChatHeadersInput(raw) {
137494
137815
  };
137495
137816
  }
137496
137817
  function isChatHeadersOutput(raw) {
137497
- if (!isRecord20(raw))
137818
+ if (!isRecord21(raw))
137498
137819
  return false;
137499
- if (!isRecord20(raw.headers)) {
137820
+ if (!isRecord21(raw.headers)) {
137500
137821
  raw.headers = {};
137501
137822
  }
137502
- return isRecord20(raw.headers);
137823
+ return isRecord21(raw.headers);
137503
137824
  }
137504
137825
  function isCopilotProvider(providerID) {
137505
137826
  return providerID === "github-copilot" || providerID === "github-copilot-enterprise";
@@ -137515,7 +137836,7 @@ async function hasInternalMarker(client2, sessionID, messageID) {
137515
137836
  path: { id: sessionID, messageID }
137516
137837
  });
137517
137838
  const data = response.data;
137518
- if (!isRecord20(data) || !Array.isArray(data.parts)) {
137839
+ if (!isRecord21(data) || !Array.isArray(data.parts)) {
137519
137840
  internalMarkerCache.set(cacheKey, false);
137520
137841
  if (internalMarkerCache.size > INTERNAL_MARKER_CACHE_LIMIT) {
137521
137842
  internalMarkerCache.clear();
@@ -137523,7 +137844,7 @@ async function hasInternalMarker(client2, sessionID, messageID) {
137523
137844
  return false;
137524
137845
  }
137525
137846
  const hasMarker = data.parts.some((part) => {
137526
- if (!isRecord20(part) || part.type !== "text" || typeof part.text !== "string") {
137847
+ if (!isRecord21(part) || part.type !== "text" || typeof part.text !== "string") {
137527
137848
  return false;
137528
137849
  }
137529
137850
  return part.text.includes(OMO_INTERNAL_INITIATOR_MARKER);
@@ -137560,8 +137881,8 @@ function createChatHeadersHandler(args) {
137560
137881
  return;
137561
137882
  if (!isCopilotProvider(normalizedInput.provider.id))
137562
137883
  return;
137563
- const model = isRecord20(input) && isRecord20(input.model) ? input.model : undefined;
137564
- const api = model && isRecord20(model.api) ? model.api : undefined;
137884
+ const model = isRecord21(input) && isRecord21(input.model) ? input.model : undefined;
137885
+ const api = model && isRecord21(model.api) ? model.api : undefined;
137565
137886
  if (api?.npm === "@ai-sdk/github-copilot")
137566
137887
  return;
137567
137888
  if (!await isOmoInternalMessage(normalizedInput, ctx.client))
@@ -138258,7 +138579,16 @@ function createTeamIdleWakeHint(ctx, config2, options) {
138258
138579
  return;
138259
138580
  }
138260
138581
  applyMemberSessionRouting(sessionID, memberEntry);
138261
- await settleAfterSessionIdle(options?.idleSettleMs);
138582
+ if (!await shouldPromptAfterSessionIdle(ctx.client, sessionID, options?.idleSettleMs)) {
138583
+ log("team idle wake hint skipped because session is active", {
138584
+ event: "team-mode-idle-wake-hint-active-session",
138585
+ teamRunId: runtimeState.teamRunId,
138586
+ memberName: memberEntry.name,
138587
+ sessionID,
138588
+ unreadCount: unreadMessages.length
138589
+ });
138590
+ return;
138591
+ }
138262
138592
  await ctx.client.session.promptAsync({
138263
138593
  path: { id: sessionID },
138264
138594
  body: buildMemberPromptBody(memberEntry, buildWakeHint(unreadMessages.length)),
@@ -138529,14 +138859,14 @@ function normalizeSessionStatusToIdle(input) {
138529
138859
 
138530
138860
  // src/plugin/event.ts
138531
138861
  init_event_session_id();
138532
- function isRecord21(value) {
138862
+ function isRecord22(value) {
138533
138863
  return typeof value === "object" && value !== null;
138534
138864
  }
138535
138865
  function normalizeFallbackModelID(modelID) {
138536
138866
  return modelID.replace(/-thinking$/i, "").replace(/-max$/i, "").replace(/-high$/i, "");
138537
138867
  }
138538
138868
  function extractErrorName3(error) {
138539
- if (isRecord21(error) && typeof error.name === "string")
138869
+ if (isRecord22(error) && typeof error.name === "string")
138540
138870
  return error.name;
138541
138871
  if (error instanceof Error)
138542
138872
  return error.name;
@@ -138547,16 +138877,16 @@ function extractErrorMessage3(error) {
138547
138877
  return "";
138548
138878
  if (typeof error === "string")
138549
138879
  return error;
138550
- if (isRecord21(error)) {
138880
+ if (isRecord22(error)) {
138551
138881
  const candidates = [
138552
138882
  error.data,
138553
- isRecord21(error.data) ? error.data.error : undefined,
138883
+ isRecord22(error.data) ? error.data.error : undefined,
138554
138884
  error.error,
138555
138885
  error.cause,
138556
138886
  error
138557
138887
  ];
138558
138888
  for (const candidate of candidates) {
138559
- if (isRecord21(candidate) && typeof candidate.message === "string" && candidate.message.length > 0) {
138889
+ if (isRecord22(candidate) && typeof candidate.message === "string" && candidate.message.length > 0) {
138560
138890
  return candidate.message;
138561
138891
  }
138562
138892
  }
@@ -138612,6 +138942,10 @@ function createEventHandler2(args) {
138612
138942
  const lastHandledRetryStatusKey = new Map;
138613
138943
  const lastKnownModelBySession = new Map;
138614
138944
  const resolveFallbackProviderID = (sessionID, providerHint) => {
138945
+ const normalizedProviderHint = providerHint?.trim();
138946
+ if (normalizedProviderHint) {
138947
+ return normalizedProviderHint;
138948
+ }
138615
138949
  const sessionModel = getSessionModel(sessionID);
138616
138950
  if (sessionModel?.providerID) {
138617
138951
  return sessionModel.providerID;
@@ -138620,10 +138954,6 @@ function createEventHandler2(args) {
138620
138954
  if (lastKnownModel?.providerID) {
138621
138955
  return lastKnownModel.providerID;
138622
138956
  }
138623
- const normalizedProviderHint = providerHint?.trim();
138624
- if (normalizedProviderHint) {
138625
- return normalizedProviderHint;
138626
- }
138627
138957
  const connectedProvider = readConnectedProvidersCache()?.[0];
138628
138958
  if (connectedProvider) {
138629
138959
  return connectedProvider;
@@ -138638,7 +138968,7 @@ function createEventHandler2(args) {
138638
138968
  if (input.event.type.startsWith("message.") || input.event.type.startsWith("tool.")) {
138639
138969
  return resolveMessageEventSessionID(properties);
138640
138970
  }
138641
- const record3 = isRecord21(properties) ? properties : undefined;
138971
+ const record3 = isRecord22(properties) ? properties : undefined;
138642
138972
  const sessionID = record3?.sessionID;
138643
138973
  return typeof sessionID === "string" && sessionID.length > 0 ? sessionID : undefined;
138644
138974
  };
@@ -138737,7 +139067,7 @@ function createEventHandler2(args) {
138737
139067
  ...launchAgent ? { agent: launchAgent } : {},
138738
139068
  ...launchModel ? { model: launchModel } : {},
138739
139069
  ...launchVariant ? { variant: launchVariant } : {},
138740
- parts: [{ type: "text", text: "continue" }]
139070
+ parts: [createInternalAgentContinuationTextPart("continue")]
138741
139071
  },
138742
139072
  query: { directory: pluginContext.directory }
138743
139073
  };
@@ -139044,7 +139374,7 @@ function createEventHandler2(args) {
139044
139374
  });
139045
139375
  await pluginContext.client.session.prompt({
139046
139376
  path: { id: sessionID },
139047
- body: { parts: [{ type: "text", text: "continue" }] },
139377
+ body: { parts: [createInternalAgentContinuationTextPart("continue")] },
139048
139378
  query: { directory: pluginContext.directory }
139049
139379
  }).catch(() => {});
139050
139380
  }