oh-my-openagent 4.1.0 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -54332,7 +54332,7 @@ var {
54332
54332
  // package.json
54333
54333
  var package_default = {
54334
54334
  name: "oh-my-openagent",
54335
- version: "4.1.0",
54335
+ version: "4.1.1",
54336
54336
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
54337
54337
  main: "./dist/index.js",
54338
54338
  types: "dist/index.d.ts",
@@ -54413,17 +54413,17 @@ var package_default = {
54413
54413
  zod: "^4.3.0"
54414
54414
  },
54415
54415
  optionalDependencies: {
54416
- "oh-my-openagent-darwin-arm64": "4.1.0",
54417
- "oh-my-openagent-darwin-x64": "4.1.0",
54418
- "oh-my-openagent-darwin-x64-baseline": "4.1.0",
54419
- "oh-my-openagent-linux-arm64": "4.1.0",
54420
- "oh-my-openagent-linux-arm64-musl": "4.1.0",
54421
- "oh-my-openagent-linux-x64": "4.1.0",
54422
- "oh-my-openagent-linux-x64-baseline": "4.1.0",
54423
- "oh-my-openagent-linux-x64-musl": "4.1.0",
54424
- "oh-my-openagent-linux-x64-musl-baseline": "4.1.0",
54425
- "oh-my-openagent-windows-x64": "4.1.0",
54426
- "oh-my-openagent-windows-x64-baseline": "4.1.0"
54416
+ "oh-my-openagent-darwin-arm64": "4.1.1",
54417
+ "oh-my-openagent-darwin-x64": "4.1.1",
54418
+ "oh-my-openagent-darwin-x64-baseline": "4.1.1",
54419
+ "oh-my-openagent-linux-arm64": "4.1.1",
54420
+ "oh-my-openagent-linux-arm64-musl": "4.1.1",
54421
+ "oh-my-openagent-linux-x64": "4.1.1",
54422
+ "oh-my-openagent-linux-x64-baseline": "4.1.1",
54423
+ "oh-my-openagent-linux-x64-musl": "4.1.1",
54424
+ "oh-my-openagent-linux-x64-musl-baseline": "4.1.1",
54425
+ "oh-my-openagent-windows-x64": "4.1.1",
54426
+ "oh-my-openagent-windows-x64-baseline": "4.1.1"
54427
54427
  },
54428
54428
  overrides: {
54429
54429
  hono: "^4.12.18",
@@ -56,6 +56,8 @@ export declare class BackgroundManager {
56
56
  private completedTaskSummaries;
57
57
  private idleDeferralTimers;
58
58
  private notificationQueueByParent;
59
+ private pendingParentWakes;
60
+ private pendingParentWakeTimers;
59
61
  private observedOutputSessions;
60
62
  private observedIncompleteTodosBySession;
61
63
  private rootDescendantCounts;
@@ -170,6 +172,11 @@ export declare class BackgroundManager {
170
172
  */
171
173
  private tryCompleteTask;
172
174
  private notifyParentSession;
175
+ private isSessionActive;
176
+ private queuePendingParentWake;
177
+ private flushPendingParentWake;
178
+ private schedulePendingParentWakeFlush;
179
+ private clearPendingParentWakeTimer;
173
180
  private hasRunningTasks;
174
181
  private pruneStaleTasksAndNotifications;
175
182
  private checkAndInterruptStaleTasks;
@@ -1,6 +1,6 @@
1
1
  import type { PluginInput } from "@opencode-ai/plugin";
2
2
  import type { BackgroundTaskStatusProvider, SessionState } from "./types";
3
- export type BoulderContinuationResult = "injected" | "skipped_background_tasks" | "skipped_agent_unavailable" | "failed";
3
+ export type BoulderContinuationResult = "injected" | "skipped_active_session" | "skipped_background_tasks" | "skipped_agent_unavailable" | "failed";
4
4
  export declare function injectBoulderContinuation(input: {
5
5
  ctx: PluginInput;
6
6
  sessionID: string;
@@ -1,2 +1,11 @@
1
1
  export declare const DEFAULT_SESSION_IDLE_SETTLE_MS = 150;
2
2
  export declare function settleAfterSessionIdle(ms?: number): Promise<void>;
3
+ type SessionStatusClient = {
4
+ session?: {
5
+ status?: () => Promise<unknown>;
6
+ };
7
+ };
8
+ export declare function isActiveSessionStatusType(statusType: string): boolean;
9
+ export declare function isSessionActive(client: SessionStatusClient, sessionID: string): Promise<boolean>;
10
+ export declare function shouldPromptAfterSessionIdle(client: SessionStatusClient, sessionID: string, settleMs?: number): Promise<boolean>;
11
+ export {};
@@ -24,6 +24,7 @@ type TeamIdleWakeHintContext = {
24
24
  client: {
25
25
  session: {
26
26
  promptAsync?: (input: PromptAsyncInput) => Promise<unknown>;
27
+ status?: () => Promise<unknown>;
27
28
  };
28
29
  };
29
30
  };
@@ -55,6 +55,7 @@ type BabysitterContext = {
55
55
  directory?: string;
56
56
  };
57
57
  }) => Promise<unknown>;
58
+ status?: () => Promise<unknown>;
58
59
  };
59
60
  };
60
61
  };
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"];
@@ -69633,6 +69641,48 @@ init_logger();
69633
69641
  init_opencode_storage_detection();
69634
69642
  init_agent_display_names();
69635
69643
 
69644
+ // src/hooks/shared/session-idle-settle.ts
69645
+ var DEFAULT_SESSION_IDLE_SETTLE_MS = 150;
69646
+ function settleAfterSessionIdle(ms = DEFAULT_SESSION_IDLE_SETTLE_MS) {
69647
+ return ms > 0 ? new Promise((resolve8) => setTimeout(resolve8, ms)) : Promise.resolve();
69648
+ }
69649
+ var ACTIVE_SESSION_STATUSES = new Set(["busy", "retry", "running"]);
69650
+ function isRecord8(value) {
69651
+ return typeof value === "object" && value !== null;
69652
+ }
69653
+ function getSessionStatusPayload(response) {
69654
+ if (isRecord8(response) && isRecord8(response.data)) {
69655
+ return response.data;
69656
+ }
69657
+ if (isRecord8(response)) {
69658
+ return response;
69659
+ }
69660
+ return {};
69661
+ }
69662
+ function isActiveSessionStatusType(statusType) {
69663
+ return ACTIVE_SESSION_STATUSES.has(statusType);
69664
+ }
69665
+ async function isSessionActive(client, sessionID) {
69666
+ if (typeof client.session?.status !== "function") {
69667
+ return false;
69668
+ }
69669
+ try {
69670
+ const statusResult = await client.session.status();
69671
+ const status = getSessionStatusPayload(statusResult)[sessionID];
69672
+ if (!isRecord8(status)) {
69673
+ return false;
69674
+ }
69675
+ const statusType = status.type;
69676
+ return typeof statusType === "string" && isActiveSessionStatusType(statusType);
69677
+ } catch {
69678
+ return false;
69679
+ }
69680
+ }
69681
+ async function shouldPromptAfterSessionIdle(client, sessionID, settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS) {
69682
+ await settleAfterSessionIdle(settleMs);
69683
+ return !await isSessionActive(client, sessionID);
69684
+ }
69685
+
69636
69686
  // src/hooks/todo-continuation-enforcer/message-directory.ts
69637
69687
  init_opencode_message_dir();
69638
69688
 
@@ -69906,6 +69956,10 @@ ${todoList}`;
69906
69956
  log(`[${HOOK_NAME}] Skipped injection: session was cancelled before prompt`, { sessionID });
69907
69957
  return;
69908
69958
  }
69959
+ if (await isSessionActive(ctx.client, sessionID)) {
69960
+ log(`[${HOOK_NAME}] Skipped injection: session is active before prompt`, { sessionID });
69961
+ return;
69962
+ }
69909
69963
  if (injectionState) {
69910
69964
  injectionState.inFlight = true;
69911
69965
  }
@@ -69926,7 +69980,7 @@ ${todoList}`;
69926
69980
  ...launchModel ? { model: launchModel } : {},
69927
69981
  ...launchVariant ? { variant: launchVariant } : {},
69928
69982
  ...inheritedTools ? { tools: inheritedTools } : {},
69929
- parts: [createInternalAgentTextPart(prompt)]
69983
+ parts: [createInternalAgentContinuationTextPart(prompt)]
69930
69984
  },
69931
69985
  query: { directory: ctx.directory }
69932
69986
  });
