oh-my-opencode-slim 2.0.0-beta.12 → 2.0.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +551 -49
- package/dist/tools/cancel-task.d.ts +6 -0
- package/dist/utils/background-job-board.d.ts +9 -1
- package/dist/utils/task.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22642,6 +22642,10 @@ function createDisplayNameMentionRewriter(config) {
|
|
|
22642
22642
|
};
|
|
22643
22643
|
}
|
|
22644
22644
|
// src/utils/task.ts
|
|
22645
|
+
var TRANSIENT_PROCESS_ERROR_TEXT = new Set([
|
|
22646
|
+
"Task is not running in this process and has no final output.",
|
|
22647
|
+
"Task is not running in this process and has not produced output."
|
|
22648
|
+
]);
|
|
22645
22649
|
function parseTaskIdFromTaskOutput(output) {
|
|
22646
22650
|
const lines = output.split(/\r?\n/);
|
|
22647
22651
|
for (const line of lines) {
|
|
@@ -22677,6 +22681,19 @@ function parseTaskStatusOutput(output) {
|
|
|
22677
22681
|
result: parseTaskResultFromOutput(output)
|
|
22678
22682
|
};
|
|
22679
22683
|
}
|
|
22684
|
+
function classifyTaskStatusOutput(status) {
|
|
22685
|
+
if (status.timedOut)
|
|
22686
|
+
return "timeout";
|
|
22687
|
+
if (status.state === "running")
|
|
22688
|
+
return "running";
|
|
22689
|
+
if (status.state === "completed" || status.state === "cancelled") {
|
|
22690
|
+
return "terminal";
|
|
22691
|
+
}
|
|
22692
|
+
if (TRANSIENT_PROCESS_ERROR_TEXT.has(status.result ?? "")) {
|
|
22693
|
+
return "transient_process_error";
|
|
22694
|
+
}
|
|
22695
|
+
return "unknown_error";
|
|
22696
|
+
}
|
|
22680
22697
|
function parseTaskStateFromOutput(output) {
|
|
22681
22698
|
for (const line of getTaskHeader(output).split(/\r?\n/)) {
|
|
22682
22699
|
const match = /^state:\s*(running|completed|error|cancelled)\s*$/i.exec(line.trim());
|
|
@@ -22735,11 +22752,15 @@ class BackgroundJobBoard {
|
|
|
22735
22752
|
objective: input.objective ?? existing.objective,
|
|
22736
22753
|
state: "running",
|
|
22737
22754
|
timedOut: false,
|
|
22755
|
+
statusUncertain: false,
|
|
22756
|
+
cancellationRequested: false,
|
|
22738
22757
|
terminalUnreconciled: false,
|
|
22739
22758
|
completedAt: undefined,
|
|
22740
22759
|
resultSummary: undefined,
|
|
22760
|
+
lastStatusError: undefined,
|
|
22741
22761
|
terminalState: undefined,
|
|
22742
22762
|
lastLaunchedAt: now,
|
|
22763
|
+
lastLiveBusyAt: now,
|
|
22743
22764
|
lastUsedAt: now,
|
|
22744
22765
|
updatedAt: now
|
|
22745
22766
|
};
|
|
@@ -22754,9 +22775,12 @@ class BackgroundJobBoard {
|
|
|
22754
22775
|
objective: input.objective,
|
|
22755
22776
|
state: "running",
|
|
22756
22777
|
timedOut: false,
|
|
22778
|
+
statusUncertain: false,
|
|
22779
|
+
cancellationRequested: false,
|
|
22757
22780
|
terminalUnreconciled: false,
|
|
22758
22781
|
launchedAt: now,
|
|
22759
22782
|
lastLaunchedAt: now,
|
|
22783
|
+
lastLiveBusyAt: now,
|
|
22760
22784
|
lastUsedAt: now,
|
|
22761
22785
|
updatedAt: now,
|
|
22762
22786
|
alias: this.nextAlias(input.parentSessionID, input.agent),
|
|
@@ -22778,11 +22802,13 @@ class BackgroundJobBoard {
|
|
|
22778
22802
|
...existing,
|
|
22779
22803
|
state: input.state,
|
|
22780
22804
|
timedOut: input.timedOut ?? false,
|
|
22805
|
+
statusUncertain: input.statusUncertain ?? false,
|
|
22781
22806
|
terminalUnreconciled: terminal ? true : existing.terminalUnreconciled,
|
|
22782
22807
|
updatedAt: now,
|
|
22783
22808
|
completedAt: terminal ? existing.completedAt ?? now : existing.completedAt,
|
|
22784
22809
|
terminalState: terminal ? input.state : existing.terminalState,
|
|
22785
|
-
resultSummary: input.resultSummary ?? existing.resultSummary
|
|
22810
|
+
resultSummary: input.resultSummary ?? existing.resultSummary,
|
|
22811
|
+
lastStatusError: input.lastStatusError
|
|
22786
22812
|
};
|
|
22787
22813
|
this.jobs.set(input.taskID, updated);
|
|
22788
22814
|
this.trimReusable(input.taskID);
|
|
@@ -22803,18 +22829,28 @@ class BackgroundJobBoard {
|
|
|
22803
22829
|
const existing = this.jobs.get(taskID);
|
|
22804
22830
|
if (!existing)
|
|
22805
22831
|
return;
|
|
22806
|
-
const
|
|
22807
|
-
if (!
|
|
22808
|
-
|
|
22832
|
+
const isStaleTerminal = TERMINAL_STATES.has(existing.state) || existing.state === "reconciled";
|
|
22833
|
+
if (!isStaleTerminal || existing.cancellationRequested) {
|
|
22834
|
+
const updated2 = {
|
|
22835
|
+
...existing,
|
|
22836
|
+
lastLiveBusyAt: now
|
|
22837
|
+
};
|
|
22838
|
+
this.jobs.set(taskID, updated2);
|
|
22839
|
+
return updated2;
|
|
22840
|
+
}
|
|
22809
22841
|
const updated = {
|
|
22810
22842
|
...existing,
|
|
22811
22843
|
state: "running",
|
|
22812
22844
|
timedOut: false,
|
|
22845
|
+
statusUncertain: false,
|
|
22846
|
+
cancellationRequested: false,
|
|
22813
22847
|
terminalUnreconciled: false,
|
|
22814
22848
|
updatedAt: now,
|
|
22849
|
+
lastLiveBusyAt: now,
|
|
22815
22850
|
completedAt: undefined,
|
|
22816
22851
|
terminalState: undefined,
|
|
22817
|
-
resultSummary: undefined
|
|
22852
|
+
resultSummary: undefined,
|
|
22853
|
+
lastStatusError: undefined
|
|
22818
22854
|
};
|
|
22819
22855
|
this.jobs.set(taskID, updated);
|
|
22820
22856
|
return updated;
|
|
@@ -22830,6 +22866,7 @@ class BackgroundJobBoard {
|
|
|
22830
22866
|
...existing,
|
|
22831
22867
|
state: "reconciled",
|
|
22832
22868
|
terminalUnreconciled: false,
|
|
22869
|
+
statusUncertain: false,
|
|
22833
22870
|
updatedAt: now,
|
|
22834
22871
|
lastUsedAt: now,
|
|
22835
22872
|
terminalState: existing.terminalState ?? terminalStateOf(existing.state)
|
|
@@ -22838,24 +22875,29 @@ class BackgroundJobBoard {
|
|
|
22838
22875
|
this.trimReusable(taskID);
|
|
22839
22876
|
return updated;
|
|
22840
22877
|
}
|
|
22841
|
-
markCancelled(taskID, reason, now = Date.now()) {
|
|
22878
|
+
markCancelled(taskID, reason, now = Date.now(), options = {}) {
|
|
22842
22879
|
const existing = this.jobs.get(taskID);
|
|
22843
22880
|
if (!existing)
|
|
22844
22881
|
return;
|
|
22845
|
-
if (
|
|
22846
|
-
|
|
22847
|
-
|
|
22848
|
-
|
|
22882
|
+
if (!options.force) {
|
|
22883
|
+
if (existing.state === "reconciled")
|
|
22884
|
+
return existing;
|
|
22885
|
+
if (TERMINAL_STATES.has(existing.state))
|
|
22886
|
+
return existing;
|
|
22887
|
+
}
|
|
22849
22888
|
const summary = normalizeCancelReason(reason);
|
|
22850
22889
|
const updated = {
|
|
22851
22890
|
...existing,
|
|
22852
22891
|
state: "cancelled",
|
|
22853
22892
|
timedOut: false,
|
|
22893
|
+
statusUncertain: false,
|
|
22894
|
+
cancellationRequested: true,
|
|
22854
22895
|
terminalUnreconciled: true,
|
|
22855
22896
|
updatedAt: now,
|
|
22856
22897
|
completedAt: existing.completedAt ?? now,
|
|
22857
22898
|
terminalState: "cancelled",
|
|
22858
|
-
resultSummary: summary
|
|
22899
|
+
resultSummary: summary,
|
|
22900
|
+
lastStatusError: undefined
|
|
22859
22901
|
};
|
|
22860
22902
|
this.jobs.set(taskID, updated);
|
|
22861
22903
|
return updated;
|
|
@@ -22925,7 +22967,7 @@ class BackgroundJobBoard {
|
|
|
22925
22967
|
return [
|
|
22926
22968
|
"### Background Job Board",
|
|
22927
22969
|
"SENTINEL: background-job-board-v2",
|
|
22928
|
-
"Use task_status for running jobs. Reconcile terminal jobs before final response. Reuse
|
|
22970
|
+
"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.",
|
|
22929
22971
|
"",
|
|
22930
22972
|
"#### Active / Unreconciled",
|
|
22931
22973
|
...active.length > 0 ? active.map((job) => formatJob(job, now)) : ["- none"],
|
|
@@ -22981,7 +23023,8 @@ function deriveTaskSessionLabel(input) {
|
|
|
22981
23023
|
return firstPromptLine ? firstPromptLine.slice(0, 48) : `recent ${input.agentType} task`;
|
|
22982
23024
|
}
|
|
22983
23025
|
function isReusable(job) {
|
|
22984
|
-
|
|
23026
|
+
const terminal = job.terminalState ?? terminalStateOf(job.state);
|
|
23027
|
+
return terminal === "completed" && !job.terminalUnreconciled;
|
|
22985
23028
|
}
|
|
22986
23029
|
function terminalStateOf(state) {
|
|
22987
23030
|
return state === "completed" || state === "error" || state === "cancelled" ? state : undefined;
|
|
@@ -23001,13 +23044,15 @@ function formatJob(job, now = Date.now()) {
|
|
|
23001
23044
|
const ageMs = now - job.lastLaunchedAt;
|
|
23002
23045
|
const isResume = job.lastLaunchedAt !== job.launchedAt;
|
|
23003
23046
|
const ageLabel = job.state === "running" && ageMs < 30000 ? ` [${isResume ? "resumed" : "just launched"}, ${Math.floor(ageMs / 1000)}s ago]` : "";
|
|
23004
|
-
const status = job.terminalUnreconciled ? `${job.state}, unreconciled` : job.timedOut ? `${job.state}, timed out` : `${job.state}${ageLabel}`;
|
|
23047
|
+
const status = job.terminalUnreconciled ? `${job.state}, unreconciled` : job.statusUncertain ? `${job.state}, status uncertain` : job.timedOut ? `${job.state}, timed out` : `${job.state}${ageLabel}`;
|
|
23005
23048
|
const lines = [
|
|
23006
23049
|
`- ${job.alias} / ${job.taskID} / ${job.agent} / ${status}`,
|
|
23007
23050
|
` Objective: ${job.objective || job.description}`
|
|
23008
23051
|
];
|
|
23009
23052
|
if (job.resultSummary && job.terminalUnreconciled) {
|
|
23010
23053
|
lines.push(` Result: ${singleLine(job.resultSummary)}`);
|
|
23054
|
+
} else if (job.lastStatusError && job.statusUncertain) {
|
|
23055
|
+
lines.push(` Status: ${singleLine(job.lastStatusError)}`);
|
|
23011
23056
|
}
|
|
23012
23057
|
return lines.join(`
|
|
23013
23058
|
`);
|
|
@@ -24094,6 +24139,17 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24094
24139
|
timedOut: status.timedOut,
|
|
24095
24140
|
hasResult: Boolean(status.result)
|
|
24096
24141
|
});
|
|
24142
|
+
const existing = backgroundJobBoard.get(status.taskID);
|
|
24143
|
+
if (isLateCancelledTaskError(existing, status.state)) {
|
|
24144
|
+
log("[task-session-manager] suppressed late cancelled task error", {
|
|
24145
|
+
taskID: status.taskID,
|
|
24146
|
+
alias: existing?.alias,
|
|
24147
|
+
state: existing?.state,
|
|
24148
|
+
terminalState: existing?.terminalState,
|
|
24149
|
+
result: status.result
|
|
24150
|
+
});
|
|
24151
|
+
return existing;
|
|
24152
|
+
}
|
|
24097
24153
|
const updated = backgroundJobBoard.updateStatus({
|
|
24098
24154
|
taskID: status.taskID,
|
|
24099
24155
|
state: status.state,
|
|
@@ -24122,6 +24178,61 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24122
24178
|
}
|
|
24123
24179
|
return updated;
|
|
24124
24180
|
}
|
|
24181
|
+
async function handleTransientTaskStatusOutput(output) {
|
|
24182
|
+
if (typeof output.output !== "string")
|
|
24183
|
+
return false;
|
|
24184
|
+
const status = parseTaskStatusOutput(output.output);
|
|
24185
|
+
if (!status)
|
|
24186
|
+
return false;
|
|
24187
|
+
if (classifyTaskStatusOutput(status) !== "transient_process_error") {
|
|
24188
|
+
return false;
|
|
24189
|
+
}
|
|
24190
|
+
const existing = backgroundJobBoard.get(status.taskID);
|
|
24191
|
+
const liveStatus = existing && existing.state === "running" ? undefined : await getLiveSessionStatus(status.taskID);
|
|
24192
|
+
const recentLiveBusy = !!existing?.lastLiveBusyAt && (!existing.completedAt || existing.lastLiveBusyAt >= existing.completedAt);
|
|
24193
|
+
const isStillRunning = existing?.state === "running" || recentLiveBusy || liveStatus === "busy" || liveStatus === "retry";
|
|
24194
|
+
if (!isStillRunning)
|
|
24195
|
+
return false;
|
|
24196
|
+
const updated = existing?.state === "running" ? backgroundJobBoard.updateStatus({
|
|
24197
|
+
taskID: status.taskID,
|
|
24198
|
+
state: "running",
|
|
24199
|
+
statusUncertain: true,
|
|
24200
|
+
lastStatusError: status.result
|
|
24201
|
+
}) : undefined;
|
|
24202
|
+
log("[task-session-manager] classified transient task_status error", {
|
|
24203
|
+
taskID: status.taskID,
|
|
24204
|
+
alias: existing?.alias,
|
|
24205
|
+
parentSessionID: existing?.parentSessionID,
|
|
24206
|
+
previousState: existing?.state,
|
|
24207
|
+
updatedState: updated?.state,
|
|
24208
|
+
liveStatus,
|
|
24209
|
+
recentLiveBusy
|
|
24210
|
+
});
|
|
24211
|
+
return true;
|
|
24212
|
+
}
|
|
24213
|
+
async function getLiveSessionStatus(sessionID) {
|
|
24214
|
+
try {
|
|
24215
|
+
const response = await _ctx.client.session.status();
|
|
24216
|
+
const data = response.data;
|
|
24217
|
+
if (!isObjectRecord(data))
|
|
24218
|
+
return;
|
|
24219
|
+
const item = data[sessionID];
|
|
24220
|
+
if (item === undefined)
|
|
24221
|
+
return "idle";
|
|
24222
|
+
if (isObjectRecord(item) && typeof item.type === "string") {
|
|
24223
|
+
return item.type;
|
|
24224
|
+
}
|
|
24225
|
+
const directType = data.type;
|
|
24226
|
+
if (typeof directType === "string")
|
|
24227
|
+
return directType;
|
|
24228
|
+
const nestedStatus = data.status;
|
|
24229
|
+
if (!isObjectRecord(nestedStatus))
|
|
24230
|
+
return;
|
|
24231
|
+
return typeof nestedStatus.type === "string" ? nestedStatus.type : undefined;
|
|
24232
|
+
} catch {
|
|
24233
|
+
return;
|
|
24234
|
+
}
|
|
24235
|
+
}
|
|
24125
24236
|
function updateFromInjectedCompletion(part, message, _messageIndex, partIndex) {
|
|
24126
24237
|
if (part.type !== "text" || typeof part.text !== "string") {
|
|
24127
24238
|
return;
|
|
@@ -24134,11 +24245,24 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24134
24245
|
const status = parseTaskStatusOutput(part.text);
|
|
24135
24246
|
if (!status)
|
|
24136
24247
|
return;
|
|
24248
|
+
const occurrenceId = createOccurrenceId(part, message, partIndex);
|
|
24249
|
+
const existing = backgroundJobBoard.get(status.taskID);
|
|
24250
|
+
if (isFailed && isLateCancelledTaskError(existing, status.state)) {
|
|
24251
|
+
part.text = formatCancelledTaskStatusOutput(status.taskID, existing?.resultSummary);
|
|
24252
|
+
log("[task-session-manager] normalized late cancelled injected failure", {
|
|
24253
|
+
taskID: status.taskID,
|
|
24254
|
+
alias: existing?.alias,
|
|
24255
|
+
state: existing?.state,
|
|
24256
|
+
terminalState: existing?.terminalState,
|
|
24257
|
+
result: status.result
|
|
24258
|
+
});
|
|
24259
|
+
rememberProcessedInjectedCompletion(occurrenceId);
|
|
24260
|
+
return existing;
|
|
24261
|
+
}
|
|
24137
24262
|
if (isCompleted && status.state !== "completed")
|
|
24138
24263
|
return;
|
|
24139
24264
|
if (isFailed && status.state !== "error")
|
|
24140
24265
|
return;
|
|
24141
|
-
const occurrenceId = createOccurrenceId(part, message, partIndex);
|
|
24142
24266
|
if (processedInjectedCompletions.has(occurrenceId))
|
|
24143
24267
|
return;
|
|
24144
24268
|
const updated = updateBackgroundJobFromOutput(part.text);
|
|
@@ -24298,6 +24422,10 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24298
24422
|
if (!input.sessionID || !options.shouldManageSession(input.sessionID)) {
|
|
24299
24423
|
return;
|
|
24300
24424
|
}
|
|
24425
|
+
normalizeLateCancelledToolStatus(output);
|
|
24426
|
+
if (await handleTransientTaskStatusOutput(output)) {
|
|
24427
|
+
return;
|
|
24428
|
+
}
|
|
24301
24429
|
updateBackgroundJobFromOutput(output.output);
|
|
24302
24430
|
return;
|
|
24303
24431
|
}
|
|
@@ -24421,12 +24549,27 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24421
24549
|
const sessionId2 = input.event.properties?.info?.id ?? input.event.properties?.sessionID;
|
|
24422
24550
|
const before = sessionId2 ? backgroundJobBoard.get(sessionId2) : undefined;
|
|
24423
24551
|
const updated = sessionId2 ? backgroundJobBoard.markRunningFromLiveSession(sessionId2) : undefined;
|
|
24552
|
+
if (before?.cancellationRequested) {
|
|
24553
|
+
log("[task-session-manager] busy observed after cancel request", {
|
|
24554
|
+
sessionID: sessionId2,
|
|
24555
|
+
previousState: before.state,
|
|
24556
|
+
previousTerminalState: before.terminalState,
|
|
24557
|
+
terminalUnreconciled: before.terminalUnreconciled,
|
|
24558
|
+
resultSummary: before.resultSummary,
|
|
24559
|
+
updatedState: updated?.state,
|
|
24560
|
+
updatedCancellationRequested: updated?.cancellationRequested
|
|
24561
|
+
});
|
|
24562
|
+
}
|
|
24424
24563
|
log("[task-session-manager] busy/status busy observed", {
|
|
24425
24564
|
sessionID: sessionId2,
|
|
24426
24565
|
managesSession: sessionId2 ? options.shouldManageSession(sessionId2) : false,
|
|
24427
24566
|
previousState: before?.state,
|
|
24428
24567
|
previousTerminalState: before?.terminalState,
|
|
24429
|
-
|
|
24568
|
+
previousCancellationRequested: before?.cancellationRequested,
|
|
24569
|
+
previousLastLiveBusyAt: before?.lastLiveBusyAt,
|
|
24570
|
+
updatedState: updated?.state,
|
|
24571
|
+
updatedCancellationRequested: updated?.cancellationRequested,
|
|
24572
|
+
updatedLastLiveBusyAt: updated?.lastLiveBusyAt
|
|
24430
24573
|
});
|
|
24431
24574
|
return;
|
|
24432
24575
|
}
|
|
@@ -24459,6 +24602,45 @@ function createTaskSessionManagerHook(_ctx, options) {
|
|
|
24459
24602
|
}
|
|
24460
24603
|
}
|
|
24461
24604
|
};
|
|
24605
|
+
function normalizeLateCancelledToolStatus(output) {
|
|
24606
|
+
if (typeof output.output !== "string")
|
|
24607
|
+
return;
|
|
24608
|
+
const status = parseTaskStatusOutput(output.output);
|
|
24609
|
+
if (!status)
|
|
24610
|
+
return;
|
|
24611
|
+
const existing = backgroundJobBoard.get(status.taskID);
|
|
24612
|
+
if (!isLateCancelledTaskError(existing, status.state))
|
|
24613
|
+
return;
|
|
24614
|
+
log("[task-session-manager] normalized late cancelled task_status output", {
|
|
24615
|
+
taskID: status.taskID,
|
|
24616
|
+
alias: existing?.alias,
|
|
24617
|
+
state: existing?.state,
|
|
24618
|
+
terminalState: existing?.terminalState,
|
|
24619
|
+
result: status.result
|
|
24620
|
+
});
|
|
24621
|
+
output.output = formatCancelledTaskStatusOutput(status.taskID, existing?.resultSummary);
|
|
24622
|
+
if (isObjectRecord(output) && isObjectRecord(output.metadata)) {
|
|
24623
|
+
output.metadata.state = "cancelled";
|
|
24624
|
+
}
|
|
24625
|
+
}
|
|
24626
|
+
}
|
|
24627
|
+
function isLateCancelledTaskError(job, state) {
|
|
24628
|
+
if (state !== "error")
|
|
24629
|
+
return false;
|
|
24630
|
+
if (!job?.cancellationRequested)
|
|
24631
|
+
return false;
|
|
24632
|
+
return job.state === "cancelled" || job.terminalState === "cancelled";
|
|
24633
|
+
}
|
|
24634
|
+
function formatCancelledTaskStatusOutput(taskID, summary = "cancelled") {
|
|
24635
|
+
return [
|
|
24636
|
+
`task_id: ${taskID}`,
|
|
24637
|
+
"state: cancelled",
|
|
24638
|
+
"",
|
|
24639
|
+
"<task_error>",
|
|
24640
|
+
summary,
|
|
24641
|
+
"</task_error>"
|
|
24642
|
+
].join(`
|
|
24643
|
+
`);
|
|
24462
24644
|
}
|
|
24463
24645
|
// src/hooks/todo-continuation/index.ts
|
|
24464
24646
|
import { tool } from "@opencode-ai/plugin";
|
|
@@ -29951,7 +30133,7 @@ class MultiplexerSessionManager {
|
|
|
29951
30133
|
});
|
|
29952
30134
|
return;
|
|
29953
30135
|
}
|
|
29954
|
-
if (tracked.ownerInstanceId !== this.instanceId) {
|
|
30136
|
+
if (reason !== "deleted" && tracked.ownerInstanceId !== this.instanceId) {
|
|
29955
30137
|
log("[multiplexer-session-manager] close skipped; non-owner instance", {
|
|
29956
30138
|
instanceId: this.instanceId,
|
|
29957
30139
|
ownerInstanceId: tracked.ownerInstanceId,
|
|
@@ -29961,6 +30143,15 @@ class MultiplexerSessionManager {
|
|
|
29961
30143
|
});
|
|
29962
30144
|
return;
|
|
29963
30145
|
}
|
|
30146
|
+
if (reason === "deleted" && tracked.ownerInstanceId !== this.instanceId) {
|
|
30147
|
+
log("[multiplexer-session-manager] closing deleted pane as non-owner", {
|
|
30148
|
+
instanceId: this.instanceId,
|
|
30149
|
+
ownerInstanceId: tracked.ownerInstanceId,
|
|
30150
|
+
sessionId,
|
|
30151
|
+
paneId: tracked.paneId,
|
|
30152
|
+
reason
|
|
30153
|
+
});
|
|
30154
|
+
}
|
|
29964
30155
|
if (reason === "idle" && this.isRunningBackgroundJob(sessionId)) {
|
|
29965
30156
|
log("[multiplexer-session-manager] close skipped; background job running", {
|
|
29966
30157
|
instanceId: this.instanceId,
|
|
@@ -30679,6 +30870,9 @@ import {
|
|
|
30679
30870
|
tool as tool3
|
|
30680
30871
|
} from "@opencode-ai/plugin";
|
|
30681
30872
|
var z4 = tool3.schema;
|
|
30873
|
+
|
|
30874
|
+
class SessionStillRunningError extends Error {
|
|
30875
|
+
}
|
|
30682
30876
|
function createCancelTaskTool(options) {
|
|
30683
30877
|
const cancel_task = tool3({
|
|
30684
30878
|
description: `Cancel a tracked background specialist task.
|
|
@@ -30702,43 +30896,68 @@ Use only for obsolete, wrong, conflicting, or user-requested cancellation. Accep
|
|
|
30702
30896
|
if (!requested)
|
|
30703
30897
|
throw new Error("cancel_task requires task_id");
|
|
30704
30898
|
const job = options.backgroundJobBoard.resolve(parentSessionID, requested);
|
|
30899
|
+
log("[cancel-task] request received", {
|
|
30900
|
+
parentSessionID,
|
|
30901
|
+
requested,
|
|
30902
|
+
resolvedTaskID: job?.taskID,
|
|
30903
|
+
alias: job?.alias,
|
|
30904
|
+
state: job?.state,
|
|
30905
|
+
terminalState: job?.terminalState,
|
|
30906
|
+
cancellationRequested: job?.cancellationRequested
|
|
30907
|
+
});
|
|
30705
30908
|
if (!job) {
|
|
30706
|
-
|
|
30707
|
-
|
|
30708
|
-
|
|
30709
|
-
|
|
30710
|
-
|
|
30711
|
-
|
|
30712
|
-
|
|
30713
|
-
|
|
30714
|
-
|
|
30715
|
-
|
|
30716
|
-
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30721
|
-
|
|
30722
|
-
|
|
30723
|
-
|
|
30724
|
-
|
|
30725
|
-
|
|
30726
|
-
|
|
30909
|
+
if (isSessionID(requested)) {
|
|
30910
|
+
if (requested === parentSessionID) {
|
|
30911
|
+
log("[cancel-task] rejected parent session cancellation", {
|
|
30912
|
+
parentSessionID,
|
|
30913
|
+
taskID: requested
|
|
30914
|
+
});
|
|
30915
|
+
return unknownTaskOutput(requested, "cannot cancel parent session");
|
|
30916
|
+
}
|
|
30917
|
+
const knownJob = options.backgroundJobBoard.get(requested);
|
|
30918
|
+
if (knownJob && knownJob.parentSessionID !== parentSessionID) {
|
|
30919
|
+
log("[cancel-task] rejected unowned tracked raw session", {
|
|
30920
|
+
parentSessionID,
|
|
30921
|
+
taskID: requested,
|
|
30922
|
+
ownerParentSessionID: knownJob.parentSessionID
|
|
30923
|
+
});
|
|
30924
|
+
return unknownTaskOutput(requested, "unknown or unowned background task");
|
|
30925
|
+
}
|
|
30926
|
+
const parentID = await getSessionParentID(options.client, requested);
|
|
30927
|
+
if (parentID !== parentSessionID) {
|
|
30928
|
+
log("[cancel-task] rejected raw session without parent ownership", {
|
|
30929
|
+
parentSessionID,
|
|
30930
|
+
taskID: requested,
|
|
30931
|
+
actualParentID: parentID
|
|
30932
|
+
});
|
|
30933
|
+
return unknownTaskOutput(requested, "unknown or unowned background task");
|
|
30934
|
+
}
|
|
30935
|
+
log("[cancel-task] falling back to owned raw session abort", {
|
|
30936
|
+
parentSessionID,
|
|
30937
|
+
taskID: requested
|
|
30938
|
+
});
|
|
30939
|
+
return cancelSessionByID(options, requested, args.reason);
|
|
30940
|
+
}
|
|
30941
|
+
return unknownTaskOutput(requested, "unknown or unowned background task");
|
|
30727
30942
|
}
|
|
30728
30943
|
try {
|
|
30729
|
-
await
|
|
30944
|
+
await abortAndVerifySession(options, job.taskID);
|
|
30730
30945
|
} catch (error) {
|
|
30731
|
-
const
|
|
30732
|
-
|
|
30733
|
-
|
|
30734
|
-
|
|
30735
|
-
|
|
30736
|
-
|
|
30737
|
-
|
|
30738
|
-
|
|
30946
|
+
const stillRunning = error instanceof SessionStillRunningError;
|
|
30947
|
+
log("[cancel-task] abort failed", {
|
|
30948
|
+
taskID: job.taskID,
|
|
30949
|
+
stillRunning,
|
|
30950
|
+
error: error instanceof Error ? error.message : String(error)
|
|
30951
|
+
});
|
|
30952
|
+
options.backgroundJobBoard.updateStatus({
|
|
30953
|
+
taskID: job.taskID,
|
|
30954
|
+
state: "running",
|
|
30955
|
+
statusUncertain: true,
|
|
30956
|
+
lastStatusError: error instanceof Error ? error.message : String(error)
|
|
30957
|
+
});
|
|
30739
30958
|
return [
|
|
30740
30959
|
`task_id: ${job.taskID}`,
|
|
30741
|
-
|
|
30960
|
+
"state: running",
|
|
30742
30961
|
"",
|
|
30743
30962
|
"<task_error>",
|
|
30744
30963
|
error instanceof Error ? error.message : String(error),
|
|
@@ -30746,7 +30965,14 @@ Use only for obsolete, wrong, conflicting, or user-requested cancellation. Accep
|
|
|
30746
30965
|
].join(`
|
|
30747
30966
|
`);
|
|
30748
30967
|
}
|
|
30749
|
-
const cancelled = options.backgroundJobBoard.markCancelled(job.taskID, args.reason);
|
|
30968
|
+
const cancelled = options.backgroundJobBoard.markCancelled(job.taskID, args.reason, Date.now(), { force: true });
|
|
30969
|
+
log("[cancel-task] marked job cancelled after verified abort", {
|
|
30970
|
+
taskID: job.taskID,
|
|
30971
|
+
alias: job.alias,
|
|
30972
|
+
previousState: job.state,
|
|
30973
|
+
state: cancelled?.state,
|
|
30974
|
+
cancellationRequested: cancelled?.cancellationRequested
|
|
30975
|
+
});
|
|
30750
30976
|
return [
|
|
30751
30977
|
`task_id: ${job.taskID}`,
|
|
30752
30978
|
`state: ${cancelled?.state ?? "cancelled"}`,
|
|
@@ -30760,6 +30986,282 @@ Use only for obsolete, wrong, conflicting, or user-requested cancellation. Accep
|
|
|
30760
30986
|
});
|
|
30761
30987
|
return { cancel_task };
|
|
30762
30988
|
}
|
|
30989
|
+
async function cancelSessionByID(options, taskID, reason) {
|
|
30990
|
+
try {
|
|
30991
|
+
await abortAndVerifySession(options, taskID);
|
|
30992
|
+
} catch (error) {
|
|
30993
|
+
const stillRunning = error instanceof SessionStillRunningError;
|
|
30994
|
+
log("[cancel-task] raw session abort failed", {
|
|
30995
|
+
taskID,
|
|
30996
|
+
stillRunning,
|
|
30997
|
+
error: error instanceof Error ? error.message : String(error)
|
|
30998
|
+
});
|
|
30999
|
+
return [
|
|
31000
|
+
`task_id: ${taskID}`,
|
|
31001
|
+
`state: ${stillRunning ? "running" : "error"}`,
|
|
31002
|
+
"",
|
|
31003
|
+
"<task_error>",
|
|
31004
|
+
error instanceof Error ? error.message : String(error),
|
|
31005
|
+
"</task_error>"
|
|
31006
|
+
].join(`
|
|
31007
|
+
`);
|
|
31008
|
+
}
|
|
31009
|
+
return [
|
|
31010
|
+
`task_id: ${taskID}`,
|
|
31011
|
+
"state: cancelled",
|
|
31012
|
+
"",
|
|
31013
|
+
"<task_error>",
|
|
31014
|
+
normalizeCancelReason2(reason),
|
|
31015
|
+
"</task_error>"
|
|
31016
|
+
].join(`
|
|
31017
|
+
`);
|
|
31018
|
+
}
|
|
31019
|
+
async function abortAndVerifySession(options, taskID) {
|
|
31020
|
+
log("[cancel-task] abort attempt starting", { taskID });
|
|
31021
|
+
const abortStartedAt = Date.now();
|
|
31022
|
+
try {
|
|
31023
|
+
await abortSessionWithTimeout(options.client, taskID, options.abortTimeoutMs ?? 1e4);
|
|
31024
|
+
log("[cancel-task] abort call returned", { taskID });
|
|
31025
|
+
} catch (error) {
|
|
31026
|
+
log("[cancel-task] abort call failed", {
|
|
31027
|
+
taskID,
|
|
31028
|
+
error: error instanceof Error ? error.message : String(error),
|
|
31029
|
+
canDelete: canDeleteSession(options.client)
|
|
31030
|
+
});
|
|
31031
|
+
if (!canDeleteSession(options.client))
|
|
31032
|
+
throw error;
|
|
31033
|
+
}
|
|
31034
|
+
if (canDeleteSession(options.client)) {
|
|
31035
|
+
await deleteAndVerifySession(options, taskID, "cancel-task-after-abort");
|
|
31036
|
+
return;
|
|
31037
|
+
}
|
|
31038
|
+
const verifyAbortMs = options.verifyAbortMs ?? 8000;
|
|
31039
|
+
const stableStoppedMs = options.stableStoppedMs ?? 3000;
|
|
31040
|
+
const retryIntervalMs = options.abortRetryIntervalMs ?? 150;
|
|
31041
|
+
const deadline = Date.now() + verifyAbortMs;
|
|
31042
|
+
log("[cancel-task] abort verification starting", {
|
|
31043
|
+
taskID,
|
|
31044
|
+
verifyAbortMs,
|
|
31045
|
+
stableStoppedMs,
|
|
31046
|
+
retryIntervalMs
|
|
31047
|
+
});
|
|
31048
|
+
let attempts = 0;
|
|
31049
|
+
let stableStoppedSince;
|
|
31050
|
+
let lastStatus;
|
|
31051
|
+
while (Date.now() <= deadline) {
|
|
31052
|
+
attempts += 1;
|
|
31053
|
+
const statusSnapshot = await getSessionStatus(options.client, taskID);
|
|
31054
|
+
lastStatus = statusSnapshot.status;
|
|
31055
|
+
log("[cancel-task] abort verification status", {
|
|
31056
|
+
taskID,
|
|
31057
|
+
attempts,
|
|
31058
|
+
status: statusSnapshot.status,
|
|
31059
|
+
statusSource: statusSnapshot.source,
|
|
31060
|
+
statusKeys: statusSnapshot.keys,
|
|
31061
|
+
stableStoppedSince,
|
|
31062
|
+
stableStoppedForMs: stableStoppedSince ? Date.now() - stableStoppedSince : 0,
|
|
31063
|
+
boardState: options.backgroundJobBoard.get(taskID)?.state,
|
|
31064
|
+
boardLastLiveBusyAt: options.backgroundJobBoard.get(taskID)?.lastLiveBusyAt
|
|
31065
|
+
});
|
|
31066
|
+
const boardLastLiveBusyAt = options.backgroundJobBoard.get(taskID)?.lastLiveBusyAt;
|
|
31067
|
+
if (boardLastLiveBusyAt && boardLastLiveBusyAt >= abortStartedAt) {
|
|
31068
|
+
log("[cancel-task] abort verification saw board busy after abort", {
|
|
31069
|
+
taskID,
|
|
31070
|
+
attempts,
|
|
31071
|
+
abortStartedAt,
|
|
31072
|
+
boardLastLiveBusyAt,
|
|
31073
|
+
status: statusSnapshot.status,
|
|
31074
|
+
statusSource: statusSnapshot.source
|
|
31075
|
+
});
|
|
31076
|
+
await deleteAndVerifySession(options, taskID, "board-busy-after-abort");
|
|
31077
|
+
return;
|
|
31078
|
+
}
|
|
31079
|
+
if (statusSnapshot.status === "busy" || statusSnapshot.status === "retry") {
|
|
31080
|
+
if (stableStoppedSince !== undefined) {
|
|
31081
|
+
log("[cancel-task] abort verification saw busy after idle", {
|
|
31082
|
+
taskID,
|
|
31083
|
+
attempts,
|
|
31084
|
+
stableStoppedForMs: Date.now() - stableStoppedSince
|
|
31085
|
+
});
|
|
31086
|
+
await deleteAndVerifySession(options, taskID, "busy-after-idle");
|
|
31087
|
+
return;
|
|
31088
|
+
}
|
|
31089
|
+
stableStoppedSince = undefined;
|
|
31090
|
+
await abortSessionWithTimeout(options.client, taskID, options.abortTimeoutMs ?? 1e4);
|
|
31091
|
+
log("[cancel-task] abort retry returned", {
|
|
31092
|
+
taskID,
|
|
31093
|
+
attempts,
|
|
31094
|
+
status: statusSnapshot.status
|
|
31095
|
+
});
|
|
31096
|
+
await delay(retryIntervalMs);
|
|
31097
|
+
continue;
|
|
31098
|
+
}
|
|
31099
|
+
stableStoppedSince ??= Date.now();
|
|
31100
|
+
if (Date.now() - stableStoppedSince >= stableStoppedMs) {
|
|
31101
|
+
log("[cancel-task] abort verified stopped", {
|
|
31102
|
+
taskID,
|
|
31103
|
+
attempts,
|
|
31104
|
+
status: statusSnapshot.status,
|
|
31105
|
+
stableStoppedMs
|
|
31106
|
+
});
|
|
31107
|
+
return;
|
|
31108
|
+
}
|
|
31109
|
+
await delay(retryIntervalMs);
|
|
31110
|
+
}
|
|
31111
|
+
log("[cancel-task] abort verification timed out", {
|
|
31112
|
+
taskID,
|
|
31113
|
+
attempts,
|
|
31114
|
+
lastStatus,
|
|
31115
|
+
stableStoppedSince
|
|
31116
|
+
});
|
|
31117
|
+
if (lastStatus === "busy" || lastStatus === "retry") {
|
|
31118
|
+
await deleteAndVerifySession(options, taskID, "still-busy-after-abort");
|
|
31119
|
+
return;
|
|
31120
|
+
}
|
|
31121
|
+
throw new SessionStillRunningError(`Session abort returned but task did not stay stopped: ${taskID}`);
|
|
31122
|
+
}
|
|
31123
|
+
async function deleteAndVerifySession(options, taskID, reason) {
|
|
31124
|
+
const session2 = options.client.session;
|
|
31125
|
+
if (!session2.delete) {
|
|
31126
|
+
log("[cancel-task] session delete unavailable", { taskID, reason });
|
|
31127
|
+
throw new SessionStillRunningError(`Session resumed after abort and delete is unavailable: ${taskID}`);
|
|
31128
|
+
}
|
|
31129
|
+
log("[cancel-task] deleting session after unstable abort", {
|
|
31130
|
+
taskID,
|
|
31131
|
+
reason
|
|
31132
|
+
});
|
|
31133
|
+
try {
|
|
31134
|
+
await withTimeout(session2.delete({ path: { id: taskID } }), options.deleteTimeoutMs ?? 1e4, `Session delete timed out after ${options.deleteTimeoutMs ?? 1e4}ms`);
|
|
31135
|
+
log("[cancel-task] session delete returned", { taskID, reason });
|
|
31136
|
+
} catch (error) {
|
|
31137
|
+
log("[cancel-task] session delete failed; verifying live state", {
|
|
31138
|
+
taskID,
|
|
31139
|
+
reason,
|
|
31140
|
+
error: error instanceof Error ? error.message : String(error)
|
|
31141
|
+
});
|
|
31142
|
+
const status = await getSessionStatus(options.client, taskID);
|
|
31143
|
+
log("[cancel-task] delete failure verification status", {
|
|
31144
|
+
taskID,
|
|
31145
|
+
reason,
|
|
31146
|
+
status: status.status,
|
|
31147
|
+
statusSource: status.source,
|
|
31148
|
+
statusKeys: status.keys
|
|
31149
|
+
});
|
|
31150
|
+
if (status.status === "busy" || status.status === "retry") {
|
|
31151
|
+
throw new SessionStillRunningError(`Session delete failed and task is still busy: ${taskID}`);
|
|
31152
|
+
}
|
|
31153
|
+
if (status.status !== "idle")
|
|
31154
|
+
throw error;
|
|
31155
|
+
}
|
|
31156
|
+
const deadline = Date.now() + (options.deleteVerifyMs ?? 1500);
|
|
31157
|
+
const stableStoppedMs = options.deleteStableStoppedMs ?? 300;
|
|
31158
|
+
const retryIntervalMs = options.abortRetryIntervalMs ?? 150;
|
|
31159
|
+
let stableStoppedSince;
|
|
31160
|
+
let attempts = 0;
|
|
31161
|
+
let lastStatus;
|
|
31162
|
+
while (Date.now() <= deadline) {
|
|
31163
|
+
attempts += 1;
|
|
31164
|
+
const status = await getSessionStatus(options.client, taskID);
|
|
31165
|
+
lastStatus = status.status;
|
|
31166
|
+
log("[cancel-task] delete verification status", {
|
|
31167
|
+
taskID,
|
|
31168
|
+
reason,
|
|
31169
|
+
attempts,
|
|
31170
|
+
status: status.status,
|
|
31171
|
+
statusSource: status.source,
|
|
31172
|
+
statusKeys: status.keys,
|
|
31173
|
+
stableStoppedSince
|
|
31174
|
+
});
|
|
31175
|
+
if (status.status === "busy" || status.status === "retry") {
|
|
31176
|
+
stableStoppedSince = undefined;
|
|
31177
|
+
await delay(retryIntervalMs);
|
|
31178
|
+
continue;
|
|
31179
|
+
}
|
|
31180
|
+
stableStoppedSince ??= Date.now();
|
|
31181
|
+
if (Date.now() - stableStoppedSince >= stableStoppedMs)
|
|
31182
|
+
return;
|
|
31183
|
+
await delay(retryIntervalMs);
|
|
31184
|
+
}
|
|
31185
|
+
throw new SessionStillRunningError(`Session delete returned but task did not stay stopped: ${taskID} (${lastStatus ?? "unknown"})`);
|
|
31186
|
+
}
|
|
31187
|
+
function canDeleteSession(client) {
|
|
31188
|
+
const session2 = client.session;
|
|
31189
|
+
return typeof session2.delete === "function";
|
|
31190
|
+
}
|
|
31191
|
+
async function getSessionStatus(client, taskID) {
|
|
31192
|
+
try {
|
|
31193
|
+
const result = await client.session.status();
|
|
31194
|
+
const data = result.data;
|
|
31195
|
+
if (!isObjectRecord2(data)) {
|
|
31196
|
+
return { status: undefined, source: "invalid-data", keys: [] };
|
|
31197
|
+
}
|
|
31198
|
+
const keys = Object.keys(data).slice(0, 20);
|
|
31199
|
+
const item = data[taskID];
|
|
31200
|
+
if (item === undefined) {
|
|
31201
|
+
return { status: "idle", source: "missing-from-map", keys };
|
|
31202
|
+
}
|
|
31203
|
+
if (isObjectRecord2(item) && typeof item.type === "string") {
|
|
31204
|
+
return { status: item.type, source: "task-map-entry", keys };
|
|
31205
|
+
}
|
|
31206
|
+
if (typeof data.type === "string") {
|
|
31207
|
+
return { status: data.type, source: "legacy-data-type", keys };
|
|
31208
|
+
}
|
|
31209
|
+
const nested = data.status;
|
|
31210
|
+
if (isObjectRecord2(nested) && typeof nested.type === "string") {
|
|
31211
|
+
return { status: nested.type, source: "legacy-data-status", keys };
|
|
31212
|
+
}
|
|
31213
|
+
return { status: undefined, source: "unknown-shape", keys };
|
|
31214
|
+
} catch (error) {
|
|
31215
|
+
log("[cancel-task] session status lookup failed", {
|
|
31216
|
+
taskID,
|
|
31217
|
+
error: error instanceof Error ? error.message : String(error)
|
|
31218
|
+
});
|
|
31219
|
+
return { status: undefined, source: "lookup-error", keys: [] };
|
|
31220
|
+
}
|
|
31221
|
+
}
|
|
31222
|
+
function delay(ms) {
|
|
31223
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
31224
|
+
}
|
|
31225
|
+
function isObjectRecord2(value) {
|
|
31226
|
+
return typeof value === "object" && value !== null;
|
|
31227
|
+
}
|
|
31228
|
+
function isSessionID(value) {
|
|
31229
|
+
return /^ses_[\w-]+$/.test(value);
|
|
31230
|
+
}
|
|
31231
|
+
function normalizeCancelReason2(reason) {
|
|
31232
|
+
const normalized = reason?.replace(/\s+/g, " ").trim();
|
|
31233
|
+
return normalized ? `cancelled: ${normalized}` : "cancelled";
|
|
31234
|
+
}
|
|
31235
|
+
async function getSessionParentID(client, taskID) {
|
|
31236
|
+
const session2 = client.session;
|
|
31237
|
+
if (!session2.get)
|
|
31238
|
+
return;
|
|
31239
|
+
try {
|
|
31240
|
+
const response = await session2.get({ path: { id: taskID } });
|
|
31241
|
+
const data = response.data;
|
|
31242
|
+
if (!isObjectRecord2(data))
|
|
31243
|
+
return;
|
|
31244
|
+
const parentID = data.parentID;
|
|
31245
|
+
return typeof parentID === "string" ? parentID : undefined;
|
|
31246
|
+
} catch (error) {
|
|
31247
|
+
log("[cancel-task] session metadata lookup failed", {
|
|
31248
|
+
taskID,
|
|
31249
|
+
error: error instanceof Error ? error.message : String(error)
|
|
31250
|
+
});
|
|
31251
|
+
return;
|
|
31252
|
+
}
|
|
31253
|
+
}
|
|
31254
|
+
function unknownTaskOutput(taskID, message) {
|
|
31255
|
+
return [
|
|
31256
|
+
`task_id: ${taskID}`,
|
|
31257
|
+
"state: unknown",
|
|
31258
|
+
"",
|
|
31259
|
+
"<task_error>",
|
|
31260
|
+
message,
|
|
31261
|
+
"</task_error>"
|
|
31262
|
+
].join(`
|
|
31263
|
+
`);
|
|
31264
|
+
}
|
|
30763
31265
|
// src/tools/council.ts
|
|
30764
31266
|
import {
|
|
30765
31267
|
tool as tool4
|
|
@@ -5,6 +5,12 @@ interface CancelTaskToolOptions {
|
|
|
5
5
|
backgroundJobBoard: BackgroundJobBoard;
|
|
6
6
|
shouldManageSession: (sessionID: string) => boolean;
|
|
7
7
|
abortTimeoutMs?: number;
|
|
8
|
+
verifyAbortMs?: number;
|
|
9
|
+
abortRetryIntervalMs?: number;
|
|
10
|
+
stableStoppedMs?: number;
|
|
11
|
+
deleteTimeoutMs?: number;
|
|
12
|
+
deleteVerifyMs?: number;
|
|
13
|
+
deleteStableStoppedMs?: number;
|
|
8
14
|
}
|
|
9
15
|
export declare function createCancelTaskTool(options: CancelTaskToolOptions): Record<string, ToolDefinition>;
|
|
10
16
|
export {};
|
|
@@ -14,12 +14,16 @@ export interface BackgroundJobRecord {
|
|
|
14
14
|
objective?: string;
|
|
15
15
|
state: BackgroundJobState;
|
|
16
16
|
timedOut: boolean;
|
|
17
|
+
statusUncertain: boolean;
|
|
18
|
+
cancellationRequested: boolean;
|
|
17
19
|
terminalUnreconciled: boolean;
|
|
18
20
|
launchedAt: number;
|
|
19
21
|
lastLaunchedAt: number;
|
|
20
22
|
updatedAt: number;
|
|
23
|
+
lastLiveBusyAt?: number;
|
|
21
24
|
completedAt?: number;
|
|
22
25
|
resultSummary?: string;
|
|
26
|
+
lastStatusError?: string;
|
|
23
27
|
alias: string;
|
|
24
28
|
lastUsedAt: number;
|
|
25
29
|
terminalState?: TaskOutputState;
|
|
@@ -42,7 +46,9 @@ export interface BackgroundJobStatusInput {
|
|
|
42
46
|
taskID: string;
|
|
43
47
|
state: TaskOutputState;
|
|
44
48
|
timedOut?: boolean;
|
|
49
|
+
statusUncertain?: boolean;
|
|
45
50
|
resultSummary?: string;
|
|
51
|
+
lastStatusError?: string;
|
|
46
52
|
now?: number;
|
|
47
53
|
}
|
|
48
54
|
export declare class BackgroundJobBoard {
|
|
@@ -57,7 +63,9 @@ export declare class BackgroundJobBoard {
|
|
|
57
63
|
updateFromStatusOutput(output: string): BackgroundJobRecord | undefined;
|
|
58
64
|
markRunningFromLiveSession(taskID: string, now?: number): BackgroundJobRecord | undefined;
|
|
59
65
|
markReconciled(taskID: string, now?: number): BackgroundJobRecord | undefined;
|
|
60
|
-
markCancelled(taskID: string, reason?: string, now?: number
|
|
66
|
+
markCancelled(taskID: string, reason?: string, now?: number, options?: {
|
|
67
|
+
force?: boolean;
|
|
68
|
+
}): BackgroundJobRecord | undefined;
|
|
61
69
|
get(taskID: string): BackgroundJobRecord | undefined;
|
|
62
70
|
resolve(parentSessionID: string, taskIDOrAlias: string): BackgroundJobRecord | undefined;
|
|
63
71
|
resolveForStatus(parentSessionID: string, taskIDOrAlias: string): BackgroundJobRecord | undefined;
|
package/dist/utils/task.d.ts
CHANGED
|
@@ -13,8 +13,10 @@ export interface TaskStatusOutput {
|
|
|
13
13
|
timedOut: boolean;
|
|
14
14
|
result?: string;
|
|
15
15
|
}
|
|
16
|
+
export type TaskStatusClassification = 'running' | 'terminal' | 'timeout' | 'transient_process_error' | 'unknown_error';
|
|
16
17
|
export declare function parseTaskIdFromTaskOutput(output: string): string | undefined;
|
|
17
18
|
export declare function parseTaskLaunchOutput(output: string): TaskLaunchOutput | undefined;
|
|
18
19
|
export declare function parseTaskStatusOutput(output: string): TaskStatusOutput | undefined;
|
|
20
|
+
export declare function classifyTaskStatusOutput(status: TaskStatusOutput): TaskStatusClassification;
|
|
19
21
|
export declare function parseTaskStateFromOutput(output: string): TaskOutputState | undefined;
|
|
20
22
|
export declare function parseTaskResultFromOutput(output: string): string | undefined;
|
package/package.json
CHANGED