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