@@ -71025,12 +71079,12 @@ async function playSessionNotificationSound(ctx, platform2, soundPath) {
71025
71079
  }
71026
71080
 
71027
71081
  // src/hooks/session-notification-event-properties.ts
71028
- function isRecord8(value) {
71082
+ function isRecord9(value) {
71029
71083
  return typeof value === "object" && value !== null;
71030
71084
  }
71031
71085
  function getEventInfo(properties) {
71032
71086
  const info = properties?.info;
71033
- return isRecord8(info) ? info : undefined;
71087
+ return isRecord9(info) ? info : undefined;
71034
71088
  }
71035
71089
  function getSessionID(properties) {
71036
71090
  const sessionID = properties?.sessionID;
@@ -71047,7 +71101,7 @@ function getSessionID(properties) {
71047
71101
  if (typeof infoSessionId === "string" && infoSessionId.length > 0)
71048
71102
  return infoSessionId;
71049
71103
  const part = properties?.part;
71050
- if (isRecord8(part)) {
71104
+ if (isRecord9(part)) {
71051
71105
  const partSessionID = part.sessionID;
71052
71106
  if (typeof partSessionID === "string" && partSessionID.length > 0)
71053
71107
  return partSessionID;
@@ -71068,13 +71122,13 @@ function getEventToolName(properties) {
71068
71122
  }
71069
71123
  function getQuestionText(properties) {
71070
71124
  const args = properties?.args;
71071
- if (!isRecord8(args))
71125
+ if (!isRecord9(args))
71072
71126
  return "";
71073
71127
  const questions = args.questions;
71074
71128
  if (!Array.isArray(questions) || questions.length === 0)
71075
71129
  return "";
71076
71130
  const firstQuestion = questions[0];
71077
- if (!isRecord8(firstQuestion))
71131
+ if (!isRecord9(firstQuestion))
71078
71132
  return "";
71079
71133
  const questionText = firstQuestion.question;
71080
71134
  return typeof questionText === "string" ? questionText : "";
@@ -72304,7 +72358,7 @@ async function resumeSession(client, config) {
72304
72358
  await client.session.promptAsync({
72305
72359
  path: { id: config.sessionID },
72306
72360
  body: {
72307
- parts: [createInternalAgentTextPart(RECOVERY_RESUME_TEXT)],
72361
+ parts: [createInternalAgentContinuationTextPart(RECOVERY_RESUME_TEXT)],
72308
72362
  agent: config.agent,
72309
72363
  ...launchModel ? { model: launchModel } : {},
72310
72364
  ...launchVariant ? { variant: launchVariant } : {},
@@ -76690,7 +76744,7 @@ ${result.stderr.trim()}`);
76690
76744
 
76691
76745
  // src/hooks/claude-code-hooks/handlers/tool-execute-after-handler.ts
76692
76746
  init_shared();
76693
- function isRecord9(value) {
76747
+ function isRecord10(value) {
76694
76748
  return typeof value === "object" && value !== null && !Array.isArray(value);
76695
76749
  }
76696
76750
  function getStringValue(record, key) {
@@ -76703,7 +76757,7 @@ function getNumberValue(record, key) {
76703
76757
  }
76704
76758
  function buildTranscriptToolOutput(outputText, metadata) {
76705
76759
  const compactOutput = { output: outputText };
76706
- if (!isRecord9(metadata)) {
76760
+ if (!isRecord10(metadata)) {
76707
76761
  return compactOutput;
76708
76762
  }
76709
76763
  const filePath = getStringValue(metadata, "filePath") ?? getStringValue(metadata, "path") ?? getStringValue(metadata, "file");
@@ -76725,7 +76779,7 @@ function buildTranscriptToolOutput(outputText, metadata) {
76725
76779
  }
76726
76780
  }
76727
76781
  const filediff = metadata.filediff;
76728
- if (isRecord9(filediff)) {
76782
+ if (isRecord10(filediff)) {
76729
76783
  const additions = getNumberValue(filediff, "additions");
76730
76784
  const deletions = getNumberValue(filediff, "deletions");
76731
76785
  if (additions !== undefined || deletions !== undefined) {
@@ -78695,14 +78749,14 @@ var defaultDeps3 = {
78695
78749
  runBackgroundUpdateCheck,
78696
78750
  log
78697
78751
  };
78698
- var isRecord10 = (value) => {
78752
+ var isRecord11 = (value) => {
78699
78753
  return typeof value === "object" && value !== null;
78700
78754
  };
78701
78755
  var getParentID = (properties) => {
78702
- if (!isRecord10(properties))
78756
+ if (!isRecord11(properties))
78703
78757
  return;
78704
78758
  const { info } = properties;
78705
- if (!isRecord10(info))
78759
+ if (!isRecord11(info))
78706
78760
  return;
78707
78761
  const { parentID } = info;
78708
78762
  return typeof parentID === "string" && parentID.length > 0 ? parentID : undefined;
@@ -81366,7 +81420,7 @@ async function injectContinuationPrompt(ctx, options) {
81366
81420
  ...launchModel ? { model: launchModel } : {},
81367
81421
  ...launchVariant ? { variant: launchVariant } : {},
81368
81422
  ...inheritedTools ? { tools: inheritedTools } : {},
81369
- parts: [createInternalAgentTextPart(options.prompt)]
81423
+ parts: [createInternalAgentContinuationTextPart(options.prompt)]
81370
81424
  },
81371
81425
  query: { directory: options.directory }
81372
81426
  });
@@ -81496,7 +81550,7 @@ function resolveToolCallID(ctx) {
81496
81550
  }
81497
81551
  // src/features/tool-metadata-store/task-metadata-contract.ts
81498
81552
  init_logger();
81499
- function isRecord12(value) {
81553
+ function isRecord13(value) {
81500
81554
  return typeof value === "object" && value !== null;
81501
81555
  }
81502
81556
  function readString2(value) {
@@ -81581,7 +81635,7 @@ function parseTaskMetadataBlock(text) {
81581
81635
  return parsed;
81582
81636
  }
81583
81637
  function extractTaskLink(metadata, outputText) {
81584
- if (isRecord12(metadata)) {
81638
+ if (isRecord13(metadata)) {
81585
81639
  const metadataLink = {
81586
81640
  sessionId: readSessionIdFromMetadata(metadata),
81587
81641
  taskId: readTaskIdFromMetadata(metadata),
@@ -82352,6 +82406,10 @@ function createRalphLoopEventHandler(ctx, options) {
82352
82406
  });
82353
82407
  return;
82354
82408
  }
82409
+ if (await isSessionActive(ctx.client, sessionID)) {
82410
+ log(`[${HOOK_NAME3}] Skipped: session became active during settle window`, { sessionID });
82411
+ return;
82412
+ }
82355
82413
  if (stateAfterSettle.verification_pending) {
82356
82414
  log(`[${HOOK_NAME3}] Skipped: state entered verification_pending during settle window`, { sessionID });
82357
82415
  return;
@@ -82485,6 +82543,10 @@ function createRalphLoopEventHandler(ctx, options) {
82485
82543
  });
82486
82544
  return;
82487
82545
  }
82546
+ if (await isSessionActive(ctx.client, sessionID)) {
82547
+ log(`[${HOOK_NAME3}] Skipped: session became active during settle window`, { sessionID });
82548
+ return;
82549
+ }
82488
82550
  if (stateAfterSettle.verification_pending) {
82489
82551
  log(`[${HOOK_NAME3}] Skipped: state entered verification_pending during settle window`, { sessionID });
82490
82552
  return;
@@ -89383,7 +89445,7 @@ function createProcessedCommandStore() {
89383
89445
 
89384
89446
  // src/hooks/auto-slash-command/hook.ts
89385
89447
  var COMMAND_EXECUTE_FALLBACK_DEDUP_TTL_MS = 100;
89386
- function isRecord13(value) {
89448
+ function isRecord14(value) {
89387
89449
  return typeof value === "object" && value !== null;
89388
89450
  }
89389
89451
  function getDeletedSessionID(properties) {
@@ -89401,7 +89463,7 @@ function getCommandExecutionEventID(input) {
89401
89463
  "commandId"
89402
89464
  ];
89403
89465
  const recordInput = input;
89404
- if (!isRecord13(recordInput)) {
89466
+ if (!isRecord14(recordInput)) {
89405
89467
  return null;
89406
89468
  }
89407
89469
  for (const key of candidateKeys) {
@@ -91211,12 +91273,6 @@ init_shared();
91211
91273
  init_agent_display_names();
91212
91274
  init_logger();
91213
91275
 
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
91276
  // src/hooks/atlas/boulder-continuation-injector.ts
91221
91277
  init_logger();
91222
91278
  init_shared();
@@ -91500,6 +91556,10 @@ async function injectBoulderContinuation(input) {
91500
91556
  return "skipped_agent_unavailable";
91501
91557
  }
91502
91558
  try {
91559
+ if (await isSessionActive(ctx.client, sessionID)) {
91560
+ log(`[${HOOK_NAME7}] Skipped injection: session is active`, { sessionID });
91561
+ return "skipped_active_session";
91562
+ }
91503
91563
  log(`[${HOOK_NAME7}] Injecting boulder continuation`, { sessionID, planName, remaining });
91504
91564
  const promptContext = await resolveRecentPromptContextForSession(ctx, sessionID);
91505
91565
  const inheritedTools = resolveInheritedPromptTools(sessionID, promptContext.tools);
@@ -91512,7 +91572,7 @@ async function injectBoulderContinuation(input) {
91512
91572
  ...launchModel ? { model: launchModel } : {},
91513
91573
  ...launchVariant ? { variant: launchVariant } : {},
91514
91574
  ...inheritedTools ? { tools: inheritedTools } : {},
91515
- parts: [createInternalAgentTextPart(prompt)]
91575
+ parts: [createInternalAgentContinuationTextPart(prompt)]
91516
91576
  },
91517
91577
  query: { directory: ctx.directory }
91518
91578
  });
@@ -91760,11 +91820,15 @@ async function handleAtlasSessionIdle(input) {
91760
91820
  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
91821
  const atlasAgent = resolveRegisteredAgentName(boulderState.agent ?? (isAgentRegistered("atlas") ? "atlas" : undefined));
91762
91822
  if (atlasAgent && isAgentRegistered(atlasAgent)) {
91823
+ if (!await shouldPromptAfterSessionIdle(ctx.client, sessionID, options?.idleSettleMs)) {
91824
+ log(`[${HOOK_NAME7}] Boulder completion nudge skipped because session is active`, { sessionID });
91825
+ return;
91826
+ }
91763
91827
  await ctx.client.session.promptAsync({
91764
91828
  path: { id: sessionID },
91765
91829
  body: {
91766
91830
  agent: atlasAgent,
91767
- parts: [createInternalAgentTextPart(prompt)]
91831
+ parts: [createInternalAgentContinuationTextPart(prompt)]
91768
91832
  },
91769
91833
  query: { directory: ctx.directory }
91770
91834
  });
@@ -91842,7 +91906,10 @@ async function handleAtlasSessionIdle(input) {
91842
91906
  });
91843
91907
  return;
91844
91908
  }
91845
- await settleAfterSessionIdle(options?.idleSettleMs);
91909
+ if (!await shouldPromptAfterSessionIdle(ctx.client, sessionID, options?.idleSettleMs)) {
91910
+ log(`[${HOOK_NAME7}] Skipped: session became active during idle settle`, { sessionID });
91911
+ return;
91912
+ }
91846
91913
  await injectContinuation2({
91847
91914
  ctx,
91848
91915
  sessionID,
@@ -93889,16 +93956,16 @@ var THINKING_SUMMARY_MAX_CHARS = 500;
93889
93956
  function hasData(value) {
93890
93957
  return typeof value === "object" && value !== null && "data" in value;
93891
93958
  }
93892
- function isRecord14(value) {
93959
+ function isRecord15(value) {
93893
93960
  return typeof value === "object" && value !== null;
93894
93961
  }
93895
93962
  function getMessageInfo(value) {
93896
- if (!isRecord14(value))
93963
+ if (!isRecord15(value))
93897
93964
  return;
93898
- if (!isRecord14(value.info))
93965
+ if (!isRecord15(value.info))
93899
93966
  return;
93900
93967
  const info = value.info;
93901
- const modelValue = isRecord14(info.model) ? info.model : undefined;
93968
+ const modelValue = isRecord15(info.model) ? info.model : undefined;
93902
93969
  const model = modelValue && typeof modelValue.providerID === "string" && typeof modelValue.modelID === "string" ? {
93903
93970
  providerID: modelValue.providerID,
93904
93971
  modelID: modelValue.modelID,
@@ -93910,7 +93977,7 @@ function getMessageInfo(value) {
93910
93977
  model,
93911
93978
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
93912
93979
  modelID: typeof info.modelID === "string" ? info.modelID : undefined,
93913
- tools: isRecord14(info.tools) ? Object.entries(info.tools).reduce((acc, [key, value2]) => {
93980
+ tools: isRecord15(info.tools) ? Object.entries(info.tools).reduce((acc, [key, value2]) => {
93914
93981
  if (value2 === true || value2 === false || value2 === "allow" || value2 === "deny" || value2 === "ask") {
93915
93982
  acc[key] = value2;
93916
93983
  }
@@ -93919,11 +93986,11 @@ function getMessageInfo(value) {
93919
93986
  };
93920
93987
  }
93921
93988
  function getMessageParts(value) {
93922
- if (!isRecord14(value))
93989
+ if (!isRecord15(value))
93923
93990
  return [];
93924
93991
  if (!Array.isArray(value.parts))
93925
93992
  return [];
93926
- return value.parts.filter(isRecord14).map((part) => ({
93993
+ return value.parts.filter(isRecord15).map((part) => ({
93927
93994
  type: typeof part.type === "string" ? part.type : undefined,
93928
93995
  text: typeof part.text === "string" ? part.text : undefined,
93929
93996
  thinking: typeof part.thinking === "string" ? part.thinking : undefined
@@ -94108,7 +94175,10 @@ function createUnstableAgentBabysitterHook(ctx, options) {
94108
94175
  try {
94109
94176
  const launchModel = model ? { providerID: model.providerID, modelID: model.modelID } : undefined;
94110
94177
  const launchVariant = model?.variant;
94111
- await settleAfterSessionIdle(options.idleSettleMs);
94178
+ if (!await shouldPromptAfterSessionIdle(ctx.client, mainSessionID, options.idleSettleMs)) {
94179
+ log(`[${HOOK_NAME10}] Reminder skipped because main session is active`, { taskId: task.id, sessionID: mainSessionID });
94180
+ continue;
94181
+ }
94112
94182
  await ctx.client.session.promptAsync({
94113
94183
  path: { id: mainSessionID },
94114
94184
  body: {
@@ -94736,11 +94806,11 @@ function buildRetryModelPayload(model, agentSettings) {
94736
94806
  }
94737
94807
 
94738
94808
  // src/hooks/runtime-fallback/session-messages.ts
94739
- function isRecord15(value) {
94809
+ function isRecord16(value) {
94740
94810
  return typeof value === "object" && value !== null;
94741
94811
  }
94742
94812
  function isSessionMessage(value) {
94743
- return isRecord15(value);
94813
+ return isRecord16(value);
94744
94814
  }
94745
94815
  function isSessionMessageArray(value) {
94746
94816
  return Array.isArray(value) && value.every(isSessionMessage);
@@ -94749,7 +94819,7 @@ function extractSessionMessages(messagesResponse) {
94749
94819
  if (isSessionMessageArray(messagesResponse)) {
94750
94820
  return messagesResponse;
94751
94821
  }
94752
- if (!isRecord15(messagesResponse)) {
94822
+ if (!isRecord16(messagesResponse)) {
94753
94823
  return;
94754
94824
  }
94755
94825
  const data = messagesResponse.data;
@@ -101171,7 +101241,7 @@ function normalizeToolArgSchemas(toolDefinition) {
101171
101241
  return toolDefinition;
101172
101242
  }
101173
101243
  var UNSUPPORTED_SCHEMA_KEYWORDS = new Set(["contentEncoding", "contentMediaType"]);
101174
- function isRecord16(value) {
101244
+ function isRecord17(value) {
101175
101245
  return typeof value === "object" && value !== null && !Array.isArray(value);
101176
101246
  }
101177
101247
  function normalizeJsonSchemaRef(value) {
@@ -101184,7 +101254,7 @@ function sanitizeJsonSchema(value, depth = 0, isPropertyName = false) {
101184
101254
  if (Array.isArray(value)) {
101185
101255
  return value.map((item) => sanitizeJsonSchema(item, depth + 1, false));
101186
101256
  }
101187
- if (!isRecord16(value)) {
101257
+ if (!isRecord17(value)) {
101188
101258
  return value;
101189
101259
  }
101190
101260
  const sanitized = {};
@@ -102344,6 +102414,9 @@ var BLOCKED_TMUX_SUBCOMMANDS = [
102344
102414
  "pipe-pane",
102345
102415
  "pipep"
102346
102416
  ];
102417
+ var PROHIBITED_TMUX_SUBCOMMANDS = [
102418
+ "kill-server"
102419
+ ];
102347
102420
  var INTERACTIVE_BASH_DESCRIPTION = `WARNING: This is TMUX ONLY. Pass tmux subcommands directly (without 'tmux' prefix).
102348
102421
 
102349
102422
  Examples: new-session -d -s omo-dev, send-keys -t omo-dev "vim" Enter
@@ -102352,6 +102425,7 @@ For TUI apps needing ongoing interaction (vim, htop, pudb). One-shot commands \u
102352
102425
 
102353
102426
  // src/tools/interactive-bash/tools.ts
102354
102427
  init_tmux_path_resolver();
102428
+ var GLOBAL_TMUX_OPTIONS_WITH_ARGS = new Set(["-L", "-S", "-f", "-c", "-T"]);
102355
102429
  function resolveTmuxExecutable2(tmuxPath2) {
102356
102430
  if (!isCmuxCompatEnvironment()) {
102357
102431
  return [tmuxPath2];
@@ -102396,6 +102470,71 @@ function tokenizeCommand2(cmd) {
102396
102470
  tokens.push(current);
102397
102471
  return tokens;
102398
102472
  }
102473
+ function findSubcommandIndex(parts) {
102474
+ let index = 0;
102475
+ while (index < parts.length) {
102476
+ const part = parts[index] ?? "";
102477
+ if (part === "--") {
102478
+ return index + 1 < parts.length ? index + 1 : -1;
102479
+ }
102480
+ if (GLOBAL_TMUX_OPTIONS_WITH_ARGS.has(part)) {
102481
+ index += 2;
102482
+ continue;
102483
+ }
102484
+ if (part.startsWith("-")) {
102485
+ index++;
102486
+ continue;
102487
+ }
102488
+ return index;
102489
+ }
102490
+ return -1;
102491
+ }
102492
+ function getTargetSessionName(parts) {
102493
+ const sessionIdx = parts.findIndex((p) => p === "-t" || p.startsWith("-t"));
102494
+ if (sessionIdx === -1) {
102495
+ return "omo-session";
102496
+ }
102497
+ const sessionToken = parts[sessionIdx] ?? "";
102498
+ const nextToken = parts[sessionIdx + 1];
102499
+ if (sessionToken === "-t" && nextToken) {
102500
+ return nextToken;
102501
+ }
102502
+ if (sessionToken.startsWith("-t")) {
102503
+ return sessionToken.slice(2);
102504
+ }
102505
+ return "omo-session";
102506
+ }
102507
+ function buildBlockedTmuxCommandMessage(command, parts) {
102508
+ const sessionName = getTargetSessionName(parts);
102509
+ return `Error: '${command}' is blocked in interactive_bash.
102510
+
102511
+ **USE BASH TOOL INSTEAD:**
102512
+
102513
+ \`\`\`bash
102514
+ # Capture terminal output
102515
+ tmux capture-pane -p -t ${sessionName}
102516
+
102517
+ # Or capture with history (last 1000 lines)
102518
+ tmux capture-pane -p -t ${sessionName} -S -1000
102519
+ \`\`\`
102520
+
102521
+ The Bash tool can execute these commands directly. Do NOT retry with interactive_bash.`;
102522
+ }
102523
+ function buildProhibitedTmuxCommandMessage(command) {
102524
+ return `Error: '${command}' is prohibited in interactive_bash.
102525
+
102526
+ NEVER EVER run tmux kill-server from interactive_bash.
102527
+
102528
+ It terminates the entire tmux server, destroying every tmux session and pane that the user, Codex, or other agents may be using.
102529
+
102530
+ Use scoped cleanup only:
102531
+
102532
+ \`\`\`bash
102533
+ tmux kill-session -t <session-name>
102534
+ \`\`\`
102535
+
102536
+ If you created an omo-* session, kill only that exact session. Do not retry kill-server with Bash or any other tool.`;
102537
+ }
102399
102538
  var interactive_bash = tool({
102400
102539
  description: INTERACTIVE_BASH_DESCRIPTION,
102401
102540
  args: {
@@ -102408,30 +102547,14 @@ var interactive_bash = tool({
102408
102547
  if (parts.length === 0) {
102409
102548
  return "Error: Empty tmux command";
102410
102549
  }
102411
- const subcommand = parts[0].toLowerCase();
102550
+ const subcommandIndex = findSubcommandIndex(parts);
102551
+ const rawSubcommand = subcommandIndex === -1 ? "" : parts[subcommandIndex];
102552
+ const subcommand = rawSubcommand.toLowerCase();
102553
+ if (PROHIBITED_TMUX_SUBCOMMANDS.includes(subcommand)) {
102554
+ return buildProhibitedTmuxCommandMessage(rawSubcommand);
102555
+ }
102412
102556
  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.`;
102557
+ return buildBlockedTmuxCommandMessage(rawSubcommand, parts);
102435
102558
  }
102436
102559
  const proc = spawnWithWindowsHide([...resolveTmuxExecutable2(tmuxPath2), ...parts], {
102437
102560
  stdout: "pipe",
@@ -102902,7 +103025,7 @@ async function formatFullSession(task, client2, options) {
102902
103025
  }
102903
103026
 
102904
103027
  // src/features/background-agent/error-classifier.ts
102905
- function isRecord17(value) {
103028
+ function isRecord18(value) {
102906
103029
  return typeof value === "object" && value !== null;
102907
103030
  }
102908
103031
  function isAbortedSessionError(error) {
@@ -102928,7 +103051,7 @@ function getErrorText(error) {
102928
103051
  return "";
102929
103052
  }
102930
103053
  function extractErrorName2(error) {
102931
- if (isRecord17(error) && typeof error["name"] === "string")
103054
+ if (isRecord18(error) && typeof error["name"] === "string")
102932
103055
  return error["name"];
102933
103056
  if (error instanceof Error)
102934
103057
  return error.name;
@@ -102939,11 +103062,11 @@ function extractErrorMessage(error) {
102939
103062
  return;
102940
103063
  if (typeof error === "string")
102941
103064
  return error;
102942
- if (isRecord17(error)) {
103065
+ if (isRecord18(error)) {
102943
103066
  const dataRaw = error["data"];
102944
103067
  const candidates = [
102945
103068
  dataRaw,
102946
- isRecord17(dataRaw) ? dataRaw["error"] : undefined,
103069
+ isRecord18(dataRaw) ? dataRaw["error"] : undefined,
102947
103070
  error["error"],
102948
103071
  error["cause"],
102949
103072
  error
@@ -102951,7 +103074,7 @@ function extractErrorMessage(error) {
102951
103074
  for (const candidate of candidates) {
102952
103075
  if (typeof candidate === "string" && candidate.length > 0)
102953
103076
  return candidate;
102954
- if (isRecord17(candidate) && typeof candidate["message"] === "string" && candidate["message"].length > 0) {
103077
+ if (isRecord18(candidate) && typeof candidate["message"] === "string" && candidate["message"].length > 0) {
102955
103078
  return candidate["message"];
102956
103079
  }
102957
103080
  }
@@ -102966,10 +103089,10 @@ function extractErrorMessage(error) {
102966
103089
  }
102967
103090
  function getSessionErrorMessage(properties) {
102968
103091
  const errorRaw = properties["error"];
102969
- if (!isRecord17(errorRaw))
103092
+ if (!isRecord18(errorRaw))
102970
103093
  return;
102971
103094
  const dataRaw = errorRaw["data"];
102972
- if (isRecord17(dataRaw)) {
103095
+ if (isRecord18(dataRaw)) {
102973
103096
  const message2 = dataRaw["message"];
102974
103097
  if (typeof message2 === "string")
102975
103098
  return message2;
@@ -105345,7 +105468,7 @@ init_logger();
105345
105468
  init_shared();
105346
105469
  var NON_TERMINAL_FINISH_REASONS = new Set(["tool-calls", "unknown"]);
105347
105470
  var PENDING_TOOL_PART_TYPES = new Set(["tool", "tool_use", "tool-call"]);
105348
- var ACTIVE_SESSION_STATUSES = new Set(["busy", "retry", "running"]);
105471
+ var ACTIVE_SESSION_STATUSES2 = new Set(["busy", "retry", "running"]);
105349
105472
  function wait(milliseconds) {
105350
105473
  const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
105351
105474
  const typedArray = new Int32Array(sharedBuffer);
@@ -105363,7 +105486,7 @@ function abortSyncSession(client2, sessionID, reason) {
105363
105486
  });
105364
105487
  }
105365
105488
  function isActiveSessionStatus(status) {
105366
- return status !== undefined && ACTIVE_SESSION_STATUSES.has(status.type);
105489
+ return status !== undefined && ACTIVE_SESSION_STATUSES2.has(status.type);
105367
105490
  }
105368
105491
  async function fetchSessionMessages(client2, sessionID) {
105369
105492
  const messagesResult = await client2.session.messages({ path: { id: sessionID } });
@@ -111697,10 +111820,10 @@ async function verifySessionExists(client2, sessionID, directory) {
111697
111820
 
111698
111821
  // src/features/background-agent/session-status-classifier.ts
111699
111822
  init_shared();
111700
- var ACTIVE_SESSION_STATUSES2 = new Set(["busy", "retry", "running"]);
111823
+ var ACTIVE_SESSION_STATUSES3 = new Set(["busy", "retry", "running"]);
111701
111824
  var KNOWN_TERMINAL_STATUSES = new Set(["idle", "interrupted"]);
111702
111825
  function isActiveSessionStatus2(type2) {
111703
- if (ACTIVE_SESSION_STATUSES2.has(type2)) {
111826
+ if (ACTIVE_SESSION_STATUSES3.has(type2)) {
111704
111827
  return true;
111705
111828
  }
111706
111829
  if (!KNOWN_TERMINAL_STATUSES.has(type2)) {
@@ -112000,6 +112123,7 @@ function createSubagentDepthLimitError(input) {
112000
112123
  }
112001
112124
 
112002
112125
  // src/features/background-agent/manager.ts
112126
+ var PENDING_PARENT_WAKE_RETRY_MS = 1000;
112003
112127
  function resolveMessagePartInfo(properties) {
112004
112128
  if (!properties || typeof properties !== "object") {
112005
112129
  return;
@@ -112062,6 +112186,8 @@ class BackgroundManager {
112062
112186
  completedTaskSummaries = new Map;
112063
112187
  idleDeferralTimers = new Map;
112064
112188
  notificationQueueByParent = new Map;
112189
+ pendingParentWakes = new Map;
112190
+ pendingParentWakeTimers = new Map;
112065
112191
  observedOutputSessions = new Set;
112066
112192
  observedIncompleteTodosBySession = new Map;
112067
112193
  rootDescendantCounts;
@@ -113031,6 +113157,12 @@ The fallback retry session is now created and can be inspected directly.
113031
113157
  if (event.type === "session.idle") {
113032
113158
  if (!props || typeof props !== "object")
113033
113159
  return;
113160
+ const sessionID = resolveSessionEventID(props);
113161
+ if (sessionID) {
113162
+ this.enqueueNotificationForParent(sessionID, () => this.flushPendingParentWake(sessionID)).catch((error) => {
113163
+ log("[background-agent] Failed to flush pending parent wake:", { sessionID, error });
113164
+ });
113165
+ }
113034
113166
  handleSessionIdleBackgroundEvent({
113035
113167
  properties: props,
113036
113168
  findBySession: (id) => {
@@ -113041,7 +113173,7 @@ The fallback retry session is now created and can be inspected directly.
113041
113173
  validateSessionHasOutput: (id) => this.validateSessionHasOutput(id),
113042
113174
  checkSessionTodos: (id) => this.checkSessionTodos(id),
113043
113175
  tryCompleteTask: (task, source) => this.tryCompleteTask(task, source),
113044
- emitIdleEvent: (sessionID) => this.handleEvent({ type: "session.idle", properties: { sessionID } })
113176
+ emitIdleEvent: (sessionID2) => this.handleEvent({ type: "session.idle", properties: { sessionID: sessionID2 } })
113045
113177
  });
113046
113178
  }
113047
113179
  if (event.type === "session.error") {
@@ -113610,7 +113742,7 @@ ${originalText}`;
113610
113742
  }, this.directory);
113611
113743
  const messages = normalizeSDKResponse(messagesResp, []);
113612
113744
  promptContext = resolvePromptContextFromSessionMessages(messages, task.parentSessionId);
113613
- const normalizedTools = isRecord17(promptContext?.tools) ? normalizePromptTools(promptContext.tools) : undefined;
113745
+ const normalizedTools = isRecord18(promptContext?.tools) ? normalizePromptTools(promptContext.tools) : undefined;
113614
113746
  if (promptContext?.agent || promptContext?.model || normalizedTools) {
113615
113747
  agent = promptContext?.agent ?? task.parentAgent;
113616
113748
  model = promptContext?.model?.providerID && promptContext.model.modelID ? { providerID: promptContext.model.providerID, modelID: promptContext.model.modelID } : undefined;
@@ -113644,30 +113776,40 @@ ${originalText}`;
113644
113776
  ...variant !== undefined ? { variant } : {},
113645
113777
  ...resolvedTools ? { tools: resolvedTools } : {}
113646
113778
  };
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:", {
113779
+ const shouldDeferReply = shouldReply && await this.isSessionActive(task.parentSessionId);
113780
+ if (shouldDeferReply) {
113781
+ this.queuePendingParentWake(task.parentSessionId, notification2, parentPromptContext);
113782
+ log("[background-agent] Deferred notification until parent session is idle:", {
113657
113783
  taskId: task.id,
113658
113784
  allComplete,
113659
- isTaskFailure,
113660
- noReply: !shouldReply
113785
+ isTaskFailure
113661
113786
  });
113662
- } catch (error) {
113663
- if (isAbortedSessionError(error)) {
113664
- log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
113787
+ } else {
113788
+ try {
113789
+ await promptAsyncInDirectory(this.client, {
113790
+ path: { id: task.parentSessionId },
113791
+ body: {
113792
+ noReply: !shouldReply,
113793
+ ...parentPromptContext,
113794
+ parts: [createInternalAgentTextPart(notification2)]
113795
+ }
113796
+ }, this.directory);
113797
+ log("[background-agent] Sent notification to parent session:", {
113665
113798
  taskId: task.id,
113666
- parentSessionID: task.parentSessionId
113799
+ allComplete,
113800
+ isTaskFailure,
113801
+ noReply: !shouldReply
113667
113802
  });
113668
- this.queuePendingNotification(task.parentSessionId, notification2);
113669
- } else {
113670
- log("[background-agent] Failed to send notification:", error);
113803
+ } catch (error) {
113804
+ if (isAbortedSessionError(error)) {
113805
+ log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
113806
+ taskId: task.id,
113807
+ parentSessionID: task.parentSessionId
113808
+ });
113809
+ this.queuePendingNotification(task.parentSessionId, notification2);
113810
+ } else {
113811
+ log("[background-agent] Failed to send notification:", error);
113812
+ }
113671
113813
  }
113672
113814
  }
113673
113815
  } else {
@@ -113680,6 +113822,78 @@ ${originalText}`;
113680
113822
  this.scheduleTaskRemoval(task.id);
113681
113823
  }
113682
113824
  }
113825
+ async isSessionActive(sessionID) {
113826
+ return isSessionActive(this.client, sessionID);
113827
+ }
113828
+ queuePendingParentWake(sessionID, notification2, promptContext) {
113829
+ const pendingWake = this.pendingParentWakes.get(sessionID);
113830
+ if (pendingWake) {
113831
+ pendingWake.notifications.push(notification2);
113832
+ pendingWake.promptContext = promptContext;
113833
+ } else {
113834
+ this.pendingParentWakes.set(sessionID, {
113835
+ promptContext,
113836
+ notifications: [notification2]
113837
+ });
113838
+ }
113839
+ this.schedulePendingParentWakeFlush(sessionID);
113840
+ }
113841
+ async flushPendingParentWake(sessionID) {
113842
+ const pendingWake = this.pendingParentWakes.get(sessionID);
113843
+ if (!pendingWake) {
113844
+ this.clearPendingParentWakeTimer(sessionID);
113845
+ return;
113846
+ }
113847
+ if (await this.isSessionActive(sessionID)) {
113848
+ this.schedulePendingParentWakeFlush(sessionID);
113849
+ return;
113850
+ }
113851
+ this.pendingParentWakes.delete(sessionID);
113852
+ this.clearPendingParentWakeTimer(sessionID);
113853
+ await settleAfterSessionIdle();
113854
+ if (await this.isSessionActive(sessionID)) {
113855
+ this.pendingParentWakes.set(sessionID, pendingWake);
113856
+ this.schedulePendingParentWakeFlush(sessionID);
113857
+ return;
113858
+ }
113859
+ const notificationContent = pendingWake.notifications.join(`
113860
+
113861
+ `);
113862
+ try {
113863
+ await promptAsyncInDirectory(this.client, {
113864
+ path: { id: sessionID },
113865
+ body: {
113866
+ noReply: false,
113867
+ ...pendingWake.promptContext,
113868
+ parts: [createInternalAgentTextPart(notificationContent)]
113869
+ }
113870
+ }, this.directory);
113871
+ log("[background-agent] Sent deferred parent wake:", { sessionID });
113872
+ } catch (error) {
113873
+ this.queuePendingNotification(sessionID, notificationContent);
113874
+ log("[background-agent] Failed to send deferred parent wake:", { sessionID, error });
113875
+ }
113876
+ }
113877
+ schedulePendingParentWakeFlush(sessionID) {
113878
+ if (this.pendingParentWakeTimers.has(sessionID)) {
113879
+ return;
113880
+ }
113881
+ const timer = setTimeout(() => {
113882
+ this.pendingParentWakeTimers.delete(sessionID);
113883
+ this.enqueueNotificationForParent(sessionID, () => this.flushPendingParentWake(sessionID)).catch((error) => {
113884
+ log("[background-agent] Failed to retry pending parent wake:", { sessionID, error });
113885
+ });
113886
+ }, PENDING_PARENT_WAKE_RETRY_MS);
113887
+ this.pendingParentWakeTimers.set(sessionID, timer);
113888
+ }
113889
+ clearPendingParentWakeTimer(sessionID) {
113890
+ const timer = this.pendingParentWakeTimers.get(sessionID);
113891
+ if (!timer) {
113892
+ return;
113893
+ }
113894
+ clearTimeout(timer);
113895
+ this.pendingParentWakeTimers.delete(sessionID);
113896
+ }
113683
113897
  hasRunningTasks() {
113684
113898
  for (const task of this.tasks.values()) {
113685
113899
  if (task.status === "running")
@@ -113949,6 +114163,10 @@ ${originalText}`;
113949
114163
  clearTimeout(timer);
113950
114164
  }
113951
114165
  this.idleDeferralTimers.clear();
114166
+ for (const timer of this.pendingParentWakeTimers.values()) {
114167
+ clearTimeout(timer);
114168
+ }
114169
+ this.pendingParentWakeTimers.clear();
113952
114170
  for (const sessionID of trackedSessionIDs) {
113953
114171
  subagentSessions.delete(sessionID);
113954
114172
  SessionCategoryRegistry.remove(sessionID);
@@ -113959,6 +114177,7 @@ ${originalText}`;
113959
114177
  this.notifications.clear();
113960
114178
  this.pendingNotifications.clear();
113961
114179
  this.pendingByParent.clear();
114180
+ this.pendingParentWakes.clear();
113962
114181
  this.notificationQueueByParent.clear();
113963
114182
  this.rootDescendantCounts.clear();
113964
114183
  this.queuesByKey.clear();
@@ -114213,7 +114432,7 @@ async function getOrRegisterClient(options) {
114213
114432
  }
114214
114433
  }
114215
114434
  function parseRegistrationResponse(data) {
114216
- if (!isRecord18(data))
114435
+ if (!isRecord19(data))
114217
114436
  return null;
114218
114437
  const clientId = data.client_id;
114219
114438
  if (typeof clientId !== "string" || clientId.length === 0)
@@ -114224,7 +114443,7 @@ function parseRegistrationResponse(data) {
114224
114443
  }
114225
114444
  return { clientId };
114226
114445
  }
114227
- function isRecord18(value) {
114446
+ function isRecord19(value) {
114228
114447
  return typeof value === "object" && value !== null;
114229
114448
  }
114230
114449
 
@@ -137318,11 +137537,11 @@ async function createTools(args) {
137318
137537
  // src/plugin/chat-params.ts
137319
137538
  init_shared();
137320
137539
  var SAFE_MAX_OUTPUT_TOKENS_FALLBACK = 4096;
137321
- function isRecord19(value) {
137540
+ function isRecord20(value) {
137322
137541
  return typeof value === "object" && value !== null;
137323
137542
  }
137324
137543
  function buildChatParamsInput(raw) {
137325
- if (!isRecord19(raw))
137544
+ if (!isRecord20(raw))
137326
137545
  return null;
137327
137546
  const sessionID = raw.sessionID;
137328
137547
  const agent = raw.agent;
@@ -137331,16 +137550,16 @@ function buildChatParamsInput(raw) {
137331
137550
  const message = raw.message;
137332
137551
  if (typeof sessionID !== "string")
137333
137552
  return null;
137334
- if (!isRecord19(model))
137553
+ if (!isRecord20(model))
137335
137554
  return null;
137336
- if (!isRecord19(provider))
137555
+ if (!isRecord20(provider))
137337
137556
  return null;
137338
- if (!isRecord19(message))
137557
+ if (!isRecord20(message))
137339
137558
  return null;
137340
137559
  let agentName;
137341
137560
  if (typeof agent === "string") {
137342
137561
  agentName = agent;
137343
- } else if (isRecord19(agent)) {
137562
+ } else if (isRecord20(agent)) {
137344
137563
  const name = agent.name;
137345
137564
  if (typeof name === "string") {
137346
137565
  agentName = name;
@@ -137367,12 +137586,12 @@ function buildChatParamsInput(raw) {
137367
137586
  };
137368
137587
  }
137369
137588
  function isChatParamsOutput(raw) {
137370
- if (!isRecord19(raw))
137589
+ if (!isRecord20(raw))
137371
137590
  return false;
137372
- if (!isRecord19(raw.options)) {
137591
+ if (!isRecord20(raw.options)) {
137373
137592
  raw.options = {};
137374
137593
  }
137375
- return isRecord19(raw.options);
137594
+ return isRecord20(raw.options);
137376
137595
  }
137377
137596
  function createChatParamsHandler(args) {
137378
137597
  return async (input, output) => {
@@ -137412,7 +137631,7 @@ function createChatParamsHandler(args) {
137412
137631
  temperature: typeof output.temperature === "number" ? output.temperature : undefined,
137413
137632
  topP: typeof output.topP === "number" ? output.topP : undefined,
137414
137633
  maxTokens: typeof output.maxOutputTokens === "number" ? output.maxOutputTokens : undefined,
137415
- thinking: isRecord19(output.options.thinking) ? output.options.thinking : undefined
137634
+ thinking: isRecord20(output.options.thinking) ? output.options.thinking : undefined
137416
137635
  },
137417
137636
  capabilities
137418
137637
  });
@@ -137469,20 +137688,20 @@ function createChatParamsHandler(args) {
137469
137688
  init_shared();
137470
137689
  var INTERNAL_MARKER_CACHE_LIMIT = 1000;
137471
137690
  var internalMarkerCache = new Map;
137472
- function isRecord20(value) {
137691
+ function isRecord21(value) {
137473
137692
  return typeof value === "object" && value !== null;
137474
137693
  }
137475
137694
  function buildChatHeadersInput(raw) {
137476
- if (!isRecord20(raw))
137695
+ if (!isRecord21(raw))
137477
137696
  return null;
137478
137697
  const sessionID = raw.sessionID;
137479
137698
  const provider = raw.provider;
137480
137699
  const message = raw.message;
137481
137700
  if (typeof sessionID !== "string")
137482
137701
  return null;
137483
- if (!isRecord20(provider) || typeof provider.id !== "string")
137702
+ if (!isRecord21(provider) || typeof provider.id !== "string")
137484
137703
  return null;
137485
- if (!isRecord20(message))
137704
+ if (!isRecord21(message))
137486
137705
  return null;
137487
137706
  return {
137488
137707
  sessionID,
@@ -137494,12 +137713,12 @@ function buildChatHeadersInput(raw) {
137494
137713
  };
137495
137714
  }
137496
137715
  function isChatHeadersOutput(raw) {
137497
- if (!isRecord20(raw))
137716
+ if (!isRecord21(raw))
137498
137717
  return false;
137499
- if (!isRecord20(raw.headers)) {
137718
+ if (!isRecord21(raw.headers)) {
137500
137719
  raw.headers = {};
137501
137720
  }
137502
- return isRecord20(raw.headers);
137721
+ return isRecord21(raw.headers);
137503
137722
  }
137504
137723
  function isCopilotProvider(providerID) {
137505
137724
  return providerID === "github-copilot" || providerID === "github-copilot-enterprise";
@@ -137515,7 +137734,7 @@ async function hasInternalMarker(client2, sessionID, messageID) {
137515
137734
  path: { id: sessionID, messageID }
137516
137735
  });
137517
137736
  const data = response.data;
137518
- if (!isRecord20(data) || !Array.isArray(data.parts)) {
137737
+ if (!isRecord21(data) || !Array.isArray(data.parts)) {
137519
137738
  internalMarkerCache.set(cacheKey, false);
137520
137739
  if (internalMarkerCache.size > INTERNAL_MARKER_CACHE_LIMIT) {
137521
137740
  internalMarkerCache.clear();
@@ -137523,7 +137742,7 @@ async function hasInternalMarker(client2, sessionID, messageID) {
137523
137742
  return false;
137524
137743
  }
137525
137744
  const hasMarker = data.parts.some((part) => {
137526
- if (!isRecord20(part) || part.type !== "text" || typeof part.text !== "string") {
137745
+ if (!isRecord21(part) || part.type !== "text" || typeof part.text !== "string") {
137527
137746
  return false;
137528
137747
  }
137529
137748
  return part.text.includes(OMO_INTERNAL_INITIATOR_MARKER);
@@ -137560,8 +137779,8 @@ function createChatHeadersHandler(args) {
137560
137779
  return;
137561
137780
  if (!isCopilotProvider(normalizedInput.provider.id))
137562
137781
  return;
137563
- const model = isRecord20(input) && isRecord20(input.model) ? input.model : undefined;
137564
- const api = model && isRecord20(model.api) ? model.api : undefined;
137782
+ const model = isRecord21(input) && isRecord21(input.model) ? input.model : undefined;
137783
+ const api = model && isRecord21(model.api) ? model.api : undefined;
137565
137784
  if (api?.npm === "@ai-sdk/github-copilot")
137566
137785
  return;
137567
137786
  if (!await isOmoInternalMessage(normalizedInput, ctx.client))
@@ -138258,7 +138477,16 @@ function createTeamIdleWakeHint(ctx, config2, options) {
138258
138477
  return;
138259
138478
  }
138260
138479
  applyMemberSessionRouting(sessionID, memberEntry);
138261
- await settleAfterSessionIdle(options?.idleSettleMs);
138480
+ if (!await shouldPromptAfterSessionIdle(ctx.client, sessionID, options?.idleSettleMs)) {
138481
+ log("team idle wake hint skipped because session is active", {
138482
+ event: "team-mode-idle-wake-hint-active-session",
138483
+ teamRunId: runtimeState.teamRunId,
138484
+ memberName: memberEntry.name,
138485
+ sessionID,
138486
+ unreadCount: unreadMessages.length
138487
+ });
138488
+ return;
138489
+ }
138262
138490
  await ctx.client.session.promptAsync({
138263
138491
  path: { id: sessionID },
138264
138492
  body: buildMemberPromptBody(memberEntry, buildWakeHint(unreadMessages.length)),
@@ -138529,14 +138757,14 @@ function normalizeSessionStatusToIdle(input) {
138529
138757
 
138530
138758
  // src/plugin/event.ts
138531
138759
  init_event_session_id();
138532
- function isRecord21(value) {
138760
+ function isRecord22(value) {
138533
138761
  return typeof value === "object" && value !== null;
138534
138762
  }
138535
138763
  function normalizeFallbackModelID(modelID) {
138536
138764
  return modelID.replace(/-thinking$/i, "").replace(/-max$/i, "").replace(/-high$/i, "");
138537
138765
  }
138538
138766
  function extractErrorName3(error) {
138539
- if (isRecord21(error) && typeof error.name === "string")
138767
+ if (isRecord22(error) && typeof error.name === "string")
138540
138768
  return error.name;
138541
138769
  if (error instanceof Error)
138542
138770
  return error.name;
@@ -138547,16 +138775,16 @@ function extractErrorMessage3(error) {
138547
138775
  return "";
138548
138776
  if (typeof error === "string")
138549
138777
  return error;
138550
- if (isRecord21(error)) {
138778
+ if (isRecord22(error)) {
138551
138779
  const candidates = [
138552
138780
  error.data,
138553
- isRecord21(error.data) ? error.data.error : undefined,
138781
+ isRecord22(error.data) ? error.data.error : undefined,
138554
138782
  error.error,
138555
138783
  error.cause,
138556
138784
  error
138557
138785
  ];
138558
138786
  for (const candidate of candidates) {
138559
- if (isRecord21(candidate) && typeof candidate.message === "string" && candidate.message.length > 0) {
138787
+ if (isRecord22(candidate) && typeof candidate.message === "string" && candidate.message.length > 0) {
138560
138788
  return candidate.message;
138561
138789
  }
138562
138790
  }
@@ -138638,7 +138866,7 @@ function createEventHandler2(args) {
138638
138866
  if (input.event.type.startsWith("message.") || input.event.type.startsWith("tool.")) {
138639
138867
  return resolveMessageEventSessionID(properties);
138640
138868
  }
138641
- const record3 = isRecord21(properties) ? properties : undefined;
138869
+ const record3 = isRecord22(properties) ? properties : undefined;
138642
138870
  const sessionID = record3?.sessionID;
138643
138871
  return typeof sessionID === "string" && sessionID.length > 0 ? sessionID : undefined;
138644
138872
  };
@@ -138737,7 +138965,7 @@ function createEventHandler2(args) {
138737
138965
  ...launchAgent ? { agent: launchAgent } : {},
138738
138966
  ...launchModel ? { model: launchModel } : {},
138739
138967
  ...launchVariant ? { variant: launchVariant } : {},
138740
- parts: [{ type: "text", text: "continue" }]
138968
+ parts: [createInternalAgentContinuationTextPart("continue")]
138741
138969
  },
138742
138970
  query: { directory: pluginContext.directory }
138743
138971
  };
@@ -139044,7 +139272,7 @@ function createEventHandler2(args) {
139044
139272
  });
139045
139273
  await pluginContext.client.session.prompt({
139046
139274
  path: { id: sessionID },
139047
- body: { parts: [{ type: "text", text: "continue" }] },
139275
+ body: { parts: [createInternalAgentContinuationTextPart("continue")] },
139048
139276
  query: { directory: pluginContext.directory }
139049
139277
  }).catch(() => {});
139050
139278
  }
@@ -4,3 +4,11 @@ export declare function createInternalAgentTextPart(text: string): {
4
4
  type: "text";
5
5
  text: string;
6
6
  };
7
+ export declare function createInternalAgentContinuationTextPart(text: string): {
8
+ type: "text";
9
+ text: string;
10
+ synthetic: true;
11
+ metadata: {
12
+ compaction_continue: true;
13
+ };
14
+ };
@@ -1,3 +1,4 @@
1
1
  export declare const DEFAULT_TIMEOUT_MS = 60000;
2
2
  export declare const BLOCKED_TMUX_SUBCOMMANDS: string[];
3
+ export declare const PROHIBITED_TMUX_SUBCOMMANDS: string[];
3
4
  export declare const INTERACTIVE_BASH_DESCRIPTION = "WARNING: This is TMUX ONLY. Pass tmux subcommands directly (without 'tmux' prefix).\n\nExamples: new-session -d -s omo-dev, send-keys -t omo-dev \"vim\" Enter\n\nFor TUI apps needing ongoing interaction (vim, htop, pudb). One-shot commands \u2192 use Bash with &.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-openagent",
3
- "version": "4.1.0",
3
+ "version": "4.1.1",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "./dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -81,17 +81,17 @@
81
81
  "zod": "^4.3.0"
82
82
  },
83
83
  "optionalDependencies": {
84
- "oh-my-openagent-darwin-arm64": "4.1.0",
85
- "oh-my-openagent-darwin-x64": "4.1.0",
86
- "oh-my-openagent-darwin-x64-baseline": "4.1.0",
87
- "oh-my-openagent-linux-arm64": "4.1.0",
88
- "oh-my-openagent-linux-arm64-musl": "4.1.0",
89
- "oh-my-openagent-linux-x64": "4.1.0",
90
- "oh-my-openagent-linux-x64-baseline": "4.1.0",
91
- "oh-my-openagent-linux-x64-musl": "4.1.0",
92
- "oh-my-openagent-linux-x64-musl-baseline": "4.1.0",
93
- "oh-my-openagent-windows-x64": "4.1.0",
94
- "oh-my-openagent-windows-x64-baseline": "4.1.0"
84
+ "oh-my-openagent-darwin-arm64": "4.1.1",
85
+ "oh-my-openagent-darwin-x64": "4.1.1",
86
+ "oh-my-openagent-darwin-x64-baseline": "4.1.1",
87
+ "oh-my-openagent-linux-arm64": "4.1.1",
88
+ "oh-my-openagent-linux-arm64-musl": "4.1.1",
89
+ "oh-my-openagent-linux-x64": "4.1.1",
90
+ "oh-my-openagent-linux-x64-baseline": "4.1.1",
91
+ "oh-my-openagent-linux-x64-musl": "4.1.1",
92
+ "oh-my-openagent-linux-x64-musl-baseline": "4.1.1",
93
+ "oh-my-openagent-windows-x64": "4.1.1",
94
+ "oh-my-openagent-windows-x64-baseline": "4.1.1"
95
95
  },
96
96
  "overrides": {
97
97
  "hono": "^4.12.18",