oh-my-opencode-slim 2.0.0-beta.11 → 2.0.0-beta.12

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.
@@ -21,6 +21,9 @@ export declare const MAX_POLL_TIME_MS: number;
21
21
  export declare const FALLBACK_FAILOVER_TIMEOUT_MS = 15000;
22
22
  export declare const DEFAULT_MAX_SUBAGENT_DEPTH = 3;
23
23
  export declare const PHASE_REMINDER_TEXT = "!IMPORTANT! Scheduler workflow: plan lanes/dependencies \u2192 dispatch background specialists \u2192 track task IDs \u2192 wait for hook-driven completion or use task_status only when needed \u2192 reconcile terminal results \u2192 verify. Do not consume running-job output or advance dependent work. !END!";
24
+ export declare const WRITABLE_FILE_OPERATIONS_RULES = "**File Operations Rules**:\n- Prefer dedicated file tools for normal code work: glob/grep/ast_grep_search for discovery, read for file contents, and edit/write/apply_patch for targeted source changes.\n- Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.\n- Shell is acceptable for bulk or mechanical filesystem changes when it is clearer or safer than many individual edits (for example: truncate generated logs, remove build artifacts, batch rename/move files), especially when the user explicitly asks for that shell operation.\n- Before destructive or broad shell operations, verify the target set and quote paths. Prefer a dry-run/listing first when practical.\n- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.";
25
+ export declare const READONLY_FILE_OPERATIONS_RULES = "**File Operations Rules**:\n- READ-ONLY: inspect and report; do not modify files.\n- Prefer dedicated file tools for codebase inspection: glob/grep/ast_grep_search for discovery and read for file contents.\n- Bash is allowed for non-mutating diagnostics and shell-native inspection when it is the clearest tool, but not for modifying files.\n- Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.";
26
+ export declare const NO_SHELL_READONLY_FILE_OPERATIONS_RULES = "**File Operations Rules**:\n- READ-ONLY: inspect and report; do not modify files.\n- Use glob/grep/ast_grep_search for discovery and read for file contents.\n- Do not use bash or shell commands.";
24
27
  export declare const TMUX_SPAWN_DELAY_MS = 500;
25
28
  export declare const COUNCILLOR_STAGGER_MS = 250;
26
29
  export declare const STABLE_POLLS_THRESHOLD = 3;
package/dist/index.js CHANGED
@@ -18309,6 +18309,21 @@ var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
18309
18309
  var MAX_POLL_TIME_MS = 5 * 60 * 1000;
18310
18310
  var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
