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
|
|
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
|
|
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
|
|
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
|
-
###
|
|
19130
|
-
-
|
|
19131
|
-
-
|
|
19132
|
-
-
|
|
19133
|
-
-
|
|
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
|
-
-
|
|
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 (
|
|
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
|
-
"
|
|
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
|
-
"-
|
|
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
|
|
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
|
-
|
|
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"
|
|
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
|
|
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
|
|
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
|
|
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
|
@@ -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
|
-
-
|
|
108
|
-
- avoid blocking Orchestrator lane
|
|
109
|
-
|
|
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.
|