oh-my-opencode-slim 2.0.0-beta.10 → 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"];
@@ -18990,8 +19005,9 @@ var AGENT_DESCRIPTIONS = {
18990
19005
  - Lane: UI/UX design, related edits, design polish and review
18991
19006
  - Permissions: read_files, write_files
18992
19007
  - Stats: 10x better UI/UX than orchestrator
18993
- - Capabilities: Goot design taste, visual relevant edits, interactions, responsive layouts, design systems with aesthetic intent, deep UI/UX knowledge.
18994
- - Weakness: copywriting, when calling ask to use grounded, normal wording
19008
+ - Capabilities: Good design taste, visual relevant edits, interactions, responsive layouts, design systems with aesthetic intent, deep UI/UX knowledge.
19009
+ - Owns visual and interaction quality: layout, hierarchy, spacing, motion, affordances, responsive behavior, and overall feel.
19010
+ - Weakness: copywriting. Ask designer to use grounded, normal wording, then have orchestrator review/fix copy after design work without changing visual or interaction intent.
18995
19011
  - Avoid: "Let me us designer how it should look and implement yourself" → instead: "Let me ask designer to design and implement the UI/UX changes for me"
18996
19012
  - **Delegate when:** User-facing interfaces needing polish • Responsive layouts • UX-critical components (forms, nav, dashboards) • Visual consistency systems • Animations/micro-interactions • Landing/marketing pages • Refining functional→delightful • Reviewing existing UI/UX quality
18997
19013
  - **Don't delegate when:** Backend/logic with no visual • Quick prototypes where design doesn't matter yet.
@@ -19004,8 +19020,8 @@ var AGENT_DESCRIPTIONS = {
19004
19020
  - Weakness: design, taste
19005
19021
  - Tools/Constraints: Execution-focused—no research, no architectural decisions
19006
19022
  - **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Parallelization benefits: Task involves multiple folders and multiple files modification, scoping work per folder and spawning parallel @fixers for each folder.
19007
- - **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work
19008
- - **Rule of thumb:** Implementation are needed, schedule @fixer with clear scope. Bigger or lots of edits should be split by ownership and dispatched as parallel background @fixer lanes when safe. Editing files which includes design, ui, ux changes → schedule @designer.`,
19023
+ - **Don't delegate when:** Needs discovery/research/decisions • Single small change (<20 lines, one file) • Unclear requirements needing iteration • Explaining to fixer > doing • Tight integration with your current work • Requires design taste, visual hierarchy, interaction polish, responsive layout decisions, animation/motion, component feel, or UI copy/design trade-offs
19024
+ - **Rule of thumb:** Headless/mechanical implementation @fixer. User-visible design or polish @designer. If @designer already set direction, @fixer may only do bounded mechanical follow-up that preserves that design exactly.`,
19009
19025
  council: `@council
19010
19026
  - Lane: High-stakes multi-model decision support
19011
19027
  - Role: Multi-LLM consensus engine that runs several councillors, synthesizes their views, and returns a structured council report.
@@ -19092,12 +19108,7 @@ Review available agents and lane rules.
19092
19108
  - Do not immediately wait after spawning independent background tasks unless the next step truly depends on their result
19093
19109
  - Reconcile results, resolve conflicts, and gate dependent lanes
19094
19110
 
19095
- **File operations rules:**
19096
- - Always use dedicated file tools for file I/O.
19097
- - Search files/code with \`glob\`, \`grep\`, or \`ast_grep_search\`.
19098
- - Read files with \`read\`. Never use \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or bash commands to read file contents.
19099
- - Edit files with \`apply_patch\`. Never use shell redirection, \`echo\`, \`printf\`, or heredocs for file content unless no file tool can do the job.
19100
- - Use \`bash\` only for execution: git, package managers, tests, builds, scripts, or diagnostics.
19111
+ ${WRITABLE_FILE_OPERATIONS_RULES}
19101
19112
 
19102
19113
  ## 4. Plan and Parallelize
19103
19114
  Build a short work graph before dispatching:
@@ -19119,12 +19130,15 @@ Balance: respect dependencies, avoid parallelizing what must be sequential, and
19119
19130
  - Continue orchestration while tasks run only when useful: planning, scheduling independent lanes, preparing synthesis, or asking needed user questions.
19120
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.
19121
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.
19122
19135
  - Parallel background tasks are allowed only when their write scopes do not conflict.
19123
19136
  - Final response requires relevant tasks to be terminal and reconciled.
19124
19137
 
19125
19138
  ### Background Job Discipline
19126
19139
  - Every background task owns its declared lane until terminal.
19127
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.
19128
19142
  - After dispatch, classify the next step:
19129
19143
  1. independent: continue,
19130
19144
  2. dependent: wait/poll,
@@ -19134,6 +19148,13 @@ Balance: respect dependencies, avoid parallelizing what must be sequential, and
19134
19148
  - Cancellation is not rollback: if cancelling a writer, inspect and reconcile partial file changes before launching a replacement lane.
19135
19149
  - Never finalize work that depends on unresolved background jobs.
19136
19150
 
19151
+ ### Design Handoff Discipline
19152
+ - When @designer completes UI/UX work, treat layout, spacing, hierarchy, motion, color, affordances, and component feel as intentional design output.
19153
+ - Do not later simplify, normalize, or refactor it in ways that flatten the design.
19154
+ - The orchestrator should review and improve user-facing copy after designer work, because designer copy may be weak.
19155
+ - Copy edits must preserve the designer's visual structure and interaction intent.
19156
+ - If follow-up work is purely mechanical and preserves the design exactly, @fixer can handle it. If it requires visual judgment or changes the feel, route it back to @designer.
19157
+
19137
19158
  ### Session Reuse
19138
19159
  - Smartly reuse an available specialist session - context reuse saves time and tokens
19139
19160
  - When too much unrelated, and really needed, start a fresh session with the specialist
@@ -19247,11 +19268,7 @@ var COUNCIL_AGENT_PROMPT = `You are the Council agent — a multi-LLM orchestrat
19247
19268
  - Be transparent about trade-offs when different approaches have valid pros/cons
19248
19269
  - Don't just average responses — choose the best approach and improve upon it
19249
19270
 
19250
- **File Operations Rules**:
19251
- - Use dedicated tools for file I/O if local files must be inspected
19252
- - Search files/code with glob, grep, or ast_grep_search
19253
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19254
- - Use bash only for execution/diagnostics, never for file I/O
19271
+ ${READONLY_FILE_OPERATIONS_RULES}
19255
19272
 
19256
19273
  **Required Output Format**:
19257
19274
  Always include these sections in your final response:
@@ -19364,11 +19381,7 @@ var COUNCILLOR_PROMPT = `You are a councillor in a multi-model council.
19364
19381
 
19365
19382
  You CANNOT edit files, write files, run shell commands, or delegate to other agents. You are an advisor, not an implementer.
19366
19383
 
19367
- **File Operations Rules**:
19368
- - READ-ONLY: do not modify files
19369
- - Search files/code with glob, grep, or ast_grep_search
19370
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19371
- - Do not use bash or shell commands
19384
+ ${NO_SHELL_READONLY_FILE_OPERATIONS_RULES}
19372
19385
 
19373
19386
  **Behavior**:
19374
19387
  - **Examine the codebase** before answering — your read access is what makes council valuable. Don't guess at code you can see.
@@ -19456,13 +19469,9 @@ var DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who crea
19456
19469
  - Respect existing design systems when present
19457
19470
  - Leverage component libraries where available
19458
19471
  - Prioritize visual excellence—code perfection comes second
19472
+ - Use grounded, normal, regular english - don't use jargon or overly technical language
19459
19473
 
19460
- ## File Operations Rules
19461
- - Always use dedicated file tools for file I/O
19462
- - Search files/code with glob, grep, or ast_grep_search
19463
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19464
- - 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
19465
- - Use bash only for execution: git, package managers, tests, builds, scripts, or diagnostics
19474
+ ${WRITABLE_FILE_OPERATIONS_RULES}
19466
19475
 
19467
19476
  ## Review Responsibilities
19468
19477
  - Review existing UI for usability, responsiveness, visual consistency, and polish when asked
@@ -19501,11 +19510,7 @@ var EXPLORER_PROMPT = `You are Explorer - a fast codebase navigation specialist.
19501
19510
  - **Structural patterns** (function shapes, class structures): ast_grep_search
19502
19511
  - **File discovery** (find by name/extension): glob
19503
19512
 
19504
- **File Operations Rules**:
19505
- - READ-ONLY: Search and report, don't modify files
19506
- - Search files/code with glob, grep, or ast_grep_search
19507
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19508
- - Use bash only for execution/diagnostics, never for file I/O
19513
+ ${READONLY_FILE_OPERATIONS_RULES}
19509
19514
 
19510
19515
  **Behavior**:
19511
19516
  - Be fast and thorough
@@ -19561,12 +19566,7 @@ var FIXER_PROMPT = `You are Fixer - a fast, focused implementation specialist.
19561
19566
  - Run relevant validation when requested or clearly applicable (otherwise note as skipped with reason)
19562
19567
  - Report completion with summary of changes
19563
19568
 
19564
- **File Operations Rules**:
19565
- - Always use dedicated file tools for file I/O
19566
- - Search files/code with glob, grep, or ast_grep_search
19567
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19568
- - 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
19569
- - Use bash only for execution: git, package managers, tests, builds, scripts, or diagnostics
19569
+ ${WRITABLE_FILE_OPERATIONS_RULES}
19570
19570
 
19571
19571
  **Constraints**:
19572
19572
  - NO external research (no websearch, context7, grep_app)
@@ -19633,11 +19633,7 @@ var LIBRARIAN_PROMPT = `You are Librarian - a research specialist for codebases
19633
19633
  - grep_app: Search GitHub repositories
19634
19634
  - websearch: General web search for docs
19635
19635
 
19636
- **File Operations Rules**:
19637
- - Use dedicated tools for file I/O when local files must be inspected
19638
- - Search files/code with glob, grep, or ast_grep_search
19639
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19640
- - Use bash only for execution/diagnostics, never for file I/O
19636
+ ${READONLY_FILE_OPERATIONS_RULES}
19641
19637
 
19642
19638
  **Behavior**:
19643
19639
  - Provide evidence-based answers with sources
@@ -19684,10 +19680,7 @@ var OBSERVER_PROMPT = `You are Observer — a visual analysis specialist.
19684
19680
  - Match the language of the request
19685
19681
  - If info not found, state clearly what's missing
19686
19682
 
19687
- **File Operations Rules**:
19688
- - READ-ONLY: do not modify files
19689
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19690
- - Use bash only for execution/diagnostics, never for file I/O
19683
+ ${READONLY_FILE_OPERATIONS_RULES}
19691
19684
  `;
19692
19685
  function createObserverAgent(model, customPrompt, customAppendPrompt) {
19693
19686
  let prompt = OBSERVER_PROMPT;
@@ -19733,11 +19726,7 @@ var ORACLE_PROMPT = `You are Oracle - a strategic technical advisor and code rev
19733
19726
  - Focus on strategy, not execution
19734
19727
  - Point to specific files/lines when relevant
19735
19728
 
19736
- **File Operations Rules**:
19737
- - READ-ONLY: do not modify files
19738
- - Search files/code with glob, grep, or ast_grep_search
19739
- - Read files with read. Never use cat, head, tail, sed, awk, or bash commands to read file contents
19740
- - Use bash only for execution/diagnostics, never for file I/O
19729
+ ${READONLY_FILE_OPERATIONS_RULES}
19741
19730
  `;
19742
19731
  function createOracleAgent(model, customPrompt, customAppendPrompt) {
19743
19732
  let prompt = ORACLE_PROMPT;
@@ -22810,6 +22799,26 @@ class BackgroundJobBoard {
22810
22799
  resultSummary: status.result
22811
22800
  });
22812
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
+ }
22813
22822
  markReconciled(taskID, now = Date.now()) {
22814
22823
  const existing = this.jobs.get(taskID);
22815
22824
  if (!existing)
@@ -22916,7 +22925,7 @@ class BackgroundJobBoard {
22916
22925
  return [
22917
22926
  "### Background Job Board",
22918
22927
  "SENTINEL: background-job-board-v2",
22919
- "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.",
22920
22929
  "",
22921
22930
  "#### Active / Unreconciled",
22922
22931
  ...active.length > 0 ? active.map((job) => formatJob(job, now)) : ["- none"],
@@ -22944,8 +22953,10 @@ class BackgroundJobBoard {
22944
22953
  }
22945
22954
  }
22946
22955
  formatReusableJob(job) {
22956
+ const terminal = job.terminalState ?? terminalStateOf(job.state);
22957
+ const reconciliation = job.terminalUnreconciled ? "unreconciled" : "reconciled";
22947
22958
  const lines = [
22948
- `- ${job.alias} / ${job.taskID} / ${job.agent} / completed, reconciled`,
22959
+ `- ${job.alias} / ${job.taskID} / ${job.agent} / ${terminal ?? job.state}, ${reconciliation}`,
22949
22960
  ` Objective: ${job.objective || job.description}`
22950
22961
  ];
22951
22962
  const context = formatContextFiles(job.contextFiles, this.readContextMaxFiles);
@@ -22970,7 +22981,7 @@ function deriveTaskSessionLabel(input) {
22970
22981
  return firstPromptLine ? firstPromptLine.slice(0, 48) : `recent ${input.agentType} task`;
22971
22982
  }
22972
22983
  function isReusable(job) {
22973
- return job.state === "reconciled" && job.terminalState === "completed";
22984
+ return job.state !== "running";
22974
22985
  }
22975
22986
  function terminalStateOf(state) {
22976
22987
  return state === "completed" || state === "error" || state === "cancelled" ? state : undefined;
@@ -24077,14 +24088,33 @@ function createTaskSessionManagerHook(_ctx, options) {
24077
24088
  const status = parseTaskStatusOutput(output);
24078
24089
  if (!status)
24079
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
+ });
24080
24097
  const updated = backgroundJobBoard.updateStatus({
24081
24098
  taskID: status.taskID,
24082
24099
  state: status.state,
24083
24100
  timedOut: status.timedOut,
24084
24101
  resultSummary: status.result
24085
24102
  });
24086
- 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
+ });
24087
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
+ });
24088
24118
  if (updated.terminalUnreconciled) {
24089
24119
  pendingManagedTaskIds.delete(updated.taskID);
24090
24120
  backgroundJobBoard.addContext(updated.taskID, contextFilesForPrompt(contextByTask.get(updated.taskID)));
@@ -24114,6 +24144,13 @@ function createTaskSessionManagerHook(_ctx, options) {
24114
24144
  const updated = updateBackgroundJobFromOutput(part.text);
24115
24145
  if (!updated)
24116
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
+ });
24117
24154
  rememberProcessedInjectedCompletion(occurrenceId);
24118
24155
  return updated;
24119
24156
  }
