oh-my-opencode-slim 2.0.0-beta.7 → 2.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -18308,7 +18308,7 @@ var POLL_INTERVAL_BACKGROUND_MS = 2000;
18308
18308
  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
- var PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → poll task_status → reconcile terminal results → verify. Do not consume running-job output or advance dependent work. !END!`;
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
18312
  var TMUX_SPAWN_DELAY_MS = 500;
18313
18313
  var COUNCILLOR_STAGGER_MS = 250;
18314
18314
  var DEFAULT_DISABLED_AGENTS = ["observer"];
@@ -18964,51 +18964,48 @@ ${customAppendPrompt}`;
18964
18964
  }
18965
18965
  var AGENT_DESCRIPTIONS = {
18966
18966
  explorer: `@explorer
18967
- - Lane: Codebase discovery and reconnaissance
18968
- - Role: Parallel search specialist for discovering unknowns across the codebase
18969
- - Permissions: Read files
18967
+ - Lane: Fast codebase recon that returns compressed context
18968
+ - Permissions: read_files
18970
18969
  - Stats: 2x faster codebase search than orchestrator, 1/2 cost of orchestrator
18971
18970
  - Capabilities: Glob, grep, AST queries to locate files, symbols, patterns
18972
18971
  - **Delegate when:** Need to discover what exists before planning • Parallel searches speed discovery • Need summarized map vs full contents • Broad/uncertain scope
18973
18972
  - **Don't delegate when:** Know the path and need actual content • Need full file anyway • Single specific lookup • About to edit the file`,
18974
18973
  librarian: `@librarian
18975
18974
  - Lane: External knowledge and library research, fast web research
18976
- - Role: Authoritative source for current library docs and API references, web information retrieval
18977
- - Permissions: External docs/search MCPs; no file edits
18978
- - Stats: 10x better finding up-to-date library docs than orchestrator, 1/2 cost of orchestrator
18979
- - Capabilities: Fetches latest official docs, examples, API signatures, version-specific behavior via grep_app MCP
18975
+ - Role: Authoritative source for current library docs, API references, examples, bug investigations, and web retrieval
18976
+ - Stats: 2x faster web research than orchestrator, 1/2 cost of orchestrator
18980
18977
  - **Delegate when:** Libraries with frequent API changes (React, Next.js, AI SDKs) • Complex APIs needing official examples (ORMs, auth) • Version-specific behavior matters • Unfamiliar library • Edge cases or advanced features • Nuanced best practices • Working on fixing tricky bug or problem and need latest web research information
18981
18978
  - **Don't delegate when:** Standard usage you're confident • Simple stable APIs • General programming knowledge • Info already in conversation • Built-in language features
18982
18979
  - **Rule of thumb:** "How does this library work?" → @librarian. "How does programming work?" → answer directly. How does others solve or workaround this tricky issue?" → @librarian.`,
18983
18980
  oracle: `@oracle
18984
18981
  - Lane: Architecture, risk, debugging strategy, and review
18985
18982
  - Role: Strategic advisor for high-stakes decisions and persistent problems, code reviewer
18986
- - Permissions: Read files
18983
+ - Permissions: read_files
18987
18984
  - Stats: 5x better decision maker, problem solver, investigator than orchestrator, 0.8x speed of orchestrator, same cost.
18988
18985
  - Capabilities: Deep architectural reasoning, system-level trade-offs, complex debugging, code review, simplification, maintainability review
18989
18986
  - **Delegate when:** Major architectural decisions with long-term impact • Problems persisting after 2+ fix attempts • High-risk multi-system refactors • Costly trade-offs (performance vs maintainability) • Complex debugging with unclear root cause • Security/scalability/data integrity decisions • Genuinely uncertain and cost of wrong choice is high • When a workflow calls for a **reviewer** subagent • Code needs simplification or YAGNI scrutiny
18990
18987
  - **Don't delegate when:** Routine decisions you're confident about • First bug fix attempt • Straightforward trade-offs • Tactical "how" vs strategic "should" • Time-sensitive good-enough decisions • Quick research/testing can answer
18991
18988
  - **Rule of thumb:** Need senior architect review? → @oracle. Need code review or simplification? → @oracle. Routine coordination or final synthesis? → handle directly.`,
18992
18989
  designer: `@designer
18993
- - Lane: UI/UX design, related edits, design polishign, and review
18994
- - Role: UI/UX specialist for intentional, polished experiences
18995
- - Permissions: Read/write files
18990
+ - Lane: UI/UX design, related edits, design polish and review
18991
+ - Permissions: read_files, write_files
18996
18992
  - Stats: 10x better UI/UX than orchestrator
18997
18993
  - Capabilities: Goot design taste, visual relevant edits, interactions, responsive layouts, design systems with aesthetic intent, deep UI/UX knowledge.
18998
- - Weakness: copywriting, needs Orchestrator control, dictation, reviews
18994
+ - Weakness: copywriting, when calling ask to use grounded, normal wording
18995
+ - 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"
18999
18996
  - **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
19000
- - **Don't delegate when:** Backend/logic with no visual • Quick prototypes where design doesn't matter yet
18997
+ - **Don't delegate when:** Backend/logic with no visual • Quick prototypes where design doesn't matter yet.
19001
18998
  - **Rule of thumb:** Users see it and polish matters? → @designer. Headless/functional implementation? → schedule @fixer.`,
19002
18999
  fixer: `@fixer
19003
- - Lane: Bounded implementation and test execution.
19004
- - Role: Fast execution specialist for well-defined tasks.
19005
- - Permissions: Read/write files
19006
- - Stats: 2x faster code edits, 1/2 cost of orchestrator, 0.8x quality of orchestrator
19007
- - Weakness: design taste
19000
+ - Lane: Bounded implementation and executioner
19001
+ - Role: Fast execution specialist for well-defined tasks
19002
+ - Permissions: read_files, write_files
19003
+ - Stats: 2x faster code edits, 1/2 cost of orchestrator
19004
+ - Weakness: design, taste
19008
19005
  - Tools/Constraints: Execution-focused—no research, no architectural decisions
19009
- - **Delegate when:** For implementation work, think and triage first. If the change is non-trivial or multi-file, hand bounded execution to @fixer • Writing or updating tests • Tasks that touch test files, fixtures, mocks, or test helpers. Parallelization benefits: Task involves multiple folders and multiple files modificaiton, scoping work per folder and spawning parallel @fixers for each folder.
19010
- - **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 • Sequential dependencies
19011
- - **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.`,
19006
+ - **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.`,
19012
19009
  council: `@council
19013
19010
  - Lane: High-stakes multi-model decision support
19014
19011
  - Role: Multi-LLM consensus engine that runs several councillors, synthesizes their views, and returns a structured council report.
@@ -19033,7 +19030,7 @@ var AGENT_DESCRIPTIONS = {
19033
19030
  };
19034
19031
  var VALIDATION_ROUTING = [
19035
19032
  "- Route UI/UX validation and review to @designer",
19036
- "- Route code review, simplification, maintainability review, and YAGNI checks to @oracle",
19033
+ "- Route code review, code simplification and maintainability review checks to @oracle",
19037
19034
  "- Route implementation to @fixer or multiple @fixer instances for maximum parallel execution",
19038
19035
  "- Route visual/media analysis and interpretation to @observer",
19039
19036
  "- If a request spans multiple lanes, delegate only the lanes that add clear value"
@@ -19066,6 +19063,7 @@ function buildOrchestratorPrompt(disabledAgents) {
19066
19063
  You are a workflow manager for coding work. Your job is to plan, schedule, delegate, monitor, reconcile, and verify specialist-agent work. You are not the default implementation worker.
19067
19064
 
19068
19065
  Optimize for quality, speed, cost, and reliability by dispatching the right specialist lanes, tracking background task state, and integrating terminal results into one coherent outcome.
19066
+ You have perfect understanding of agent's context management, understand well the cost of building content and reusing context of existing agents when it's best or when it's best to spawn a new agent.
19069
19067
  </Role>
19070
19068
 
19071
19069
  <Agents>
@@ -19080,22 +19078,26 @@ ${enabledAgents}
19080
19078
  Parse request: explicit requirements + implicit needs.
19081
19079
 
19082
19080
  ## 2. Path Selection
19083
- Evaluate approach by: quality, speed, cost, reliability.
19081
+ Evaluate approach by: quality, speed and cost.
19084
19082
  Choose the path that optimizes all four.
19085
19083
 
19086
- Classify work into lanes: discovery, external knowledge, implementation, UI/UX, review/risk, visual analysis, and final verification.
19087
-
19088
19084
  ## 3. Delegation Check
19089
- **STOP. Review specialists before acting.**
19090
-
19091
- !!! Review available agents and lane rules. Decide what to schedule, what depends on what, and what minimal direct coordination is needed. !!!
19085
+ Review available agents and lane rules.
19092
19086
 
19093
19087
  **Dispatch efficiency:**
19094
19088
  - Reference paths/lines, don't paste files (\`src/app.ts:42\` not full contents)
19095
- - Provide context summaries, let specialists read what they need
19096
19089
  - Brief user on delegation goal before each call
19097
- - Keep direct work limited to clarification, minimal routing context, todos, synthesis, and final checks
19098
19090
  - For trivial conversational answers or tiny mechanical edits, direct execution is allowed when scheduling overhead would clearly dominate
19091
+ - Record task IDs, state, and advisory ownership/dependency labels
19092
+ - Do not immediately wait after spawning independent background tasks unless the next step truly depends on their result
19093
+ - Reconcile results, resolve conflicts, and gate dependent lanes
19094
+
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.
19099
19101
 
19100
19102
  ## 4. Plan and Parallelize
19101
19103
  Build a short work graph before dispatching:
@@ -19113,21 +19115,24 @@ Balance: respect dependencies, avoid parallelizing what must be sequential, and
19113
19115
  - Delegated specialists should be launched as background tasks whenever work can run independently: use \`task(..., background: true)\`.
19114
19116
  - A dispatch returns a task/session ID immediately; it does not mean completion.
19115
19117
  - Track each task ID with specialist, objective, state, and any advisory ownership/dependency labels from the dispatch plan.
19116
- - Continue orchestration while tasks run: planning, scheduling independent lanes, preparing synthesis, and asking needed user questions.
19117
- - Poll or wait with \`task_status(wait: true, timeout_ms: ...)\` before consuming outputs or starting dependent work.
19118
+ - Background completion is event/hook-driven: when a background task finishes, OpenCode injects a follow-up message with the terminal result.
19119
+ - Continue orchestration while tasks run only when useful: planning, scheduling independent lanes, preparing synthesis, or asking needed user questions.
19120
+ - 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
+ - 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.
19118
19122
  - Parallel background tasks are allowed only when their write scopes do not conflict.
19119
19123
  - Final response requires relevant tasks to be terminal and reconciled.
19120
19124
 
19121
- ## 5. Dispatch
19122
- 1. Split work into independent and dependency-ordered lanes
19123
- 2. Plan advisory ownership for write-capable lanes
19124
- 3. Dispatch independent specialists as background tasks
19125
- 4. Record task IDs, state, and advisory ownership/dependency labels
19126
- 5. Continue only independent orchestration while jobs run
19127
- 6. Poll/wait for terminal results with \`task_status(wait: true, timeout_ms: ...)\`
19128
- 7. Reconcile results, resolve conflicts, and gate dependent lanes
19129
- 8. Dispatch follow-up jobs if needed
19130
- 9. Verify final state
19125
+ ### Background Job Discipline
19126
+ - Every background task owns its declared lane until terminal.
19127
+ - Do not duplicate, undermine, or race a running lane.
19128
+ - After dispatch, classify the next step:
19129
+ 1. independent: continue,
19130
+ 2. dependent: wait/poll,
19131
+ 3. no useful independent work: stop and let hook-driven completion resume.
19132
+ - Before editing files or spawning another writer, compare against running job scopes.
19133
+ - Use \`cancel_task\` only when the user asks, or when a running lane is obsolete, wrong, or conflicts with a safer replacement plan.
19134
+ - Cancellation is not rollback: if cancelling a writer, inspect and reconcile partial file changes before launching a replacement lane.
19135
+ - Never finalize work that depends on unresolved background jobs.
19131
19136
 
19132
19137
  ### Session Reuse
19133
19138
  - Smartly reuse an available specialist session - context reuse saves time and tokens
@@ -19242,6 +19247,12 @@ var COUNCIL_AGENT_PROMPT = `You are the Council agent — a multi-LLM orchestrat
19242
19247
  - Be transparent about trade-offs when different approaches have valid pros/cons
19243
19248
  - Don't just average responses — choose the best approach and improve upon it
19244
19249
 
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
19255
+
19245
19256
  **Required Output Format**:
19246
19257
  Always include these sections in your final response:
19247
19258
 
@@ -19353,6 +19364,12 @@ var COUNCILLOR_PROMPT = `You are a councillor in a multi-model council.
19353
19364
 
19354
19365
  You CANNOT edit files, write files, run shell commands, or delegate to other agents. You are an advisor, not an implementer.
19355
19366
 
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
19372
+
19356
19373
  **Behavior**:
19357
19374
  - **Examine the codebase** before answering — your read access is what makes council valuable. Don't guess at code you can see.
19358
19375
  - Analyze the problem thoroughly
@@ -19440,6 +19457,13 @@ var DESIGNER_PROMPT = `You are a Designer - a frontend UI/UX specialist who crea
19440
19457
  - Leverage component libraries where available
19441
19458
  - Prioritize visual excellence—code perfection comes second
19442
19459
 
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
19466
+
19443
19467
  ## Review Responsibilities
19444
19468
  - Review existing UI for usability, responsiveness, visual consistency, and polish when asked
19445
19469
  - Call out concrete UX issues and improvements, not just abstract design advice
@@ -19477,6 +19501,12 @@ var EXPLORER_PROMPT = `You are Explorer - a fast codebase navigation specialist.
19477
19501
  - **Structural patterns** (function shapes, class structures): ast_grep_search
19478
19502
  - **File discovery** (find by name/extension): glob
19479
19503
 
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
19509
+
19480
19510
  **Behavior**:
19481
19511
  - Be fast and thorough
19482
19512
  - Fire multiple searches in parallel if needed
@@ -19531,6 +19561,13 @@ var FIXER_PROMPT = `You are Fixer - a fast, focused implementation specialist.
19531
19561
  - Run relevant validation when requested or clearly applicable (otherwise note as skipped with reason)
19532
19562
  - Report completion with summary of changes
19533
19563
 
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
19570
+
19534
19571
  **Constraints**:
19535
19572
  - NO external research (no websearch, context7, grep_app)
19536
19573
  - NO delegation or spawning subagents
@@ -19596,6 +19633,12 @@ var LIBRARIAN_PROMPT = `You are Librarian - a research specialist for codebases
19596
19633
  - grep_app: Search GitHub repositories
19597
19634
  - websearch: General web search for docs
19598
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
19641
+
19599
19642
  **Behavior**:
19600
19643
  - Provide evidence-based answers with sources
19601
19644
  - Quote relevant code snippets
@@ -19640,6 +19683,11 @@ var OBSERVER_PROMPT = `You are Observer — a visual analysis specialist.
19640
19683
  - Save context tokens — the Orchestrator never processes the raw file
19641
19684
  - Match the language of the request
19642
19685
  - If info not found, state clearly what's missing
19686
+
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
19643
19691
  `;
19644
19692
  function createObserverAgent(model, customPrompt, customAppendPrompt) {
19645
19693
  let prompt = OBSERVER_PROMPT;
@@ -19684,6 +19732,12 @@ var ORACLE_PROMPT = `You are Oracle - a strategic technical advisor and code rev
19684
19732
  - READ-ONLY: You advise, you don't implement
19685
19733
  - Focus on strategy, not execution
19686
19734
  - Point to specific files/lines when relevant
19735
+
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
19687
19741
  `;
19688
19742
  function createOracleAgent(model, customPrompt, customAppendPrompt) {
19689
19743
  let prompt = ORACLE_PROMPT;
@@ -19707,6 +19761,7 @@ ${customAppendPrompt}`;
19707
19761
 
19708
19762
  // src/agents/index.ts
19709
19763
  var COUNCIL_TOOL_ALLOWED_AGENTS = new Set(["council"]);
19764
+ var CANCEL_TASK_ALLOWED_AGENTS = new Set(["orchestrator"]);
19710
19765
  var SAFE_AGENT_ALIAS_RE = /^[a-z][a-z0-9_-]*$/i;
19711
19766
  function normalizeDisplayName(displayName) {
19712
19767
  const trimmed = displayName.trim();
@@ -19783,10 +19838,12 @@ function applyDefaultPermissions(agent, configuredSkills) {
19783
19838
  const skillPermissions = getSkillPermissionsForAgent(agent.name, configuredSkills);
19784
19839
  const questionPerm = existing.question === "deny" ? "deny" : "allow";
19785
19840
  const councilSessionPerm = COUNCIL_TOOL_ALLOWED_AGENTS.has(agent.name) ? existing.council_session ?? "allow" : "deny";
19841
+ const cancelTaskPerm = CANCEL_TASK_ALLOWED_AGENTS.has(agent.name) ? existing.cancel_task ?? "allow" : "deny";
19786
19842
  agent.config.permission = {
19787
19843
  ...existing,
19788
19844
  question: questionPerm,
19789
19845
  council_session: councilSessionPerm,
19846
+ cancel_task: cancelTaskPerm,
19790
19847
  skill: {
19791
19848
  ...typeof existing.skill === "object" ? existing.skill : {},
19792
19849
  ...skillPermissions
@@ -22684,6 +22741,7 @@ class BackgroundJobBoard {
22684
22741
  terminalUnreconciled: false,
22685
22742
  completedAt: undefined,
22686
22743
  resultSummary: undefined,
22744
+ lastLaunchedAt: now,
22687
22745
  updatedAt: now
22688
22746
  };
22689
22747
  this.jobs.set(input.taskID, updated);
@@ -22699,6 +22757,7 @@ class BackgroundJobBoard {
22699
22757
  timedOut: false,
22700
22758
  terminalUnreconciled: false,
22701
22759
  launchedAt: now,
22760
+ lastLaunchedAt: now,
22702
22761
  updatedAt: now,
22703
22762
  alias: this.nextAlias(input.parentSessionID, input.agent)
22704
22763
  };
@@ -22709,7 +22768,7 @@ class BackgroundJobBoard {
22709
22768
  const existing = this.jobs.get(input.taskID);
22710
22769
  if (!existing)
22711
22770
  return;
22712
- if (existing.state === "reconciled") {
22771
+ if (existing.state === "reconciled" || existing.state === "cancelled" && input.state !== "cancelled" || TERMINAL_STATES.has(existing.state) && input.state === "running") {
22713
22772
  return existing;
22714
22773
  }
22715
22774
  const now = input.now ?? Date.now();
@@ -22753,9 +22812,34 @@ class BackgroundJobBoard {
22753
22812
  this.jobs.set(taskID, updated);
22754
22813
  return updated;
22755
22814
  }
22815
+ markCancelled(taskID, reason, now = Date.now()) {
22816
+ const existing = this.jobs.get(taskID);
22817
+ if (!existing)
22818
+ return;
22819
+ if (existing.state === "reconciled")
22820
+ return existing;
22821
+ if (TERMINAL_STATES.has(existing.state))
22822
+ return existing;
22823
+ const summary = normalizeCancelReason(reason);
22824
+ const updated = {
22825
+ ...existing,
22826
+ state: "cancelled",
22827
+ timedOut: false,
22828
+ terminalUnreconciled: true,
22829
+ updatedAt: now,
22830
+ completedAt: existing.completedAt ?? now,
22831
+ resultSummary: summary
22832
+ };
22833
+ this.jobs.set(taskID, updated);
22834
+ return updated;
22835
+ }
22756
22836
  get(taskID) {
22757
22837
  return this.jobs.get(taskID);
22758
22838
  }
22839
+ resolve(parentSessionID, taskIDOrAlias) {
22840
+ const value = taskIDOrAlias.trim();
22841
+ return this.list(parentSessionID).find((job) => job.taskID === value || job.alias === value);
22842
+ }
22759
22843
  list(parentSessionID) {
22760
22844
  const jobs = [...this.jobs.values()];
22761
22845
  const filtered = parentSessionID ? jobs.filter((job) => job.parentSessionID === parentSessionID) : jobs;
@@ -22767,7 +22851,7 @@ class BackgroundJobBoard {
22767
22851
  hasTerminalUnreconciled(parentSessionID) {
22768
22852
  return this.list(parentSessionID).some((job) => job.terminalUnreconciled);
22769
22853
  }
22770
- formatForPrompt(parentSessionID) {
22854
+ formatForPrompt(parentSessionID, now = Date.now()) {
22771
22855
  const jobs = this.list(parentSessionID).filter((job) => job.state === "running" || job.terminalUnreconciled);
22772
22856
  if (jobs.length === 0)
22773
22857
  return;
@@ -22775,7 +22859,7 @@ class BackgroundJobBoard {
22775
22859
  "### Background Job Board",
22776
22860
  "Use task_status before consuming running jobs. Reconcile terminal jobs before final response.",
22777
22861
  "",
22778
- ...jobs.map(formatJob)
22862
+ ...jobs.map((job) => formatJob(job, now))
22779
22863
  ].join(`
22780
22864
  `);
22781
22865
  }
@@ -22795,8 +22879,11 @@ class BackgroundJobBoard {
22795
22879
  return `${prefix2}-${next}`;
22796
22880
  }
22797
22881
  }
22798
- function formatJob(job) {
22799
- const status = job.terminalUnreconciled ? `${job.state}, unreconciled` : job.timedOut ? `${job.state}, timed out` : job.state;
22882
+ function formatJob(job, now = Date.now()) {
22883
+ const ageMs = now - job.lastLaunchedAt;
22884
+ const isResume = job.lastLaunchedAt !== job.launchedAt;
22885
+ const ageLabel = job.state === "running" && ageMs < 30000 ? ` [${isResume ? "resumed" : "just launched"}, ${Math.floor(ageMs / 1000)}s ago]` : "";
22886
+ const status = job.terminalUnreconciled ? `${job.state}, unreconciled` : job.timedOut ? `${job.state}, timed out` : `${job.state}${ageLabel}`;
22800
22887
  const lines = [
22801
22888
  `- ${job.alias} / ${job.taskID} / ${job.agent} / ${status}`,
22802
22889
  ` Objective: ${job.objective || job.description}`
@@ -22813,6 +22900,10 @@ function singleLine(value) {
22813
22900
  return normalized;
22814
22901
  return `${normalized.slice(0, 157)}...`;
22815
22902
  }
22903
+ function normalizeCancelReason(reason) {
22904
+ const normalized = reason?.replace(/\s+/g, " ").trim();
22905
+ return normalized ? `cancelled: ${normalized}` : "cancelled";
22906
+ }
22816
22907
  // src/utils/internal-initiator.ts
22817
22908
  var SLIM_INTERNAL_INITIATOR_MARKER = "<!-- SLIM_INTERNAL_INITIATOR -->";
22818
22909
  function isRecord(value) {
@@ -23441,7 +23532,7 @@ var RATE_LIMIT_PATTERNS = [
23441
23532
  /usage limit/i,
23442
23533
  /overloaded/i,
23443
23534
  /resource.?exhausted/i,
23444
- /insufficient.?quota/i,
23535
+ /insufficient.?(quota|balance)/i,
23445
23536
  /high concurrency/i,
23446
23537
  /reduce concurrency/i
23447
23538
  ];
@@ -23517,7 +23608,7 @@ class ForegroundFallbackManager {
23517
23608
  if (!props?.sessionID || props.status?.type !== "retry")
23518
23609
  break;
23519
23610
  const msg = props.status.message?.toLowerCase() ?? "";
23520
- if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("exceededbudget") || msg.includes("over budget") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
23611
+ if (msg.includes("rate limit") || msg.includes("usage limit") || msg.includes("usage exceeded") || msg.includes("quota exceeded") || msg.includes("exceededbudget") || msg.includes("over budget") || msg.includes("insufficient") || msg.includes("high concurrency") || msg.includes("reduce concurrency")) {
23521
23612
  await this.tryFallback(props.sessionID);
23522
23613
  }
23523
23614
  break;
@@ -24737,7 +24828,7 @@ function createTaskSessionManagerHook(_ctx, options) {
24737
24828
  };
24738
24829
  }
24739
24830
  // src/hooks/todo-continuation/index.ts
24740
- import { tool } from "@opencode-ai/plugin/tool";
24831
+ import { tool } from "@opencode-ai/plugin";
24741
24832
 
24742
24833
  // src/hooks/todo-continuation/todo-hygiene.ts
24743
24834
  var TODO_HYGIENE_REMINDER = "If the active task changed or finished, update the todo list to match the current work state.";
@@ -29755,7 +29846,6 @@ function startAvailabilityCheck(config) {
29755
29846
  }
29756
29847
  }
29757
29848
  // src/multiplexer/session-manager.ts
29758
- var SESSION_TIMEOUT_MS = 10 * 60 * 1000;
29759
29849
  var SESSION_MISSING_GRACE_MS = POLL_INTERVAL_BACKGROUND_MS * 3;
29760
29850
  var SHARED_STATE_KEY = Symbol.for("oh-my-opencode-slim.multiplexer-session-manager.state");
29761
29851
  function getSharedState() {
@@ -29769,6 +29859,7 @@ function getSharedState() {
29769
29859
  return globalWithState[SHARED_STATE_KEY];
29770
29860
  }
29771
29861
  class MultiplexerSessionManager {
29862
+ backgroundJobBoard;
29772
29863
  instanceId = Math.random().toString(36).slice(2, 8);
29773
29864
  serverUrl;
29774
29865
  directory;
@@ -29779,7 +29870,8 @@ class MultiplexerSessionManager {
29779
29870
  closingSessions;
29780
29871
  pollInterval;
29781
29872
  enabled = false;
29782
- constructor(ctx, config) {
29873
+ constructor(ctx, config, backgroundJobBoard) {
29874
+ this.backgroundJobBoard = backgroundJobBoard;
29783
29875
  const sharedState = getSharedState();
29784
29876
  this.sessions = sharedState.sessions;
29785
29877
  this.knownSessions = sharedState.knownSessions;
@@ -29874,7 +29966,8 @@ class MultiplexerSessionManager {
29874
29966
  title,
29875
29967
  directory,
29876
29968
  createdAt: now,
29877
- lastSeenAt: now
29969
+ lastSeenAt: now,
29970
+ seenInStatus: false
29878
29971
  });
29879
29972
  log("[multiplexer-session-manager] pane spawned", {
29880
29973
  instanceId: this.instanceId,
@@ -29966,16 +30059,26 @@ class MultiplexerSessionManager {
29966
30059
  const isIdle = status?.type === "idle";
29967
30060
  if (status) {
29968
30061
  tracked.lastSeenAt = now;
30062
+ tracked.seenInStatus = true;
29969
30063
  tracked.missingSince = undefined;
29970
30064
  } else if (!tracked.missingSince) {
29971
30065
  tracked.missingSince = now;
29972
30066
  }
29973
30067
  const missingTooLong = !!tracked.missingSince && now - tracked.missingSince >= SESSION_MISSING_GRACE_MS;
29974
- const isTimedOut = now - tracked.createdAt > SESSION_TIMEOUT_MS;
29975
- if (isIdle || missingTooLong || isTimedOut) {
30068
+ const shouldKeepRunningBackgroundJob = missingTooLong && this.isRunningBackgroundJob(sessionId);
30069
+ if (isIdle || missingTooLong) {
30070
+ if (shouldKeepRunningBackgroundJob) {
30071
+ log("[multiplexer-session-manager] keeping running background pane", {
30072
+ instanceId: this.instanceId,
30073
+ sessionId,
30074
+ paneId: tracked.paneId,
30075
+ seenInStatus: tracked.seenInStatus
30076
+ });
30077
+ continue;
30078
+ }
29976
30079
  sessionsToClose.push({
29977
30080
  sessionId,
29978
- reason: isIdle ? "idle" : isTimedOut ? "timeout" : "missing"
30081
+ reason: isIdle ? "idle" : "missing"
29979
30082
  });
29980
30083
  }
29981
30084
  }
@@ -29992,7 +30095,15 @@ class MultiplexerSessionManager {
29992
30095
  if (!response.ok) {
29993
30096
  throw new Error(`session status request failed: ${response.status} ${response.statusText}`);
29994
30097
  }
29995
- return await response.json();
30098
+ const body = await response.text();
30099
+ if (body.trim() === "") {
30100
+ throw new Error("session status response was empty");
30101
+ }
30102
+ try {
30103
+ return JSON.parse(body);
30104
+ } catch (err) {
30105
+ throw new Error(`session status response was not valid JSON: ${err}`);
30106
+ }
29996
30107
  }
29997
30108
  async closeSession(sessionId, reason) {
29998
30109
  if (reason === "deleted") {
@@ -30092,7 +30203,8 @@ class MultiplexerSessionManager {
30092
30203
  title: known.title,
30093
30204
  directory: known.directory,
30094
30205
  createdAt: now,
30095
- lastSeenAt: now
30206
+ lastSeenAt: now,
30207
+ seenInStatus: false
30096
30208
  });
30097
30209
  log("[multiplexer-session-manager] pane respawned on busy", {
30098
30210
  instanceId: this.instanceId,
@@ -30117,6 +30229,9 @@ class MultiplexerSessionManager {
30117
30229
  getSessionId(event) {
30118
30230
  return event.properties?.info?.id ?? event.properties?.sessionID;
30119
30231
  }
30232
+ isRunningBackgroundJob(sessionId) {
30233
+ return this.backgroundJobBoard?.get(sessionId)?.state === "running";
30234
+ }
30120
30235
  async cleanup() {
30121
30236
  this.stopPolling();
30122
30237
  if (this.closingSessions.size > 0) {
@@ -30162,7 +30277,7 @@ async function isServerRunning(serverUrl, timeoutMs = 3000, maxAttempts = 2) {
30162
30277
  return false;
30163
30278
  }
30164
30279
  // src/tools/ast-grep/tools.ts
30165
- import { tool as tool2 } from "@opencode-ai/plugin/tool";
30280
+ import { tool as tool2 } from "@opencode-ai/plugin";
30166
30281
 
30167
30282
  // src/tools/ast-grep/cli.ts
30168
30283
  import { existsSync as existsSync9 } from "node:fs";
@@ -30707,11 +30822,96 @@ var ast_grep_replace = tool2({
30707
30822
  }
30708
30823
  }
30709
30824
  });
30710
- // src/tools/council.ts
30825
+ // src/tools/cancel-task.ts
30711
30826
  import {
30712
30827
  tool as tool3
30713
30828
  } from "@opencode-ai/plugin";
30714
30829
  var z4 = tool3.schema;
30830
+ function createCancelTaskTool(options) {
30831
+ const cancel_task = tool3({
30832
+ description: `Cancel a tracked background specialist task.
30833
+
30834
+ Use only for obsolete, wrong, conflicting, or user-requested cancellation. Accepts either the native task_id/session ID or the parent-scoped alias shown in the Background Job Board. Cancellation is not rollback: if cancelling a writer, inspect and reconcile partial file changes before replacing the lane.`,
30835
+ args: {
30836
+ task_id: z4.string().describe("Tracked background task ID or Background Job Board alias"),
30837
+ reason: z4.string().optional().describe("Short cancellation reason")
30838
+ },
30839
+ async execute(args, toolContext) {
30840
+ const parentSessionID = toolContext?.sessionID;
30841
+ if (!parentSessionID)
30842
+ throw new Error("cancel_task requires sessionID");
30843
+ if (toolContext.agent && toolContext.agent !== "orchestrator") {
30844
+ throw new Error("cancel_task can only be used by orchestrator");
30845
+ }
30846
+ if (!options.shouldManageSession(parentSessionID)) {
30847
+ throw new Error("cancel_task can only be used in orchestrator sessions");
30848
+ }
30849
+ const requested = args.task_id.trim();
30850
+ if (!requested)
30851
+ throw new Error("cancel_task requires task_id");
30852
+ const job = options.backgroundJobBoard.resolve(parentSessionID, requested);
30853
+ if (!job) {
30854
+ return [
30855
+ `task_id: ${requested}`,
30856
+ "state: unknown",
30857
+ "",
30858
+ "<task_error>",
30859
+ "unknown or unowned background task",
30860
+ "</task_error>"
30861
+ ].join(`
30862
+ `);
30863
+ }
30864
+ if (job.state !== "running") {
30865
+ return [
30866
+ `task_id: ${job.taskID}`,
30867
+ `state: ${job.state}`,
30868
+ "",
30869
+ "<task_result>",
30870
+ `not cancelled: task is already ${job.state}`,
30871
+ "</task_result>"
30872
+ ].join(`
30873
+ `);
30874
+ }
30875
+ try {
30876
+ await abortSessionWithTimeout(options.client, job.taskID, options.abortTimeoutMs ?? 1e4);
30877
+ } catch (error) {
30878
+ const timedOut = error instanceof OperationTimeoutError;
30879
+ if (timedOut) {
30880
+ options.backgroundJobBoard.updateStatus({
30881
+ taskID: job.taskID,
30882
+ state: "error",
30883
+ resultSummary: error instanceof Error ? error.message : "cancel request timed out"
30884
+ });
30885
+ }
30886
+ return [
30887
+ `task_id: ${job.taskID}`,
30888
+ `state: ${timedOut ? "error" : "cancel_error"}`,
30889
+ "",
30890
+ "<task_error>",
30891
+ error instanceof Error ? error.message : String(error),
30892
+ "</task_error>"
30893
+ ].join(`
30894
+ `);
30895
+ }
30896
+ const cancelled = options.backgroundJobBoard.markCancelled(job.taskID, args.reason);
30897
+ return [
30898
+ `task_id: ${job.taskID}`,
30899
+ `state: ${cancelled?.state ?? "cancelled"}`,
30900
+ "",
30901
+ "<task_error>",
30902
+ cancelled?.resultSummary ?? "cancelled",
30903
+ "</task_error>"
30904
+ ].join(`
30905
+ `);
30906
+ }
30907
+ });
30908
+ return { cancel_task };
30909
+ }
30910
+ // src/tools/council.ts
30911
+ import {
30912
+ tool as tool4
30913
+ } from "@opencode-ai/plugin";
30914
+ var z5 = tool4.schema;
30715
30915
  function formatModelComposition(councillorResults) {
30716
30916
  return councillorResults.map((cr) => {
30717
30917
  const shortModel = shortModelLabel(cr.model);
@@ -30719,15 +30919,15 @@ function formatModelComposition(councillorResults) {
30719
30919
  }).join(", ");
30720
30920
  }
30721
30921
  function createCouncilTool(_ctx, councilManager) {
30722
- const council_session = tool3({
30922
+ const council_session = tool4({
30723
30923
  description: `Launch a multi-LLM council session for consensus-based analysis.
30724
30924
 
30725
30925
  Sends the prompt to multiple models (councillors) in parallel and returns their formatted responses for you to synthesize.
30726
30926
 
30727
30927
  Returns the councillor responses with a summary footer.`,
30728
30928
  args: {
30729
- prompt: z4.string().describe("The prompt to send to all councillors"),
30730
- preset: z4.string().optional().describe('Council preset to use (default: "default"). Must match a preset in the council config.')
30929
+ prompt: z5.string().describe("The prompt to send to all councillors"),
30930
+ preset: z5.string().optional().describe('Council preset to use (default: "default"). Must match a preset in the council config.')
30731
30931
  },
30732
30932
  async execute(args, toolContext) {
30733
30933
  if (!toolContext || typeof toolContext !== "object" || !("sessionID" in toolContext)) {
@@ -31038,7 +31238,7 @@ var WEBFETCH_DESCRIPTION = "Fetch a URL with better extraction for static/docs p
31038
31238
  import os6 from "node:os";
31039
31239
  import path18 from "node:path";
31040
31240
  import {
31041
- tool as tool4
31241
+ tool as tool5
31042
31242
  } from "@opencode-ai/plugin";
31043
31243
 
31044
31244
  // src/tools/smartfetch/binary.ts
@@ -31206,7 +31406,7 @@ var M = class u2 {
31206
31406
  return this.#S;
31207
31407
  }
31208
31408
  constructor(e) {
31209
- let { max: t = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: o, updateAgeOnHas: r, allowStale: h, dispose: l, onInsert: c, disposeAfter: f, noDisposeOnSet: g, noUpdateTTL: p, maxSize: T = 0, maxEntrySize: w = 0, sizeCalculation: y, fetchMethod: a, memoMethod: m, noDeleteOnFetchRejection: _, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: d, allowStaleOnFetchAbort: A, ignoreFetchAbort: z5, perf: x } = e;
31409
+ let { max: t = 0, ttl: i, ttlResolution: s = 1, ttlAutopurge: n, updateAgeOnGet: o, updateAgeOnHas: r, allowStale: h, dispose: l, onInsert: c, disposeAfter: f, noDisposeOnSet: g, noUpdateTTL: p, maxSize: T = 0, maxEntrySize: w = 0, sizeCalculation: y, fetchMethod: a, memoMethod: m, noDeleteOnFetchRejection: _, noDeleteOnStaleGet: b, allowStaleOnFetchRejection: d, allowStaleOnFetchAbort: A, ignoreFetchAbort: z6, perf: x } = e;
31210
31410
  if (x !== undefined && typeof x?.now != "function")
31211
31411
  throw new TypeError("perf option must have a now() method if specified");
31212
31412
  if (this.#m = x ?? C, t !== 0 && !F(t))
@@ -31224,7 +31424,7 @@ var M = class u2 {
31224
31424
  throw new TypeError("memoMethod must be a function if defined");
31225
31425
  if (this.#U = m, a !== undefined && typeof a != "function")
31226
31426
  throw new TypeError("fetchMethod must be a function if specified");
31227
- if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#x = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#x, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z5, this.maxEntrySize !== 0) {
31427
+ if (this.#M = a, this.#W = !!a, this.#s = new Map, this.#i = Array.from({ length: t }).fill(undefined), this.#t = Array.from({ length: t }).fill(undefined), this.#a = new v(t), this.#c = new v(t), this.#l = 0, this.#h = 0, this.#y = R.create(t), this.#n = 0, this.#b = 0, typeof l == "function" && (this.#w = l), typeof c == "function" && (this.#x = c), typeof f == "function" ? (this.#S = f, this.#r = []) : (this.#S = undefined, this.#r = undefined), this.#T = !!this.#w, this.#j = !!this.#x, this.#f = !!this.#S, this.noDisposeOnSet = !!g, this.noUpdateTTL = !!p, this.noDeleteOnFetchRejection = !!_, this.allowStaleOnFetchRejection = !!d, this.allowStaleOnFetchAbort = !!A, this.ignoreFetchAbort = !!z6, this.maxEntrySize !== 0) {
31228
31428
  if (this.#u !== 0 && !F(this.#u))
31229
31429
  throw new TypeError("maxSize must be a positive integer if specified");
31230
31430
  if (!F(this.maxEntrySize))
@@ -31604,8 +31804,8 @@ var M = class u2 {
31604
31804
  let A = this.#p(b);
31605
31805
  if (!y && !A)
31606
31806
  return a && (a.fetch = "hit"), this.#L(b), s && this.#D(b), a && this.#E(a, b), d;
31607
- let z5 = this.#P(e, b, _, w), v = z5.__staleWhileFetching !== undefined && i;
31608
- return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z5.__staleWhileFetching : z5.__returned = z5;
31807
+ let z6 = this.#P(e, b, _, w), v = z6.__staleWhileFetching !== undefined && i;
31808
+ return a && (a.fetch = A ? "stale" : "refresh", v && A && (a.returnedStale = true)), v ? z6.__staleWhileFetching : z6.__returned = z6;
31609
31809
  }
31610
31810
  }
31611
31811
  forceFetch(e, t = {}) {
@@ -32820,20 +33020,20 @@ async function runSecondaryModelWithFallback(client, directory, models, prompt,
32820
33020
  }
32821
33021
 
32822
33022
  // src/tools/smartfetch/tool.ts
32823
- var z5 = tool4.schema;
33023
+ var z6 = tool5.schema;
32824
33024
  function createWebfetchTool(pluginCtx, options = {}) {
32825
33025
  const binaryDir = options.binaryDir || path18.join(os6.tmpdir(), "opencode-smartfetch");
32826
- return tool4({
33026
+ return tool5({
32827
33027
  description: WEBFETCH_DESCRIPTION,
32828
33028
  args: {
32829
- url: z5.httpUrl(),
32830
- format: z5.enum(["text", "markdown", "html"]).default("markdown"),
32831
- timeout: z5.number().positive().max(MAX_TIMEOUT_SECONDS).optional().describe("Timeout in seconds, max 120."),
32832
- prompt: z5.string().optional().describe("Optional extraction task to run on the fetched content using a cheap secondary model."),
32833
- extract_main: z5.boolean().default(true),
32834
- prefer_llms_txt: z5.enum(["auto", "always", "never"]).default("auto"),
32835
- include_metadata: z5.boolean().default(true),
32836
- save_binary: z5.boolean().default(false).describe("Save binary payload to disk when it fits within the active download limit.")
33029
+ url: z6.httpUrl(),
33030
+ format: z6.enum(["text", "markdown", "html"]).default("markdown"),
33031
+ timeout: z6.number().positive().max(MAX_TIMEOUT_SECONDS).optional().describe("Timeout in seconds, max 120."),
33032
+ prompt: z6.string().optional().describe("Optional extraction task to run on the fetched content using a cheap secondary model."),
33033
+ extract_main: z6.boolean().default(true),
33034
+ prefer_llms_txt: z6.enum(["auto", "always", "never"]).default("auto"),
33035
+ include_metadata: z6.boolean().default(true),
33036
+ save_binary: z6.boolean().default(false).describe("Save binary payload to disk when it fits within the active download limit.")
32837
33037
  },
32838
33038
  async execute(args, ctx) {
32839
33039
  const secondaryModels = await readSecondaryModelFromConfig(ctx.directory || pluginCtx.directory);
@@ -33423,6 +33623,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33423
33623
  let presetManager;
33424
33624
  let divoomManager;
33425
33625
  let councilTools;
33626
+ let cancelTaskTools;
33426
33627
  let webfetch;
33427
33628
  let rewriteDisplayNameMentions;
33428
33629
  let toolCount = 0;
@@ -33487,7 +33688,8 @@ var OhMyOpenCodeLite = async (ctx) => {
33487
33688
  councilTools = config.council ? createCouncilTool(ctx, new CouncilManager(ctx, config, depthTracker, multiplexerEnabled)) : {};
33488
33689
  mcps = createBuiltinMcps(config.disabled_mcps, config.websearch);
33489
33690
  webfetch = createWebfetchTool(ctx);
33490
- multiplexerSessionManager = new MultiplexerSessionManager(ctx, multiplexerConfig);
33691
+ backgroundJobBoard = new BackgroundJobBoard;
33692
+ multiplexerSessionManager = new MultiplexerSessionManager(ctx, multiplexerConfig, backgroundJobBoard);
33491
33693
  autoUpdateChecker = createAutoUpdateCheckerHook(ctx, {
33492
33694
  autoUpdate: config.autoUpdate ?? true
33493
33695
  });
@@ -33502,7 +33704,6 @@ var OhMyOpenCodeLite = async (ctx) => {
33502
33704
  applyPatchHook = createApplyPatchHook(ctx);
33503
33705
  jsonErrorRecoveryHook = createJsonErrorRecoveryHook(ctx);
33504
33706
  foregroundFallback = new ForegroundFallbackManager(ctx.client, runtimeChains, config.fallback?.enabled !== false && Object.keys(runtimeChains).length > 0);
33505
- backgroundJobBoard = new BackgroundJobBoard;
33506
33707
  todoContinuationHook = createTodoContinuationHook(ctx, {
33507
33708
  maxContinuations: config.todoContinuation?.maxContinuations ?? 5,
33508
33709
  cooldownMs: config.todoContinuation?.cooldownMs ?? 3000,
@@ -33524,7 +33725,12 @@ var OhMyOpenCodeLite = async (ctx) => {
33524
33725
  interviewManager = createInterviewManager(ctx, config);
33525
33726
  presetManager = createPresetManager(ctx, config);
33526
33727
  divoomManager = createDivoomManager(config.divoom);
33527
- toolCount = Object.keys(councilTools).length + Object.keys(todoContinuationHook.tool).length + 1 + 2;
33728
+ cancelTaskTools = createCancelTaskTool({
33729
+ client: ctx.client,
33730
+ backgroundJobBoard,
33731
+ shouldManageSession: (sessionID) => sessionAgentMap.get(sessionID) === "orchestrator"
33732
+ });
33733
+ toolCount = Object.keys(councilTools).length + Object.keys(cancelTaskTools).length + Object.keys(todoContinuationHook.tool).length + 1 + 2;
33528
33734
  } catch (err) {
33529
33735
  log("[plugin] FATAL: init failed", String(err));
33530
33736
  await appLog(ctx, "error", `INIT FAILED: ${String(err)}. Report at github.com/alvinunreal/oh-my-opencode-slim/issues/310`);
@@ -33566,6 +33772,7 @@ var OhMyOpenCodeLite = async (ctx) => {
33566
33772
  agent: agents,
33567
33773
  tool: {
33568
33774
  ...councilTools,
33775
+ ...cancelTaskTools,
33569
33776
  webfetch,
33570
33777
  ...todoContinuationHook.tool,
33571
33778
  ast_grep_search,