oh-my-opencode-slim 2.0.0-beta.14 → 2.0.0-beta.15

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.
@@ -20,7 +20,7 @@ export declare const DEFAULT_TIMEOUT_MS: number;
20
20
  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
- 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!";
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 \u2192 reconcile terminal results \u2192 verify. Do not poll running jobs, consume running-job output, or advance dependent work. !END!";
24
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
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
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.";
@@ -1,4 +1,4 @@
1
- export declare const PHASE_REMINDER = "<internal_reminder>!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!</internal_reminder>";
1
+ export declare const PHASE_REMINDER = "<internal_reminder>!IMPORTANT! Scheduler workflow: plan lanes/dependencies \u2192 dispatch background specialists \u2192 track task IDs \u2192 wait for hook-driven completion \u2192 reconcile terminal results \u2192 verify. Do not poll running jobs, consume running-job output, or advance dependent work. !END!</internal_reminder>";
2
2
  interface MessageInfo {
3
3
  role: string;
4
4
  agent?: string;
package/dist/index.js CHANGED
@@ -18321,7 +18321,7 @@ var POLL_INTERVAL_BACKGROUND_MS = 2000;
18321
18321
  var DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
18322
18322
  var MAX_POLL_TIME_MS = 5 * 60 * 1000;
18323
18323
  var DEFAULT_MAX_SUBAGENT_DEPTH = 3;
18324
- 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!`;
18324
+ var PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → wait for hook-driven completion → reconcile terminal results → verify. Do not poll running jobs, consume running-job output, or advance dependent work. !END!`;
18325
18325
  var WRITABLE_FILE_OPERATIONS_RULES = `**File Operations Rules**:
18326
18326
  - 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.
18327
18327
  - Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.
@@ -19126,31 +19126,15 @@ ${enabledParallelExamples}
19126
19126
 
19127
19127
  Balance: respect dependencies, avoid parallelizing what must be sequential, and avoid overlapping write ownership.
19128
19128
 
19129
- ### OpenCode scheduler model
19130
- - Delegated specialists should be launched as background tasks whenever work can run independently: use \`task(..., background: true)\`.
19131
- - A dispatch returns a task/session ID immediately; it does not mean completion.
19132
- - Track each task ID with specialist, objective, state, and any advisory ownership/dependency labels from the dispatch plan.
19133
- - Background completion is event/hook-driven: when a background task finishes, OpenCode injects a follow-up message with the terminal result.
19134
- - Continue orchestration while tasks run only when useful: planning, scheduling independent lanes, preparing synthesis, or asking needed user questions.
19135
- - 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.
19136
- - 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.
19137
- - 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.
19138
- - 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.
19129
+ ### Background Task Discipline
19130
+ - Prefer \`task(..., background: true)\` for delegated work that can run independently.
19131
+ - Track each task's specialist, objective, task/session ID, and file/topic ownership.
19132
+ - Continue orchestration only on non-overlapping work; otherwise briefly report what was launched and stop.
19133
+ - Before local edits or another writer task, compare against running task scopes.
19139
19134
  - Parallel background tasks are allowed only when their write scopes do not conflict.
19140
- - Final response requires relevant tasks to be terminal and reconciled.
19141
-
19142
- ### Background Job Discipline
19143
- - Every background task owns its declared lane until terminal.
19144
- - Do not duplicate, undermine, or race a running lane.
19145
- - 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.
19146
- - After dispatch, classify the next step:
19147
- 1. independent: continue,
19148
- 2. dependent: wait/poll,
19149
- 3. no useful independent work: stop and let hook-driven completion resume.
19150
- - Before editing files or spawning another writer, compare against running job scopes.
19135
+ - Before final response, reconcile any terminal jobs shown in the Background Job Board.
19151
19136
  - Use \`cancel_task\` only when the user asks, or when a running lane is obsolete, wrong, or conflicts with a safer replacement plan.
19152
19137
  - Cancellation is not rollback: if cancelling a writer, inspect and reconcile partial file changes before launching a replacement lane.
19153
- - Never finalize work that depends on unresolved background jobs.
19154
19138
 
19155
19139
  ### Design Handoff Discipline
19156
19140
  - When @designer completes UI/UX work, treat layout, spacing, hierarchy, motion, color, affordances, and component feel as intentional design output.
@@ -19164,6 +19148,9 @@ Balance: respect dependencies, avoid parallelizing what must be sequential, and
19164
19148
  - When too much unrelated, and really needed, start a fresh session with the specialist
19165
19149
  - If multiple remembered sessions fit, prefer the most recently used matching session.
19166
19150
  - Prefer re-uses over creating new sessions all the time
19151
+ - When reusing a specialist session, you MUST pass the existing session or alias in the task tool's \`task_id\` argument. Saying "reuse" in prose is not enough.
19152
+ - If the Background Job Board lists \`fix-1 / ses_abc / fixer\`, call task with \`subagent_type: "fixer"\` and \`task_id: "fix-1"\` or \`task_id: "ses_abc"\`.
19153
+ - Do not leave \`task_id\` empty when intending to reuse; omitted or empty \`task_id\` creates a new specialist session.
19167
19154
 
19168
19155
  ### Validation routing
19169
19156
  - Validation is a workflow stage owned by the Orchestrator, not a separate specialist
@@ -22640,6 +22627,9 @@ var TRANSIENT_PROCESS_ERROR_TEXT = new Set([
22640
22627
  "Task is not running in this process and has not produced output."
22641
22628
  ]);
22642
22629
  function parseTaskIdFromTaskOutput(output) {
22630
+ const xmlMatch = /<task\s+[^>]*\bid=["']([^"']+)["'][^>]*>/i.exec(output);
22631
+ if (xmlMatch)
22632
+ return xmlMatch[1];
22643
22633
  const lines = output.split(/\r?\n/);
22644
22634
  for (const line of lines) {
22645
22635
  const trimmed = line.trim();
@@ -22674,20 +22664,10 @@ function parseTaskStatusOutput(output) {
22674
22664
  result: parseTaskResultFromOutput(output)
22675
22665
  };
22676
22666
  }
22677
- function classifyTaskStatusOutput(status) {
22678
- if (status.timedOut)
22679
- return "timeout";
22680
- if (status.state === "running")
22681
- return "running";
22682
- if (status.state === "completed" || status.state === "cancelled") {
22683
- return "terminal";
22684
- }
22685
- if (TRANSIENT_PROCESS_ERROR_TEXT.has(status.result ?? "")) {
22686
- return "transient_process_error";
22687
- }
22688
- return "unknown_error";
22689
- }
22690
22667
  function parseTaskStateFromOutput(output) {
22668
+ const xmlMatch = /<task\s+[^>]*\bstate=["'](running|completed|error|cancelled)["'][^>]*>/i.exec(output);
22669
+ if (xmlMatch)
22670
+ return xmlMatch[1].toLowerCase();
22691
22671
  for (const line of getTaskHeader(output).split(/\r?\n/)) {
22692
22672
  const match = /^state:\s*(running|completed|error|cancelled)\s*$/i.exec(line.trim());
22693
22673
  if (match)
@@ -22823,7 +22803,7 @@ class BackgroundJobBoard {
22823
22803
  if (!existing)
22824
22804
  return;
22825
22805
  const isStaleTerminal = TERMINAL_STATES.has(existing.state) || existing.state === "reconciled";
22826
- if (!isStaleTerminal || existing.cancellationRequested) {
22806
+ if (isStaleTerminal) {
22827
22807
  const updated2 = {
22828
22808
  ...existing,
22829
22809
  lastLiveBusyAt: now
@@ -22833,17 +22813,8 @@ class BackgroundJobBoard {
22833
22813
  }
22834
22814
  const updated = {
22835
22815
  ...existing,
22836
- state: "running",
22837
- timedOut: false,
22838
- statusUncertain: false,
22839
- cancellationRequested: false,
22840
- terminalUnreconciled: false,
22841
22816
  updatedAt: now,
22842
- lastLiveBusyAt: now,
22843
- completedAt: undefined,
22844
- terminalState: undefined,
22845
- resultSummary: undefined,
22846
- lastStatusError: undefined
22817
+ lastLiveBusyAt: now
22847
22818
  };
22848
22819
  this.jobs.set(taskID, updated);
22849
22820
  return updated;
@@ -22902,9 +22873,6 @@ class BackgroundJobBoard {
22902
22873
  const value = taskIDOrAlias.trim();
22903
22874
  return this.list(parentSessionID).find((job) => job.taskID === value || job.alias === value);
22904
22875
  }
22905
- resolveForStatus(parentSessionID, taskIDOrAlias) {
22906
- return this.resolve(parentSessionID, taskIDOrAlias);
22907
- }
22908
22876
  resolveReusable(parentSessionID, taskIDOrAlias, agent) {
22909
22877
  const job = this.resolve(parentSessionID, taskIDOrAlias);
22910
22878
  if (!job || !isReusable(job))
@@ -22960,7 +22928,7 @@ class BackgroundJobBoard {
22960
22928
  return [
22961
22929
  "### Background Job Board",
22962
22930
  "SENTINEL: background-job-board-v2",
22963
- "Use task_status for running jobs. Reconcile terminal jobs before final response. Reuse only completed sessions for the same specialist/context; never reuse cancelled or errored sessions.",
22931
+ "Do not poll running jobs. Wait for hook-driven completion, or use cancel_task only for explicit cancellation. Reconcile terminal jobs before final response. Reuse only completed sessions for the same specialist/context; never reuse cancelled or errored sessions.",
22964
22932
  "",
22965
22933
  "#### Active / Unreconciled",
22966
22934
  ...active.length > 0 ? active.map((job) => formatJob(job, now)) : ["- none"],
@@ -23223,7 +23191,7 @@ function activationPrompt(task2) {
23223
23191
  "- draft a plan and get `@oracle` review before implementation;",
23224
23192
  "- create and review a phased implementation/delegation plan;",
23225
23193
  "- execute phase by phase with background specialists where useful;",
23226
- "- poll `task_status`, reconcile results, validate, and ask `@oracle` to review each phase;",
23194
+ "- wait for hook-driven background completion, reconcile results, validate, and ask `@oracle` to review each phase;",
23227
23195
  "- ask `@oracle` to include simplify/readability feedback in phase reviews;",
23228
23196
  "- fix actionable review issues before continuing.",
23229
23197
  "",
@@ -24001,6 +23969,7 @@ var BACKGROUND_JOB_BOARD_SENTINEL = "SENTINEL: background-job-board-v2";
24001
23969
  var BACKGROUND_COMPLETION_COMPLETED = /^Background task completed: /;
24002
23970
  var BACKGROUND_COMPLETION_FAILED = /^Background task failed: /;
24003
23971
  var MAX_PROCESSED_INJECTED_COMPLETIONS = 500;
23972
+ var RAW_SESSION_ID_PATTERN = /^ses_[A-Za-z0-9_-]+$/;
24004
23973
  function djb2Hash(str) {
24005
23974
  let hash = 5381;
24006
23975
  for (let i = 0;i < str.length; i++) {
@@ -24035,6 +24004,10 @@ function isObjectRecord(value) {
24035
24004
  function extractPath(output) {
24036
24005
  return /<path>([^<]+)<\/path>/.exec(output)?.[1];
24037
24006
  }
24007
+ function extractTaskSummary(output) {
24008
+ const summary = /<summary>\s*([\s\S]*?)\s*<\/summary>/i.exec(output)?.[1];
24009
+ return summary?.trim() || undefined;
24010
+ }
24038
24011
  function normalizePath(root, file) {
24039
24012
  const relative = path9.relative(root, file);
24040
24013
  if (!relative || relative.startsWith("..") || path9.isAbsolute(relative)) {
@@ -24126,7 +24099,7 @@ function createTaskSessionManagerHook(_ctx, options) {
24126
24099
  const status = parseTaskStatusOutput(output);
24127
24100
  if (!status)
24128
24101
  return;
24129
- log("[task-session-manager] parsed task status output", {
24102
+ log("[task-session-manager] parsed task output status", {
24130
24103
  taskID: status.taskID,
24131
24104
  state: status.state,
24132
24105
  timedOut: status.timedOut,
@@ -24171,73 +24144,23 @@ function createTaskSessionManagerHook(_ctx, options) {
24171
24144
  }
24172
24145
  return updated;
24173
24146
  }
24174
- async function handleTransientTaskStatusOutput(output) {
24175
- if (typeof output.output !== "string")
24176
- return false;
24177
- const status = parseTaskStatusOutput(output.output);
24178
- if (!status)
24179
- return false;
24180
- if (classifyTaskStatusOutput(status) !== "transient_process_error") {
24181
- return false;
24182
- }
24183
- const existing = backgroundJobBoard.get(status.taskID);
24184
- const liveStatus = existing && existing.state === "running" ? undefined : await getLiveSessionStatus(status.taskID);
24185
- const recentLiveBusy = !!existing?.lastLiveBusyAt && (!existing.completedAt || existing.lastLiveBusyAt >= existing.completedAt);
24186
- const isStillRunning = existing?.state === "running" || recentLiveBusy || liveStatus === "busy" || liveStatus === "retry";
24187
- if (!isStillRunning)
24188
- return false;
24189
- const updated = existing?.state === "running" ? backgroundJobBoard.updateStatus({
24190
- taskID: status.taskID,
24191
- state: "running",
24192
- statusUncertain: true,
24193
- lastStatusError: status.result
24194
- }) : undefined;
24195
- log("[task-session-manager] classified transient task_status error", {
24196
- taskID: status.taskID,
24197
- alias: existing?.alias,
24198
- parentSessionID: existing?.parentSessionID,
24199
- previousState: existing?.state,
24200
- updatedState: updated?.state,
24201
- liveStatus,
24202
- recentLiveBusy
24203
- });
24204
- return true;
24205
- }
24206
- async function getLiveSessionStatus(sessionID) {
24207
- try {
24208
- const response = await _ctx.client.session.status();
24209
- const data = response.data;
24210
- if (!isObjectRecord(data))
24211
- return;
24212
- const item = data[sessionID];
24213
- if (item === undefined)
24214
- return "idle";
24215
- if (isObjectRecord(item) && typeof item.type === "string") {
24216
- return item.type;
24217
- }
24218
- const directType = data.type;
24219
- if (typeof directType === "string")
24220
- return directType;
24221
- const nestedStatus = data.status;
24222
- if (!isObjectRecord(nestedStatus))
24223
- return;
24224
- return typeof nestedStatus.type === "string" ? nestedStatus.type : undefined;
24225
- } catch {
24226
- return;
24227
- }
24228
- }
24229
24147
  function updateFromInjectedCompletion(part, message, _messageIndex, partIndex) {
24230
24148
  if (part.type !== "text" || typeof part.text !== "string") {
24231
24149
  return;
24232
24150
  }
24233
- const isCompleted = BACKGROUND_COMPLETION_COMPLETED.test(part.text);
24234
- const isFailed = BACKGROUND_COMPLETION_FAILED.test(part.text);
24235
- if (part.synthetic !== true || !isCompleted && !isFailed) {
24151
+ if (part.synthetic !== true)
24236
24152
  return;
24237
- }
24238
24153
  const status = parseTaskStatusOutput(part.text);
24239
24154
  if (!status)
24240
24155
  return;
24156
+ if (status.state !== "completed" && status.state !== "error") {
24157
+ return;
24158
+ }
24159
+ const summary = extractTaskSummary(part.text);
24160
+ const isCompleted = summary ? BACKGROUND_COMPLETION_COMPLETED.test(summary) : status.state === "completed";
24161
+ const isFailed = summary ? BACKGROUND_COMPLETION_FAILED.test(summary) : status.state === "error";
24162
+ if (summary && !isCompleted && !isFailed)
24163
+ return;
24241
24164
  const occurrenceId = createOccurrenceId(part, message, partIndex);
24242
24165
  const existing = backgroundJobBoard.get(status.taskID);
24243
24166
  if (isFailed && isLateCancelledTaskError(existing, status.state)) {
@@ -24350,23 +24273,13 @@ function createTaskSessionManagerHook(_ctx, options) {
24350
24273
  return {
24351
24274
  "tool.execute.before": async (input, output) => {
24352
24275
  const toolName = input.tool.toLowerCase();
24353
- if (toolName !== "task" && toolName !== "task_status")
24276
+ if (toolName !== "task")
24354
24277
  return;
24355
24278
  if (!input.sessionID || !options.shouldManageSession(input.sessionID)) {
24356
24279
  return;
24357
24280
  }
24358
24281
  if (!isObjectRecord(output.args))
24359
24282
  return;
24360
- if (toolName === "task_status") {
24361
- const args2 = output.args;
24362
- if (typeof args2.task_id !== "string" || args2.task_id.trim() === "") {
24363
- return;
24364
- }
24365
- const resolved = backgroundJobBoard.resolveForStatus(input.sessionID, args2.task_id.trim());
24366
- if (resolved)
24367
- args2.task_id = resolved.taskID;
24368
- return;
24369
- }
24370
24283
  const args = output.args;
24371
24284
  if (!isAgentName(args.subagent_type)) {
24372
24285
  if (typeof args.task_id === "string" && args.task_id.trim() !== "") {
@@ -24395,6 +24308,11 @@ function createTaskSessionManagerHook(_ctx, options) {
24395
24308
  const requested = args.task_id.trim();
24396
24309
  const remembered = backgroundJobBoard.resolveReusable(input.sessionID, requested, args.subagent_type);
24397
24310
  if (!remembered) {
24311
+ if (RAW_SESSION_ID_PATTERN.test(requested)) {
24312
+ pendingCall.resumedTaskId = requested;
24313
+ rememberPendingCall(pendingCall);
24314
+ return;
24315
+ }
24398
24316
  delete args.task_id;
24399
24317
  return;
24400
24318
  }
@@ -24411,24 +24329,13 @@ function createTaskSessionManagerHook(_ctx, options) {
24411
24329
  }
24412
24330
  return;
24413
24331
  }
24414
- if (input.tool.toLowerCase() === "task_status") {
24415
- if (!input.sessionID || !options.shouldManageSession(input.sessionID)) {
24416
- return;
24417
- }
24418
- normalizeLateCancelledToolStatus(output);
24419
- if (await handleTransientTaskStatusOutput(output)) {
24420
- return;
24421
- }
24422
- updateBackgroundJobFromOutput(output.output);
24423
- return;
24424
- }
24425
24332
  if (input.tool.toLowerCase() !== "task")
24426
24333
  return;
24427
24334
  const pending = takePendingCall(input.callID, input.sessionID);
24428
24335
  if (!pending || typeof output.output !== "string")
24429
24336
  return;
24430
24337
  const launch = parseTaskLaunchOutput(output.output);
24431
- if (launch) {
24338
+ if (launch && !launch.result?.match(/Timed out after \d+ms/i)) {
24432
24339
  const record = backgroundJobBoard.registerLaunch({
24433
24340
  taskID: launch.taskID,
24434
24341
  parentSessionID: pending.parentSessionId,
@@ -24448,6 +24355,39 @@ function createTaskSessionManagerHook(_ctx, options) {
24448
24355
  pendingManagedTaskIds.add(launch.taskID);
24449
24356
  return;
24450
24357
  }
24358
+ normalizeLateCancelledTaskOutput(output);
24359
+ const status = parseTaskStatusOutput(output.output);
24360
+ if (status) {
24361
+ const existing = backgroundJobBoard.get(status.taskID);
24362
+ const record = existing ?? backgroundJobBoard.registerLaunch({
24363
+ taskID: status.taskID,
24364
+ parentSessionID: pending.parentSessionId,
24365
+ agent: pending.agentType,
24366
+ description: pending.label,
24367
+ objective: pending.label
24368
+ });
24369
+ const updated = backgroundJobBoard.updateStatus({
24370
+ taskID: status.taskID,
24371
+ state: status.state,
24372
+ timedOut: status.timedOut,
24373
+ resultSummary: status.result
24374
+ });
24375
+ log("[task-session-manager] foreground task status registered", {
24376
+ taskID: status.taskID,
24377
+ alias: updated?.alias ?? record.alias,
24378
+ parentSessionID: pending.parentSessionId,
24379
+ agent: pending.agentType,
24380
+ state: updated?.state ?? record.state
24381
+ });
24382
+ if (pending.resumedTaskId && pending.resumedTaskId !== status.taskID) {
24383
+ backgroundJobBoard.drop(pending.resumedTaskId);
24384
+ }
24385
+ pendingManagedTaskIds.delete(status.taskID);
24386
+ const contextFiles2 = contextFilesForPrompt(contextByTask.get(status.taskID));
24387
+ backgroundJobBoard.addContext(status.taskID, contextFiles2);
24388
+ pruneContext();
24389
+ return;
24390
+ }
24451
24391
  const taskId = parseTaskIdFromTaskOutput(output.output);
24452
24392
  if (!taskId) {
24453
24393
  if (pending.resumedTaskId && isMissingRememberedSessionError(output.output)) {
@@ -24595,7 +24535,7 @@ function createTaskSessionManagerHook(_ctx, options) {
24595
24535
  }
24596
24536
  }
24597
24537
  };
24598
- function normalizeLateCancelledToolStatus(output) {
24538
+ function normalizeLateCancelledTaskOutput(output) {
24599
24539
  if (typeof output.output !== "string")
24600
24540
  return;
24601
24541
  const status = parseTaskStatusOutput(output.output);
@@ -24604,7 +24544,7 @@ function createTaskSessionManagerHook(_ctx, options) {
24604
24544
  const existing = backgroundJobBoard.get(status.taskID);
24605
24545
  if (!isLateCancelledTaskError(existing, status.state))
24606
24546
  return;
24607
- log("[task-session-manager] normalized late cancelled task_status output", {
24547
+ log("[task-session-manager] normalized late cancelled task output", {
24608
24548
  taskID: status.taskID,
24609
24549
  alias: existing?.alias,
24610
24550
  state: existing?.state,
package/dist/tui.js CHANGED
@@ -68,7 +68,7 @@ var POLL_INTERVAL_BACKGROUND_MS = 2000;
68
68
  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
- 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!`;
71
+ var PHASE_REMINDER_TEXT = `!IMPORTANT! Scheduler workflow: plan lanes/dependencies → dispatch background specialists → track task IDs → wait for hook-driven completion → reconcile terminal results → verify. Do not poll running jobs, consume running-job output, or advance dependent work. !END!`;
72
72
  var WRITABLE_FILE_OPERATIONS_RULES = `**File Operations Rules**:
73
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
74
  - Use bash for execution and automation: git, package managers, tests, builds, scripts, diagnostics, and shell-native filesystem operations.
@@ -68,7 +68,6 @@ export declare class BackgroundJobBoard {
68
68
  }): BackgroundJobRecord | undefined;
69
69
  get(taskID: string): BackgroundJobRecord | undefined;
70
70
  resolve(parentSessionID: string, taskIDOrAlias: string): BackgroundJobRecord | undefined;
71
- resolveForStatus(parentSessionID: string, taskIDOrAlias: string): BackgroundJobRecord | undefined;
72
71
  resolveReusable(parentSessionID: string, taskIDOrAlias: string, agent?: string): BackgroundJobRecord | undefined;
73
72
  markUsed(parentSessionID: string, key: string, now?: number): void;
74
73
  taskIDs(): Set<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode-slim",
3
- "version": "2.0.0-beta.14",
3
+ "version": "2.0.0-beta.15",
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",
@@ -104,8 +104,8 @@ Use the scheduler model throughout:
104
104
 
105
105
  - follow Orchestrator delegations rules
106
106
  - record task/session IDs and ownership boundaries;
107
- - poll `task_status` before consuming background results;
108
- - avoid blocking Orchestrator lane too long; prefer shorter periodic task waits
109
- rather than one long wait;
107
+ - wait for hook-driven background completion before consuming background results;
108
+ - avoid blocking Orchestrator lane while background jobs run; if no independent
109
+ work remains, stop briefly and let the completion event resume the workflow;
110
110
  - do not advance to the next phase while relevant jobs are running or terminal
111
111
  results are unreconciled.