@@ -24170,6 +24207,10 @@ function createTaskSessionManagerHook(_ctx, options) {
24170
24207
  const taskIDs = backgroundJobBoard.list(parentSessionID).filter((job) => job.terminalUnreconciled).map((job) => job.taskID);
24171
24208
  if (taskIDs.length === 0)
24172
24209
  return;
24210
+ log("[task-session-manager] terminal jobs injected for reconciliation", {
24211
+ parentSessionID,
24212
+ taskIDs
24213
+ });
24173
24214
  const existing = terminalJobsInjectedByParent.get(parentSessionID) ?? new Set;
24174
24215
  for (const taskID of taskIDs) {
24175
24216
  existing.add(taskID);
@@ -24180,6 +24221,10 @@ function createTaskSessionManagerHook(_ctx, options) {
24180
24221
  const taskIDs = terminalJobsInjectedByParent.get(parentSessionID);
24181
24222
  if (!taskIDs)
24182
24223
  return;
24224
+ log("[task-session-manager] reconciling injected terminal jobs", {
24225
+ parentSessionID,
24226
+ taskIDs: [...taskIDs]
24227
+ });
24183
24228
  for (const taskID of taskIDs) {
24184
24229
  backgroundJobBoard.markReconciled(taskID);
24185
24230
  }
@@ -24263,13 +24308,21 @@ function createTaskSessionManagerHook(_ctx, options) {
24263
24308
  return;
24264
24309
  const launch = parseTaskLaunchOutput(output.output);
24265
24310
  if (launch) {
24266
- backgroundJobBoard.registerLaunch({
24311
+ const record = backgroundJobBoard.registerLaunch({
24267
24312
  taskID: launch.taskID,
24268
24313
  parentSessionID: pending.parentSessionId,
24269
24314
  agent: pending.agentType,
24270
24315
  description: pending.label,
24271
24316
  objective: pending.label
24272
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
+ });
24273
24326
  backgroundJobBoard.addContext(launch.taskID, contextFilesForPrompt(contextByTask.get(launch.taskID)));
24274
24327
  pendingManagedTaskIds.add(launch.taskID);
24275
24328
  return;
@@ -24335,6 +24388,11 @@ function createTaskSessionManagerHook(_ctx, options) {
24335
24388
  event: async (input) => {
24336
24389
  if (input.event.type === "session.created") {
24337
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
+ });
24338
24396
  if (info?.id && info.parentID && options.shouldManageSession(info.parentID)) {
24339
24397
  pendingManagedTaskIds.add(info.id);
24340
24398
  }
@@ -24342,6 +24400,11 @@ function createTaskSessionManagerHook(_ctx, options) {
24342
24400
  }
24343
24401
  if (input.event.type === "session.idle" || input.event.type === "session.status" && input.event.properties?.status?.type === "idle") {
24344
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
+ });
24345
24408
  if (sessionId2 && options.shouldManageSession(sessionId2)) {
24346
24409
  reconcileInjectedTerminalJobs(sessionId2);
24347
24410
  }
@@ -24354,11 +24417,34 @@ function createTaskSessionManagerHook(_ctx, options) {
24354
24417
  }
24355
24418
  return;
24356
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
+ }
24357
24433
  if (input.event.type !== "session.deleted")
24358
24434
  return;
24359
24435
  const sessionId = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
24360
24436
  if (!sessionId)
24361
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
+ });
24362
24448
  backgroundJobBoard.drop(sessionId);
24363
24449
  backgroundJobBoard.clearParent(sessionId);
24364
24450
  terminalJobsInjectedByParent.delete(sessionId);
@@ -29681,6 +29767,7 @@ class MultiplexerSessionManager {
29681
29767
  parentId,
29682
29768
  title,
29683
29769
  directory,
29770
+ ownerInstanceId: this.instanceId,
29684
29771
  createdAt: now,
29685
29772
  lastSeenAt: now,
29686
29773
  seenInStatus: false
@@ -29706,7 +29793,9 @@ class MultiplexerSessionManager {
29706
29793
  instanceId: this.instanceId,
29707
29794
  sessionId: sessionId2,
29708
29795
  tracked: this.sessions.has(sessionId2),
29709
- 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
29710
29799
  });
29711
29800
  await this.closeSession(sessionId2, "idle");
29712
29801
  return;
@@ -29717,6 +29806,14 @@ class MultiplexerSessionManager {
29717
29806
  if (!sessionId)
29718
29807
  return;
29719
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
+ });
29720
29817
  await this.closeSession(sessionId, "idle");
29721
29818
  return;
29722
29819
  }
@@ -29725,7 +29822,9 @@ class MultiplexerSessionManager {
29725
29822
  instanceId: this.instanceId,
29726
29823
  sessionId,
29727
29824
  tracked: this.sessions.has(sessionId),
29728
- 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
29729
29828
  });
29730
29829
  await this.respawnIfKnown(sessionId);
29731
29830
  }