18311
18311
  var PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → wait for hook-driven completion or use task_status only when needed → reconcile terminal results → verify. Do not consume running-job output or advance dependent work. !END!`;
18312
+ var WRITABLE_FILE_OPERATIONS_RULES = `**File Operations Rules**:
18313
+ - Prefer dedicated file tools for normal code work: glob/grep/ast_grep_search for discovery, read for file contents, and edit/write/apply_patch for targeted source changes.
18314
+ - Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.
18315
+ - Shell is acceptable for bulk or mechanical filesystem changes when it is clearer or safer than many individual edits (for example: truncate generated logs, remove build artifacts, batch rename/move files), especially when the user explicitly asks for that shell operation.
18316
+ - Before destructive or broad shell operations, verify the target set and quote paths. Prefer a dry-run/listing first when practical.
18317
+ - Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
18318
+ var READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
18319
+ - READ-ONLY: inspect and report; do not modify files.
18320
+ - Prefer dedicated file tools for codebase inspection: glob/grep/ast_grep_search for discovery and read for file contents.
18321
+ - Bash is allowed for non-mutating diagnostics and shell-native inspection when it is the clearest tool, but not for modifying files.
18322
+ - Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
18323
+ var NO_SHELL_READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
18324
+ - READ-ONLY: inspect and report; do not modify files.
18325
+ - Use glob/grep/ast_grep_search for discovery and read for file contents.
18326
+ - Do not use bash or shell commands.`;
18312
18327
  var TMUX_SPAWN_DELAY_MS = 500;
18313
18328
  var COUNCILLOR_STAGGER_MS = 250;
18314
18329
  var DEFAULT_DISABLED_AGENTS = ["observer"];
@@ -19093,12 +19108,7 @@ Review available agents and lane rules.
19093
19108
  - Do not immediately wait after spawning independent background tasks unless the next step truly depends on their result
19094
19109
  - Reconcile results, resolve conflicts, and gate dependent lanes
19095
19110
 
19096
- **File operations rules:**
19097
- - Always use dedicated file tools for file I/O.
19098
- - Search files/code with \`glob\`, \`grep\`, or \`ast_grep_search\`.
19099
- - Read files with \`read\`. Never use \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or bash commands to read file contents.
19100
- - Edit files with \`apply_patch\`. Never use shell redirection, \`echo\`, \`printf\`, or heredocs for file content unless no file tool can do the job.
19101
- - Use \`bash\` only for execution: git, package managers, tests, builds, scripts, or diagnostics.
19111
+ ${WRITABLE_FILE_OPERATIONS_RULES}
19102
19112
 
19103
19113
  ## 4. Plan and Parallelize
19104
19114
  Build a short work graph before dispatching:
@@ -19120,12 +19130,15 @@ Balance: respect dependencies, avoid parallelizing what must be sequential, and
19120
19130
  - Continue orchestration while tasks run only when useful: planning, scheduling independent lanes, preparing synthesis, or asking needed user questions.
19121
19131
  - If no useful independent work remains, stop after a brief status response; do not call \`task_status\` just to wait. OpenCode will resume you when the background completion event arrives.
19122
19132
  - Use \`task_status(wait: true, timeout_ms: ...)\` only when you actively need a result before a dependent step or final response and no completion event has arrived yet.
19133
+ - If \`task_status(wait: true)\` times out and reports the task still \`running\`, the delegated lane is still owned by that specialist. Do not treat the timeout as failure, cancellation, or permission to do the same work yourself.
19134
+ - For dependent work, either call \`task_status(wait: true)\` again with the same reasonable interval, or stop with a brief waiting status and let the completion event resume you.
19123
19135
  - Parallel background tasks are allowed only when their write scopes do not conflict.
19124
19136
  - Final response requires relevant tasks to be terminal and reconciled.
19125
19137
 
19126
19138
  ### Background Job Discipline
19127
19139
  - Every background task owns its declared lane until terminal.
19128
19140
  - Do not duplicate, undermine, or race a running lane.
19141
+ - A polling timeout is not terminal. The lane remains running until a terminal completion/error/cancel event is observed or the user explicitly cancels it.
19129
19142
  - After dispatch, classify the next step:
19130
19143
  1. independent: continue,
19131
19144
  2. dependent: wait/poll,
@@ -19255,11 +19268,7 @@ var COUNCIL_AGENT_PROMPT = `You are the Council agent — a multi-LLM orchestrat
19255
19268
  - Be transparent about trade-offs when different approaches have valid pros/cons
19256
19269
  - Don't just average responses — choose the best approach and improve upon it
19257
19270
 
19258
- **File Operations Rules**:
19259
- - Use dedicated tools for file I/O if local files must be inspected
19260
- - Search files/code with glob, grep, or ast_grep_search
19261
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19262
- - Use bash only for execution/diagnostics, never for file I/O
19271
+ ${READONLY_FILE_OPERATIONS_RULES}
19263
19272
 
19264
19273
  **Required Output Format**:
19265
19274
  Always include these sections in your final response:
@@ -19372,11 +19381,7 @@ var COUNCILLOR_PROMPT = `You are a councillor in a multi-model council.
19372
19381
 
19373
19382
  You CANNOT edit files, write files, run shell commands, or delegate to other agents. You are an advisor, not an implementer.
19374
19383
 
19375
- **File Operations Rules**:
19376
- - READ-ONLY: do not modify files
19377
- - Search files/code with glob, grep, or ast_grep_search
19378
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19379
- - Do not use bash or shell commands
19384
+ ${NO_SHELL_READONLY_FILE_OPERATIONS_RULES}
19380
19385
 
19381
19386
  **Behavior**:
19382
19387
  - **Examine the codebase** before answering — your read access is what makes council valuable. Don't guess at code you can see.
@@ -19466,12 +19471,7 @@ var DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who crea
19466
19471
  - Prioritize visual excellence—code perfection comes second
19467
19472
  - Use grounded, normal, regular english - don't use jargon or overly technical language
19468
19473
 
19469
- ## File Operations Rules
19470
- - Always use dedicated file tools for file I/O
19471
- - Search files/code with glob, grep, or ast_grep_search
19472
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19473
- - Edit/write files with write, edit, or apply_patch. Never use shell redirection, echo, printf, or heredocs for file content unless no file tool can do the job
19474
- - Use bash only for execution: git, package managers, tests, builds, scripts, or diagnostics
19474
+ ${WRITABLE_FILE_OPERATIONS_RULES}
19475
19475
 
19476
19476
  ## Review Responsibilities
19477
19477
  - Review existing UI for usability, responsiveness, visual consistency, and polish when asked
@@ -19510,11 +19510,7 @@ var EXPLORER_PROMPT = `You are Explorer - a fast codebase navigation specialist.
19510
19510
  - **Structural patterns** (function shapes, class structures): ast_grep_search
19511
19511
  - **File discovery** (find by name/extension): glob
19512
19512
 
19513
- **File Operations Rules**:
19514
- - READ-ONLY: Search and report, don't modify files
19515
- - Search files/code with glob, grep, or ast_grep_search
19516
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19517
- - Use bash only for execution/diagnostics, never for file I/O
19513
+ ${READONLY_FILE_OPERATIONS_RULES}
19518
19514
 
19519
19515
  **Behavior**:
19520
19516
  - Be fast and thorough
@@ -19570,12 +19566,7 @@ var FIXER_PROMPT = `You are Fixer - a fast, focused implementation specialist.
19570
19566
  - Run relevant validation when requested or clearly applicable (otherwise note as skipped with reason)
19571
19567
  - Report completion with summary of changes
19572
19568
 
19573
- **File Operations Rules**:
19574
- - Always use dedicated file tools for file I/O
19575
- - Search files/code with glob, grep, or ast_grep_search
19576
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19577
- - Edit/write files with write, edit, or apply_patch. Never use shell redirection, echo, printf, or heredocs for file content unless no file tool can do the job
19578
- - Use bash only for execution: git, package managers, tests, builds, scripts, or diagnostics
19569
+ ${WRITABLE_FILE_OPERATIONS_RULES}
19579
19570
 
19580
19571
  **Constraints**:
19581
19572
  - NO external research (no websearch, context7, grep_app)
@@ -19642,11 +19633,7 @@ var LIBRARIAN_PROMPT = `You are Librarian - a research specialist for codebases
19642
19633
  - grep_app: Search GitHub repositories
19643
19634
  - websearch: General web search for docs
19644
19635
 
19645
- **File Operations Rules**:
19646
- - Use dedicated tools for file I/O when local files must be inspected
19647
- - Search files/code with glob, grep, or ast_grep_search
19648
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19649
- - Use bash only for execution/diagnostics, never for file I/O
19636
+ ${READONLY_FILE_OPERATIONS_RULES}
19650
19637
 
19651
19638
  **Behavior**:
19652
19639
  - Provide evidence-based answers with sources
@@ -19693,10 +19680,7 @@ var OBSERVER_PROMPT = `You are Observer — a visual analysis specialist.
19693
19680
  - Match the language of the request
19694
19681
  - If info not found, state clearly what's missing
19695
19682
 
19696
- **File Operations Rules**:
19697
- - READ-ONLY: do not modify files
19698
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19699
- - Use bash only for execution/diagnostics, never for file I/O
19683
+ ${READONLY_FILE_OPERATIONS_RULES}
19700
19684
  `;
19701
19685
  function createObserverAgent(model, customPrompt, customAppendPrompt) {
19702
19686
  let prompt = OBSERVER_PROMPT;
@@ -19742,11 +19726,7 @@ var ORACLE_PROMPT = `You are Oracle - a strategic technical advisor and code rev
19742
19726
  - Focus on strategy, not execution
19743
19727
  - Point to specific files/lines when relevant
19744
19728
 
19745
- **File Operations Rules**:
19746
- - READ-ONLY: do not modify files
19747
- - Search files/code with glob, grep, or ast_grep_search
19748
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19749
- - Use bash only for execution/diagnostics, never for file I/O
19729
+ ${READONLY_FILE_OPERATIONS_RULES}
19750
19730
  `;
19751
19731
  function createOracleAgent(model, customPrompt, customAppendPrompt) {
19752
19732
  let prompt = ORACLE_PROMPT;
@@ -22819,6 +22799,26 @@ class BackgroundJobBoard {
22819
22799
  resultSummary: status.result
22820
22800
  });
22821
22801
  }
22802
+ markRunningFromLiveSession(taskID, now = Date.now()) {
22803
+ const existing = this.jobs.get(taskID);
22804
+ if (!existing)
22805
+ return;
22806
+ const isStaleCancellation = existing.state === "cancelled" || existing.state === "reconciled" && existing.terminalState === "cancelled";
22807
+ if (!isStaleCancellation)
22808
+ return existing;
22809
+ const updated = {
22810
+ ...existing,
22811
+ state: "running",
22812
+ timedOut: false,
22813
+ terminalUnreconciled: false,
22814
+ updatedAt: now,
22815
+ completedAt: undefined,
22816
+ terminalState: undefined,
22817
+ resultSummary: undefined
22818
+ };
22819
+ this.jobs.set(taskID, updated);
22820
+ return updated;
22821
+ }
22822
22822
  markReconciled(taskID, now = Date.now()) {
22823
22823
  const existing = this.jobs.get(taskID);
22824
22824
  if (!existing)
@@ -22925,7 +22925,7 @@ class BackgroundJobBoard {
22925
22925
  return [
22926
22926
  "### Background Job Board",
22927
22927
  "SENTINEL: background-job-board-v2",
22928
- "Use task_status for running jobs. Reconcile terminal jobs before final response. Reuse only completed/reconciled sessions for the same specialist/context.",
22928
+ "Use task_status for running jobs. Reconcile terminal jobs before final response. Reuse any non-running session for the same specialist/context.",
22929
22929
  "",
22930
22930
  "#### Active / Unreconciled",
22931
22931
  ...active.length > 0 ? active.map((job) => formatJob(job, now)) : ["- none"],
@@ -22953,8 +22953,10 @@ class BackgroundJobBoard {
22953
22953
  }
22954
22954
  }
22955
22955
  formatReusableJob(job) {
22956
+ const terminal = job.terminalState ?? terminalStateOf(job.state);
22957
+ const reconciliation = job.terminalUnreconciled ? "unreconciled" : "reconciled";
22956
22958
  const lines = [
22957
- `- ${job.alias} / ${job.taskID} / ${job.agent} / completed, reconciled`,
22959
+ `- ${job.alias} / ${job.taskID} / ${job.agent} / ${terminal ?? job.state}, ${reconciliation}`,
22958
22960
  ` Objective: ${job.objective || job.description}`
22959
22961
  ];
22960
22962
  const context = formatContextFiles(job.contextFiles, this.readContextMaxFiles);
@@ -22979,7 +22981,7 @@ function deriveTaskSessionLabel(input) {
22979
22981
  return firstPromptLine ? firstPromptLine.slice(0, 48) : `recent ${input.agentType} task`;
22980
22982
  }
22981
22983
  function isReusable(job) {
22982
- return job.state === "reconciled" && job.terminalState === "completed";
22984
+ return job.state !== "running";
22983
22985
  }
22984
22986
  function terminalStateOf(state) {
22985
22987
  return state === "completed" || state === "error" || state === "cancelled" ? state : undefined;
@@ -24086,14 +24088,33 @@ function createTaskSessionManagerHook(_ctx, options) {
24086
24088
  const status = parseTaskStatusOutput(output);
24087
24089
  if (!status)
24088
24090
  return;
24091
+ log("[task-session-manager] parsed task status output", {
24092
+ taskID: status.taskID,
24093
+ state: status.state,
24094
+ timedOut: status.timedOut,
24095
+ hasResult: Boolean(status.result)
24096
+ });
24089
24097
  const updated = backgroundJobBoard.updateStatus({
24090
24098
  taskID: status.taskID,
24091
24099
  state: status.state,
24092
24100
  timedOut: status.timedOut,
24093
24101
  resultSummary: status.result
24094
24102
  });
24095
- if (!updated)
24103
+ if (!updated) {
24104
+ log("[task-session-manager] ignored status for unknown background job", {
24105
+ taskID: status.taskID,
24106
+ state: status.state
24107
+ });
24096
24108
  return;
24109
+ }
24110
+ log("[task-session-manager] background job status updated", {
24111
+ taskID: updated.taskID,
24112
+ alias: updated.alias,
24113
+ parentSessionID: updated.parentSessionID,
24114
+ state: updated.state,
24115
+ terminalUnreconciled: updated.terminalUnreconciled,
24116
+ timedOut: updated.timedOut
24117
+ });
24097
24118
  if (updated.terminalUnreconciled) {
24098
24119
  pendingManagedTaskIds.delete(updated.taskID);
24099
24120
  backgroundJobBoard.addContext(updated.taskID, contextFilesForPrompt(contextByTask.get(updated.taskID)));
@@ -24123,6 +24144,13 @@ function createTaskSessionManagerHook(_ctx, options) {
24123
24144
  const updated = updateBackgroundJobFromOutput(part.text);
24124
24145
  if (!updated)
24125
24146
  return;
24147
+ log("[task-session-manager] processed injected background completion", {
24148
+ taskID: updated.taskID,
24149
+ alias: updated.alias,
24150
+ parentSessionID: updated.parentSessionID,
24151
+ state: updated.state,
24152
+ occurrenceId
24153
+ });
24126
24154
  rememberProcessedInjectedCompletion(occurrenceId);
24127
24155
  return updated;
24128
24156
  }
@@ -24179,6 +24207,10 @@ function createTaskSessionManagerHook(_ctx, options) {
24179
24207
  const taskIDs = backgroundJobBoard.list(parentSessionID).filter((job) => job.terminalUnreconciled).map((job) => job.taskID);
24180
24208
  if (taskIDs.length === 0)
24181
24209
  return;
24210
+ log("[task-session-manager] terminal jobs injected for reconciliation", {
24211
+ parentSessionID,
24212
+ taskIDs
24213
+ });
24182
24214
  const existing = terminalJobsInjectedByParent.get(parentSessionID) ?? new Set;
24183
24215
  for (const taskID of taskIDs) {
24184
24216
  existing.add(taskID);
@@ -24189,6 +24221,10 @@ function createTaskSessionManagerHook(_ctx, options) {
24189
24221
  const taskIDs = terminalJobsInjectedByParent.get(parentSessionID);
24190
24222
  if (!taskIDs)
24191
24223
  return;
24224
+ log("[task-session-manager] reconciling injected terminal jobs", {
24225
+ parentSessionID,
24226
+ taskIDs: [...taskIDs]
24227
+ });
24192
24228
  for (const taskID of taskIDs) {
24193
24229
  backgroundJobBoard.markReconciled(taskID);
24194
24230
  }
@@ -24272,13 +24308,21 @@ function createTaskSessionManagerHook(_ctx, options) {
24272
24308
  return;
24273
24309
  const launch = parseTaskLaunchOutput(output.output);
24274
24310
  if (launch) {
24275
- backgroundJobBoard.registerLaunch({
24311
+ const record = backgroundJobBoard.registerLaunch({
24276
24312
  taskID: launch.taskID,
24277
24313
  parentSessionID: pending.parentSessionId,
24278
24314
  agent: pending.agentType,
24279
24315
  description: pending.label,
24280
24316
  objective: pending.label
24281
24317
  });
24318
+ log("[task-session-manager] background task launch registered", {
24319
+ taskID: record.taskID,
24320
+ alias: record.alias,
24321
+ parentSessionID: record.parentSessionID,
24322
+ agent: record.agent,
24323
+ description: record.description,
24324
+ state: record.state
24325
+ });
24282
24326
  backgroundJobBoard.addContext(launch.taskID, contextFilesForPrompt(contextByTask.get(launch.taskID)));
24283
24327
  pendingManagedTaskIds.add(launch.taskID);
24284
24328
  return;
@@ -24344,6 +24388,11 @@ function createTaskSessionManagerHook(_ctx, options) {
24344
24388
  event: async (input) => {
24345
24389
  if (input.event.type === "session.created") {
24346
24390
  const info = input.event.properties?.info;
24391
+ log("[task-session-manager] session.created observed", {
24392
+ sessionID: info?.id,
24393
+ parentSessionID: info?.parentID,
24394
+ managesParent: info?.parentID ? options.shouldManageSession(info.parentID) : false
24395
+ });
24347
24396
  if (info?.id && info.parentID && options.shouldManageSession(info.parentID)) {
24348
24397
  pendingManagedTaskIds.add(info.id);
24349
24398
  }
@@ -24351,6 +24400,11 @@ function createTaskSessionManagerHook(_ctx, options) {
24351
24400
  }
24352
24401
  if (input.event.type === "session.idle" || input.event.type === "session.status" && input.event.properties?.status?.type === "idle") {
24353
24402
  const sessionId2 = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
24403
+ log("[task-session-manager] idle/status idle observed", {
24404
+ sessionID: sessionId2,
24405
+ managesSession: sessionId2 ? options.shouldManageSession(sessionId2) : false,
24406
+ terminalJobsPending: sessionId2 ? terminalJobsInjectedByParent.get(sessionId2)?.size ?? 0 : 0
24407
+ });
24354
24408
  if (sessionId2 && options.shouldManageSession(sessionId2)) {
24355
24409
  reconcileInjectedTerminalJobs(sessionId2);
24356
24410
  }
@@ -24363,11 +24417,34 @@ function createTaskSessionManagerHook(_ctx, options) {
24363
24417
  }
24364
24418
  return;
24365
24419
  }
24420
+ if (input.event.type === "session.status" && input.event.properties?.status?.type === "busy") {
24421
+ const sessionId2 = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
24422
+ const before = sessionId2 ? backgroundJobBoard.get(sessionId2) : undefined;
24423
+ const updated = sessionId2 ? backgroundJobBoard.markRunningFromLiveSession(sessionId2) : undefined;
24424
+ log("[task-session-manager] busy/status busy observed", {
24425
+ sessionID: sessionId2,
24426
+ managesSession: sessionId2 ? options.shouldManageSession(sessionId2) : false,
24427
+ previousState: before?.state,
24428
+ previousTerminalState: before?.terminalState,
24429
+ updatedState: updated?.state
24430
+ });
24431
+ return;
24432
+ }
24366
24433
  if (input.event.type !== "session.deleted")
24367
24434
  return;
24368
24435
  const sessionId = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
24369
24436
  if (!sessionId)
24370
24437
  return;
24438
+ log("[task-session-manager] session.deleted observed; clearing job state", {
24439
+ sessionID: sessionId,
24440
+ deletedJob: backgroundJobBoard.get(sessionId) ? {
24441
+ state: backgroundJobBoard.get(sessionId)?.state,
24442
+ parentSessionID: backgroundJobBoard.get(sessionId)?.parentSessionID,
24443
+ alias: backgroundJobBoard.get(sessionId)?.alias
24444
+ } : undefined,
24445
+ childJobCount: backgroundJobBoard.list(sessionId).length,
24446
+ managesSession: options.shouldManageSession(sessionId)
24447
+ });
24371
24448
  backgroundJobBoard.drop(sessionId);
24372
24449
  backgroundJobBoard.clearParent(sessionId);
24373
24450
  terminalJobsInjectedByParent.delete(sessionId);
@@ -29690,6 +29767,7 @@ class MultiplexerSessionManager {
29690
29767
  parentId,
29691
29768
  title,
29692
29769
  directory,
29770
+ ownerInstanceId: this.instanceId,
29693
29771
  createdAt: now,
29694
29772
  lastSeenAt: now,
29695
29773
  seenInStatus: false
@@ -29715,7 +29793,9 @@ class MultiplexerSessionManager {
29715
29793
  instanceId: this.instanceId,
29716
29794
  sessionId: sessionId2,
29717
29795
  tracked: this.sessions.has(sessionId2),
29718
- known: this.knownSessions.has(sessionId2)
29796
+ known: this.knownSessions.has(sessionId2),
29797
+ ownerInstanceId: this.sessions.get(sessionId2)?.ownerInstanceId,
29798
+ backgroundJobState: this.backgroundJobBoard?.get(sessionId2)?.state
29719
29799
  });
29720
29800
  await this.closeSession(sessionId2, "idle");
29721
29801
  return;
@@ -29726,6 +29806,14 @@ class MultiplexerSessionManager {
29726
29806
  if (!sessionId)
29727
29807
  return;
29728
29808
  if (event.properties?.status?.type === "idle") {
29809
+ log("[multiplexer-session-manager] session status idle received", {
29810
+ instanceId: this.instanceId,
29811
+ sessionId,
29812
+ tracked: this.sessions.has(sessionId),
29813
+ known: this.knownSessions.has(sessionId),
29814
+ ownerInstanceId: this.sessions.get(sessionId)?.ownerInstanceId,
29815
+ backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
29816
+ });
29729
29817
  await this.closeSession(sessionId, "idle");
29730
29818
  return;
29731
29819
  }
@@ -29734,7 +29822,9 @@ class MultiplexerSessionManager {
29734
29822
  instanceId: this.instanceId,
29735
29823
  sessionId,
29736
29824
  tracked: this.sessions.has(sessionId),
29737
- known: this.knownSessions.has(sessionId)
29825
+ known: this.knownSessions.has(sessionId),
29826
+ ownerInstanceId: this.sessions.get(sessionId)?.ownerInstanceId,
29827
+ backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
29738
29828
  });
29739
29829
  await this.respawnIfKnown(sessionId);
29740
29830
  }
@@ -29749,7 +29839,11 @@ class MultiplexerSessionManager {
29749
29839
  return;
29750
29840
  log("[multiplexer-session-manager] session deleted, closing pane", {
29751
29841
  instanceId: this.instanceId,
29752
- sessionId
29842
+ sessionId,
29843
+ tracked: this.sessions.has(sessionId),
29844
+ known: this.knownSessions.has(sessionId),
29845
+ ownerInstanceId: this.sessions.get(sessionId)?.ownerInstanceId,
29846
+ backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
29753
29847
  });
29754
29848
  await this.closeSession(sessionId, "deleted");
29755
29849
  }
@@ -29780,6 +29874,15 @@ class MultiplexerSessionManager {
29780
29874
  const now = Date.now();
29781
29875
  const sessionsToClose = [];
29782
29876
  for (const [sessionId, tracked] of this.sessions.entries()) {
29877
+ if (tracked.ownerInstanceId !== this.instanceId) {
29878
+ log("[multiplexer-session-manager] skipping non-owner poll close", {
29879
+ instanceId: this.instanceId,
29880
+ ownerInstanceId: tracked.ownerInstanceId,
29881
+ sessionId,
29882
+ paneId: tracked.paneId
29883
+ });
29884
+ continue;
29885
+ }
29783
29886
  const status = allStatuses[sessionId];
29784
29887
  const isIdle = status?.type === "idle";
29785
29888
  if (status) {
@@ -29790,7 +29893,7 @@ class MultiplexerSessionManager {
29790
29893
  tracked.missingSince = now;
29791
29894
  }
29792
29895
  const missingTooLong = !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
29793
- const shouldKeepRunningBackgroundJob = missingTooLong && this.isRunningBackgroundJob(sessionId);
29896
+ const shouldKeepRunningBackgroundJob = (isIdle || missingTooLong) && this.isRunningBackgroundJob(sessionId);
29794
29897
  if (isIdle || missingTooLong) {
29795
29898
  if (shouldKeepRunningBackgroundJob) {
29796
29899
  log("[multiplexer-session-manager] keeping running background pane", {
@@ -29848,12 +29951,35 @@ class MultiplexerSessionManager {
29848
29951
  });
29849
29952
  return;
29850
29953
  }
29954
+ if (tracked.ownerInstanceId !== this.instanceId) {
29955
+ log("[multiplexer-session-manager] close skipped; non-owner instance", {
29956
+ instanceId: this.instanceId,
29957
+ ownerInstanceId: tracked.ownerInstanceId,
29958
+ sessionId,
29959
+ paneId: tracked.paneId,
29960
+ reason
29961
+ });
29962
+ return;
29963
+ }
29964
+ if (reason === "idle" && this.isRunningBackgroundJob(sessionId)) {
29965
+ log("[multiplexer-session-manager] close skipped; background job running", {
29966
+ instanceId: this.instanceId,
29967
+ sessionId,
29968
+ paneId: tracked.paneId,
29969
+ reason,
29970
+ backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state
29971
+ });
29972
+ return;
29973
+ }
29851
29974
  this.sessions.delete(sessionId);
29852
29975
  log("[multiplexer-session-manager] closing session pane", {
29853
29976
  instanceId: this.instanceId,
29854
29977
  sessionId,
29855
29978
  paneId: tracked.paneId,
29856
- reason
29979
+ reason,
29980
+ backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state,
29981
+ parentId: tracked.parentId,
29982
+ title: tracked.title
29857
29983
  });
29858
29984
  const closePromise = this.multiplexer.closePane(tracked.paneId).then(() => {
29859
29985
  return;
@@ -29927,6 +30053,7 @@ class MultiplexerSessionManager {
29927
30053
  parentId: known.parentId,
29928
30054
  title: known.title,
29929
30055
  directory: known.directory,
30056
+ ownerInstanceId: this.instanceId,
29930
30057
  createdAt: now,
29931
30058
  lastSeenAt: now,
29932
30059
  seenInStatus: false
@@ -30586,7 +30713,8 @@ Use only for obsolete, wrong, conflicting, or user-requested cancellation. Accep
30586
30713
  ].join(`
30587
30714
  `);
30588
30715
  }
30589
- if (job.state !== "running") {
30716
+ const shouldAbort = job.state === "running" || job.state === "cancelled" || job.state === "reconciled" && job.terminalState === "cancelled";
30717
+ if (!shouldAbort) {
30590
30718
  return [
30591
30719
  `task_id: ${job.taskID}`,
30592
30720
  `state: ${job.state}`,
package/dist/tui.js CHANGED
@@ -69,6 +69,21 @@ var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
69
69
  var MAX_POLL_TIME_MS = 5 * 60 * 1000;
70
70
  var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
71
71
  var PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → wait for hook-driven completion or use task_status only when needed → reconcile terminal results → verify. Do not consume running-job output or advance dependent work. !END!`;
72
+ var WRITABLE_FILE_OPERATIONS_RULES = `**File Operations Rules**:
73
+ - Prefer dedicated file tools for normal code work: glob/grep/ast_grep_search for discovery, read for file contents, and edit/write/apply_patch for targeted source changes.
74
+ - Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.
75
+ - Shell is acceptable for bulk or mechanical filesystem changes when it is clearer or safer than many individual edits (for example: truncate generated logs, remove build artifacts, batch rename/move files), especially when the user explicitly asks for that shell operation.
76
+ - Before destructive or broad shell operations, verify the target set and quote paths. Prefer a dry-run/listing first when practical.
77
+ - Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
78
+ var READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
79
+ - READ-ONLY: inspect and report; do not modify files.
80
+ - Prefer dedicated file tools for codebase inspection: glob/grep/ast_grep_search for discovery and read for file contents.
81
+ - Bash is allowed for non-mutating diagnostics and shell-native inspection when it is the clearest tool, but not for modifying files.
82
+ - Do not use cat/head/tail/sed/awk only to read code into context; use read/grep unless a shell pipeline is genuinely the better diagnostic.`;
83
+ var NO_SHELL_READONLY_FILE_OPERATIONS_RULES = `**File Operations Rules**:
84
+ - READ-ONLY: inspect and report; do not modify files.
85
+ - Use glob/grep/ast_grep_search for discovery and read for file contents.
86
+ - Do not use bash or shell commands.`;
72
87
  var TMUX_SPAWN_DELAY_MS = 500;
73
88
  var COUNCILLOR_STAGGER_MS = 250;
74
89
  var DEFAULT_DISABLED_AGENTS = ["observer"];
@@ -55,6 +55,7 @@ export declare class BackgroundJobBoard {
55
55
  registerLaunch(input: BackgroundJobLaunchInput): BackgroundJobRecord;
56
56
  updateStatus(input: BackgroundJobStatusInput): BackgroundJobRecord | undefined;
57
57
  updateFromStatusOutput(output: string): BackgroundJobRecord | undefined;
58
+ markRunningFromLiveSession(taskID: string, now?: number): BackgroundJobRecord | undefined;
58
59
  markReconciled(taskID: string, now?: number): BackgroundJobRecord | undefined;
59
60
  markCancelled(taskID: string, reason?: string, now?: number): BackgroundJobRecord | undefined;
60
61
  get(taskID: string): BackgroundJobRecord | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-slim",
3
- "version": "2.0.0-beta.11",
3
+ "version": "2.0.0-beta.12",
4
4
  "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",