@@ -29740,7 +29839,11 @@ class MultiplexerSessionManager {
29740
29839
  return;
29741
29840
  log("[multiplexer-session-manager] session deleted, closing pane", {
29742
29841
  instanceId: this.instanceId,
29743
- 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
29744
29847
  });
29745
29848
  await this.closeSession(sessionId, "deleted");
29746
29849
  }
@@ -29771,6 +29874,15 @@ class MultiplexerSessionManager {
29771
29874
  const now = Date.now();
29772
29875
  const sessionsToClose = [];
29773
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
+ }
29774
29886
  const status = allStatuses[sessionId];
29775
29887
  const isIdle = status?.type === "idle";
29776
29888
  if (status) {
@@ -29781,7 +29893,7 @@ class MultiplexerSessionManager {
29781
29893
  tracked.missingSince = now;
29782
29894
  }
29783
29895
  const missingTooLong = !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
29784
- const shouldKeepRunningBackgroundJob = missingTooLong && this.isRunningBackgroundJob(sessionId);
29896
+ const shouldKeepRunningBackgroundJob = (isIdle || missingTooLong) && this.isRunningBackgroundJob(sessionId);
29785
29897
  if (isIdle || missingTooLong) {
29786
29898
  if (shouldKeepRunningBackgroundJob) {
29787
29899
  log("[multiplexer-session-manager] keeping running background pane", {
@@ -29839,12 +29951,35 @@ class MultiplexerSessionManager {
29839
29951
  });
29840
29952
  return;
29841
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
+ }
29842
29974
  this.sessions.delete(sessionId);
29843
29975
  log("[multiplexer-session-manager] closing session pane", {
29844
29976
  instanceId: this.instanceId,
29845
29977
  sessionId,
29846
29978
  paneId: tracked.paneId,
29847
- reason
29979
+ reason,
29980
+ backgroundJobState: this.backgroundJobBoard?.get(sessionId)?.state,
29981
+ parentId: tracked.parentId,
29982
+ title: tracked.title
29848
29983
  });
29849
29984
  const closePromise = this.multiplexer.closePane(tracked.paneId).then(() => {
29850
29985
  return;
@@ -29918,6 +30053,7 @@ class MultiplexerSessionManager {
29918
30053
  parentId: known.parentId,
29919
30054
  title: known.title,
29920
30055
  directory: known.directory,
30056
+ ownerInstanceId: this.instanceId,
29921
30057
  createdAt: now,
29922
30058
  lastSeenAt: now,
29923
30059
  seenInStatus: false
@@ -30577,7 +30713,8 @@ Use only for obsolete, wrong, conflicting, or user-requested cancellation. Accep
30577
30713
  ].join(`
30578
30714
  `);
30579
30715
  }
30580
- if (job.state !== "running") {
30716
+ const shouldAbort = job.state === "running" || job.state === "cancelled" || job.state === "reconciled" && job.terminalState === "cancelled";
30717
+ if (!shouldAbort) {
30581
30718
  return [
30582
30719
  `task_id: ${job.taskID}`,
30583
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.10",
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",
@@ -33,8 +33,30 @@ Required behavior:
33
33
  - after each phase, validate, update the deepwork file, prepare the plan file
34
34
  for oracle review and ask `@oracle` to review the phase result, fix
35
35
  actionable issues, then continue;
36
+ - when a phase includes `@designer`, preserve designer intent across later
37
+ phases. Use `@fixer` only for mechanical follow-up that does not alter the
38
+ UI/UX;
36
39
  - finish with final validation and a concise summary.
37
40
 
41
+ ## Designer Handoff Guardrail
42
+
43
+ When a deepwork phase includes `@designer`, treat the delivered UI/UX as
44
+ accepted design intent for later phases. Record any important design decisions in
45
+ the deepwork file before continuing.
46
+
47
+ After designer work:
48
+
49
+ - preserve layout, rhythm, hierarchy, motion, spacing, color, affordances,
50
+ responsiveness, and component feel;
51
+ - review and improve user-facing copy with grounded, normal wording, but do not
52
+ change visual structure or interaction intent;
53
+ - route follow-up visual, responsive, motion, hierarchy, polish, or
54
+ component-feel changes back to `@designer`;
55
+ - use `@fixer` only for bounded mechanical follow-up that preserves the design
56
+ exactly, such as wiring, tests, type fixes, or non-visual behavior changes;
57
+ - if design intent must change, record why in the deepwork file before changing
58
+ it.
59
+
38
60
  ## Deepwork File
39
61
 
40
62
  Create a task-specific file such as: