@ashx-j/lunr 0.2.18 → 0.2.20
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/CHANGELOG.md +31 -1
- package/README.md +2 -2
- package/dist/builtin-extensions/ashxj-tui.d.ts +2 -2
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +11 -8
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +0 -2
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts +6 -4
- package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/broker/spawn.js +93 -49
- package/dist/builtin-extensions/pi-intercom/broker/spawn.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.js +351 -351
- package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +2 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +80 -43
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts +18 -8
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +36 -11
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -3
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +21 -12
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts +7 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js +225 -15
- package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts +29 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js +179 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts +89 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js +541 -0
- package/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js.map +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts +9 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +34 -9
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js +1 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts +2 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +35 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +3 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +54 -38
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts +3 -6
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js +64 -5
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-wait.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +21 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +62 -16
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js +75 -18
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/acceptance.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +13 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +67 -11
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +5 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js +13 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts +3 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +68 -11
- package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +12 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +21 -2
- package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +16 -4
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +2 -0
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +24 -6
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +44 -4
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +227 -133
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/register-main.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts +2 -13
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js +5 -91
- package/dist/builtin-extensions/pi-subagents/src/watchdog/tool-actions.js.map +1 -1
- package/dist/core/permissions.d.ts +1 -0
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +8 -4
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +0 -5
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/runtime-bridges.d.ts.map +1 -1
- package/dist/core/runtime-bridges.js +0 -2
- package/dist/core/runtime-bridges.js.map +1 -1
- package/dist/core/subagent-cancellation.d.ts +21 -0
- package/dist/core/subagent-cancellation.d.ts.map +1 -0
- package/dist/core/subagent-cancellation.js +53 -0
- package/dist/core/subagent-cancellation.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +12 -18
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +29 -22
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/node-runtime/{agent-session-services-35WNVGMR.js → agent-session-services-75JKASCW.js} +3 -3
- package/dist/node-runtime/{builtin-extensions-QATGXENO.js → builtin-extensions-X4JMM4ZS.js} +3 -3
- package/dist/node-runtime/{chunk-XATUM2HK.js → chunk-25I2BHAA.js} +1 -1
- package/dist/node-runtime/{chunk-MCUNIRD5.js → chunk-46ECNOLF.js} +477 -246
- package/dist/node-runtime/{chunk-J26TSGDL.js → chunk-6N22HULO.js} +8 -5
- package/dist/node-runtime/{chunk-NI6OTSBH.js → chunk-AL2FMKKM.js} +7 -7
- package/dist/node-runtime/chunk-AOFXZQKA.js +24 -0
- package/dist/node-runtime/chunk-BBUVIEZB.js +51 -0
- package/dist/node-runtime/{chunk-EBF6COVM.js → chunk-ELWEXVO5.js} +7 -7
- package/dist/node-runtime/{chunk-R5EDISSV.js → chunk-JHYKXIMM.js} +5 -2
- package/dist/node-runtime/{chunk-ATGA2WLC.js → chunk-JYNNB7DU.js} +397 -34
- package/dist/node-runtime/{chunk-ZBV7FAHG.js → chunk-K3646JTM.js} +17 -118
- package/dist/node-runtime/{chunk-PNJSG3IZ.js → chunk-LTYWTR3V.js} +1 -369
- package/dist/node-runtime/{chunk-G4P75AIS.js → chunk-QKFMBRAL.js} +47 -54
- package/dist/node-runtime/cli-runtime.js +1 -1
- package/dist/node-runtime/{command-AAV3YRR5.js → command-RBTRL55A.js} +1 -1
- package/dist/node-runtime/{gateway-OAC4OBBL.js → gateway-L4ANRTVE.js} +2 -2
- package/dist/node-runtime/index.js +10 -9
- package/dist/node-runtime/{interactive-mode-2SZUO53H.js → interactive-mode-HPUNH7SV.js} +5 -4
- package/dist/node-runtime/{interactive-view-S6J3QHEF.js → interactive-view-ILS6TAN5.js} +1 -1
- package/dist/node-runtime/{lunr-cron-AUUYP4N4.js → lunr-cron-NNYTWTGF.js} +2 -2
- package/dist/node-runtime/main.js +8 -8
- package/dist/node-runtime/{pi-intercom-EEMBGQID.js → pi-intercom-DDQMRNPJ.js} +96 -76
- package/dist/node-runtime/{pi-subagents-7YJ3I4VE.js → pi-subagents-T5WMMLBB.js} +1040 -318
- package/dist/node-runtime/rpc-entry.js +8 -8
- package/dist/node-runtime/{runtime-bridges-3RKBDLNA.js → runtime-bridges-NJQERQ72.js} +2 -4
- package/dist/node-runtime/{subagent-executor-5Q5A4HJ2.js → subagent-executor-SRFC26SJ.js} +168 -249
- package/docs/features.md +17 -3
- package/docs/keybindings.md +3 -1
- package/docs/settings.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
- package/dist/builtin-extensions/lunr-settings-tools.d.ts +0 -4
- package/dist/builtin-extensions/lunr-settings-tools.d.ts.map +0 -1
- package/dist/builtin-extensions/lunr-settings-tools.js +0 -120
- package/dist/builtin-extensions/lunr-settings-tools.js.map +0 -1
- package/dist/core/settings-tools-bridge.d.ts +0 -35
- package/dist/core/settings-tools-bridge.d.ts.map +0 -1
- package/dist/core/settings-tools-bridge.js +0 -56
- package/dist/core/settings-tools-bridge.js.map +0 -1
- package/dist/modes/interactive/injected-context.d.ts +0 -2
- package/dist/modes/interactive/injected-context.d.ts.map +0 -1
- package/dist/modes/interactive/injected-context.js +0 -7
- package/dist/modes/interactive/injected-context.js.map +0 -1
- package/dist/node-runtime/chunk-3NMWLTPV.js +0 -64
|
@@ -40,11 +40,11 @@ import {
|
|
|
40
40
|
compactNestedResultChildren,
|
|
41
41
|
deliverStopRequest,
|
|
42
42
|
deliverSubagentResultIntercomEvent,
|
|
43
|
-
flatToLogicalStepIndex,
|
|
44
43
|
formatActivityLabel,
|
|
45
44
|
formatAgentRunningLabel,
|
|
46
45
|
formatAsyncRunProgressLabel,
|
|
47
46
|
formatAsyncRunTranscript,
|
|
47
|
+
formatCompactModelBadge,
|
|
48
48
|
formatControlNoticeMessage,
|
|
49
49
|
formatDuration,
|
|
50
50
|
formatModelSelection,
|
|
@@ -64,12 +64,15 @@ import {
|
|
|
64
64
|
normalizeParallelGroups,
|
|
65
65
|
projectNestedRegistryForRoot,
|
|
66
66
|
readStatus,
|
|
67
|
+
readSteerCapability,
|
|
67
68
|
reconcileAsyncRun,
|
|
68
69
|
reconcileNestedAsyncDescendants,
|
|
69
70
|
registerMainWatchdog,
|
|
71
|
+
requestAsyncSteer,
|
|
70
72
|
resolveAsyncRunLocation,
|
|
71
73
|
resolveCurrentSessionId,
|
|
72
74
|
resolveSubagentResultStatus,
|
|
75
|
+
resolveSubagentRunId,
|
|
73
76
|
resolveTurnBudgetConfig,
|
|
74
77
|
resolveWaitToolConfig,
|
|
75
78
|
resolveWatchPath,
|
|
@@ -82,15 +85,19 @@ import {
|
|
|
82
85
|
validateExecutionAcceptance,
|
|
83
86
|
validateToolBudgetConfig,
|
|
84
87
|
writeAtomicJson
|
|
85
|
-
} from "./chunk-
|
|
86
|
-
import
|
|
88
|
+
} from "./chunk-46ECNOLF.js";
|
|
89
|
+
import {
|
|
90
|
+
createSubagentCancellation,
|
|
91
|
+
registerSubagentCancellation
|
|
92
|
+
} from "./chunk-BBUVIEZB.js";
|
|
93
|
+
import "./chunk-25I2BHAA.js";
|
|
87
94
|
import "./chunk-WKDK4C5P.js";
|
|
88
95
|
import "./chunk-53YJI7JU.js";
|
|
89
96
|
import "./chunk-KBKMCJDU.js";
|
|
90
97
|
import "./chunk-CSFI6YXL.js";
|
|
91
98
|
import "./chunk-FVDOXRUD.js";
|
|
92
99
|
import "./chunk-AD6VXAO7.js";
|
|
93
|
-
import "./chunk-
|
|
100
|
+
import "./chunk-6N22HULO.js";
|
|
94
101
|
import "./chunk-M3LZLQW2.js";
|
|
95
102
|
import "./chunk-RQ2Q5IYR.js";
|
|
96
103
|
import {
|
|
@@ -104,10 +111,10 @@ import "./chunk-Q4Q6D4IY.js";
|
|
|
104
111
|
import "./chunk-BRJX2PRC.js";
|
|
105
112
|
|
|
106
113
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
|
|
107
|
-
import { randomUUID as
|
|
108
|
-
import * as
|
|
114
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
115
|
+
import * as fs10 from "node:fs";
|
|
109
116
|
import * as os from "node:os";
|
|
110
|
-
import * as
|
|
117
|
+
import * as path12 from "node:path";
|
|
111
118
|
import { Box, Container as Container2, Spacer as Spacer2, Text as Text2, truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth3, wrapTextWithAnsi as wrapTextWithAnsi2 } from "@ashx-j/lunr-tui";
|
|
112
119
|
|
|
113
120
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/tui/render.js
|
|
@@ -238,6 +245,16 @@ function statJoin(theme, parts) {
|
|
|
238
245
|
function formatTokenStat(tokens) {
|
|
239
246
|
return `${formatTokens(tokens)} token`;
|
|
240
247
|
}
|
|
248
|
+
function usageTokenTotal(usage) {
|
|
249
|
+
return usage ? (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) : 0;
|
|
250
|
+
}
|
|
251
|
+
function compactResultTokens(result2) {
|
|
252
|
+
let progress = result2.progress || result2.progressSummary;
|
|
253
|
+
if (progress && progress.tokens > 0)
|
|
254
|
+
return progress.tokens;
|
|
255
|
+
let fromUsage = usageTokenTotal(result2.usage);
|
|
256
|
+
return fromUsage > 0 ? fromUsage : progress?.tokens ?? 0;
|
|
257
|
+
}
|
|
241
258
|
function formatToolUseStat(count) {
|
|
242
259
|
return `${count} tool use${count === 1 ? "" : "s"}`;
|
|
243
260
|
}
|
|
@@ -248,7 +265,10 @@ function formatTotalCostStat(totalCost) {
|
|
|
248
265
|
return totalCost.inputTokens && parts.push(`in:${formatTokens(totalCost.inputTokens)}`), totalCost.outputTokens && parts.push(`out:${formatTokens(totalCost.outputTokens)}`), totalCost.costUsd && parts.push(`$${totalCost.costUsd.toFixed(4)}`), parts.join(" ");
|
|
249
266
|
}
|
|
250
267
|
function liveDurationMs(progress, running, now) {
|
|
251
|
-
|
|
268
|
+
if (!progress)
|
|
269
|
+
return 0;
|
|
270
|
+
let durationMs = progress.durationMs ?? 0;
|
|
271
|
+
return !running || progress.lastActivityAt === void 0 ? durationMs : durationMs + Math.max(0, now - progress.lastActivityAt);
|
|
252
272
|
}
|
|
253
273
|
function formatProgressStats(theme, progress, includeDuration = !0, options = {}) {
|
|
254
274
|
if (!progress)
|
|
@@ -288,13 +308,16 @@ function resultStatusLine(result2, output) {
|
|
|
288
308
|
function resultGlyph(result2, output, theme, running = result2.progress?.status === "running", seed = progressRunningSeed(result2.progress ?? result2.progressSummary), frame) {
|
|
289
309
|
return running ? frame !== void 0 ? theme.fg("accent", runningGlyph((seed ?? 0) + frame)) : theme.fg("accent", runningGlyph(seed)) : result2.detached || result2.stopped || result2.interrupted ? theme.fg("warning", "\u25A0") : result2.exitCode !== 0 ? theme.fg("error", "\u2717") : hasEmptyTextOutputWithoutOutputTarget(result2.task, output) ? theme.fg("warning", "\u2713") : theme.fg("success", "\u2713");
|
|
290
310
|
}
|
|
291
|
-
function
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
311
|
+
function formatCompactRowStats(tokens, durationMs) {
|
|
312
|
+
return `${formatTokenStat(tokens)} \xB7 ${durationMs > 0 ? formatDuration(durationMs) : "0ms"}`;
|
|
313
|
+
}
|
|
314
|
+
function formatCompactSubagentRow(theme, width, parts) {
|
|
315
|
+
let stats = formatCompactRowStats(parts.tokens, parts.durationMs), body = [
|
|
316
|
+
`${parts.prefix ?? ""}${parts.glyph} ${themeBold(theme, parts.description)}`,
|
|
317
|
+
parts.modelBadge ? theme.fg("dim", parts.modelBadge) : "",
|
|
318
|
+
theme.fg("dim", stats)
|
|
319
|
+
].filter(Boolean).join(` ${theme.fg("dim", "\xB7")} `);
|
|
320
|
+
return truncLine(body, width);
|
|
298
321
|
}
|
|
299
322
|
function widgetRenderKey(job) {
|
|
300
323
|
return JSON.stringify({
|
|
@@ -330,12 +353,6 @@ function formatWidgetAgents(agents) {
|
|
|
330
353
|
function widgetJobName(job) {
|
|
331
354
|
return job.mode === "parallel" ? "parallel" : job.mode === "chain" ? "chain" : job.mode === "single" && job.agents?.length === 1 ? job.agents[0] : job.agents?.length ? formatWidgetAgents(job.agents) : job.mode ?? "subagent";
|
|
332
355
|
}
|
|
333
|
-
function widgetActivity(job, now = Date.now()) {
|
|
334
|
-
let facts = [], snapshotNow = job.status === "running" ? now : job.updatedAt;
|
|
335
|
-
job.currentTool && job.currentToolStartedAt !== void 0 && snapshotNow !== void 0 ? facts.push(`${job.currentTool} ${formatDuration(Math.max(0, snapshotNow - job.currentToolStartedAt))}`) : job.currentTool && facts.push(job.currentTool), job.currentPath && facts.push(shortenPath(job.currentPath)), job.turnCount !== void 0 && facts.push(`${job.turnCount} turns`), job.toolCount !== void 0 && facts.push(`${job.toolCount} tools`);
|
|
336
|
-
let activity = buildLiveStatusLine(job, snapshotNow);
|
|
337
|
-
return activity && facts.length ? `${activity} \xB7 ${facts.join(" \xB7 ")}` : activity || (facts.length ? facts.join(" \xB7 ") : job.status === "running" ? "thinking\u2026" : job.status === "queued" ? "queued\u2026" : job.status === "paused" ? "Paused" : job.status === "stopped" ? "Stopped" : job.status === "failed" ? "Failed" : "Done");
|
|
338
|
-
}
|
|
339
356
|
function widgetStepRunningSeed(step, fallbackIndex) {
|
|
340
357
|
return runningSeed(fallbackIndex, step.index);
|
|
341
358
|
}
|
|
@@ -549,29 +566,6 @@ function resultRowLabel(details, label, resultIndex, stepNumber) {
|
|
|
549
566
|
}
|
|
550
567
|
return label.itemTitle === "Agent" ? `Agent ${label.activeParallelGroup ? Math.max(1, stepNumber - label.groupStartIndex) : stepNumber}/${label.totalCount}` : `Step ${stepNumber}`;
|
|
551
568
|
}
|
|
552
|
-
function widgetStats(job, theme, now = Date.now()) {
|
|
553
|
-
let parts = [], stepsTotal = job.stepsTotal ?? job.agents?.length ?? 1;
|
|
554
|
-
if (job.activeParallelGroup) {
|
|
555
|
-
let running = job.runningSteps ?? (job.status === "running" ? 1 : 0), done = job.completedSteps ?? (job.status === "complete" ? stepsTotal : 0);
|
|
556
|
-
if (job.mode === "parallel")
|
|
557
|
-
job.status === "running" && running > 0 && parts.push(formatAgentRunningLabel(running)), stepsTotal > 0 && parts.push(`${done}/${stepsTotal} done`);
|
|
558
|
-
else {
|
|
559
|
-
let logicalStep = (job.currentStep !== void 0 ? job.parallelGroups?.find((group) => job.currentStep >= group.start && job.currentStep < group.start + group.count) : job.parallelGroups?.find((group) => group.start === 0))?.stepIndex ?? job.currentStep ?? 0, total = job.chainStepCount ?? stepsTotal, groupParts = [`${done}/${stepsTotal} done`];
|
|
560
|
-
job.status === "running" && running > 0 && groupParts.unshift(formatAgentRunningLabel(running)), parts.push(`step ${logicalStep + 1}/${total} \xB7 parallel group: ${groupParts.join(" \xB7 ")}`);
|
|
561
|
-
}
|
|
562
|
-
} else if (job.currentStep !== void 0)
|
|
563
|
-
if (job.mode === "chain" && job.parallelGroups?.length) {
|
|
564
|
-
let total = job.chainStepCount ?? stepsTotal;
|
|
565
|
-
parts.push(`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups) + 1}/${total}`);
|
|
566
|
-
} else
|
|
567
|
-
parts.push(`step ${job.currentStep + 1}/${stepsTotal}`);
|
|
568
|
-
else stepsTotal > 1 && parts.push(`steps ${stepsTotal}`);
|
|
569
|
-
if (job.toolCount !== void 0 && parts.push(formatToolUseStat(job.toolCount)), job.totalTokens?.total && parts.push(formatTokenStat(job.totalTokens.total)), job.startedAt !== void 0) {
|
|
570
|
-
let end = job.status === "running" ? now : job.updatedAt;
|
|
571
|
-
end !== void 0 && parts.push(`${job.status === "running" ? "" : "ran for "}${formatDuration(Math.max(0, end - job.startedAt))}`);
|
|
572
|
-
}
|
|
573
|
-
return statJoin(theme, parts);
|
|
574
|
-
}
|
|
575
569
|
function widgetStepStats(theme, step, now = Date.now()) {
|
|
576
570
|
let running = step.status === "running", durationMs = step.durationMs !== void 0 ? step.durationMs + (running && step.lastActivityAt !== void 0 ? Math.max(0, now - step.lastActivityAt) : 0) : step.startedAt !== void 0 ? Math.max(0, (running ? now : step.endedAt ?? step.lastActivityAt ?? now) - step.startedAt) : void 0;
|
|
577
571
|
return statJoin(theme, [
|
|
@@ -663,12 +657,17 @@ function foregroundStyleWidgetStepLines(job, theme, step, itemTitle, index, tota
|
|
|
663
657
|
return lines;
|
|
664
658
|
}
|
|
665
659
|
function compactJobProgress(job, step, now = Date.now()) {
|
|
666
|
-
let running = (step?.status ?? job.status) === "running",
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
660
|
+
let running = (step?.status ?? job.status) === "running", tokens = step?.tokens?.total ?? job.totalTokens?.total ?? 0;
|
|
661
|
+
if (step?.durationMs !== void 0)
|
|
662
|
+
return {
|
|
663
|
+
tokens,
|
|
664
|
+
durationMs: liveDurationMs({ durationMs: step.durationMs, lastActivityAt: step.lastActivityAt }, running, now)
|
|
665
|
+
};
|
|
666
|
+
let startedAt = step?.startedAt ?? job.startedAt;
|
|
667
|
+
if (startedAt === void 0)
|
|
668
|
+
return { tokens, durationMs: 0 };
|
|
669
|
+
let endedAt = running ? now : step?.endedAt ?? step?.lastActivityAt ?? job.updatedAt ?? now;
|
|
670
|
+
return { tokens, durationMs: Math.max(0, endedAt - startedAt) };
|
|
672
671
|
}
|
|
673
672
|
function compactJobLead(job, step) {
|
|
674
673
|
return compactRowLead({
|
|
@@ -676,19 +675,30 @@ function compactJobLead(job, step) {
|
|
|
676
675
|
task: step?.description ?? step?.agent
|
|
677
676
|
}) || widgetJobName(job);
|
|
678
677
|
}
|
|
679
|
-
function
|
|
680
|
-
let
|
|
681
|
-
return [
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
];
|
|
678
|
+
function activeJobStep(job) {
|
|
679
|
+
let steps = job.steps ?? [];
|
|
680
|
+
return steps.find((step) => step.status === "running") ?? steps.find((step) => step.status === "pending" || step.status === "queued") ?? steps[steps.length - 1];
|
|
681
|
+
}
|
|
682
|
+
function compactJobChildRows(job, theme, width, now = Date.now()) {
|
|
683
|
+
let steps = job.steps ?? [];
|
|
684
|
+
return steps.length <= 1 ? compactJobLines(job, steps[0], theme, width, now) : steps.flatMap((step) => compactJobLines(job, step, theme, width, now));
|
|
685
685
|
}
|
|
686
|
-
function
|
|
687
|
-
let
|
|
686
|
+
function compactJobLines(job, step, theme, width, now = Date.now()) {
|
|
687
|
+
let running = (step?.status ?? job.status) === "running", lead = compactJobLead(job, step), modelBadge = formatCompactModelBadge(step?.modelSelection, step?.model), glyph = step ? widgetStepGlyph(step.status, theme, running ? (widgetStepRunningSeed(step, step.index) ?? 0) + widgetAnimFrame(now) : widgetStepRunningSeed(step, step.index)) : widgetStatusGlyph(job, theme, now), progress = compactJobProgress(job, step, now);
|
|
688
|
+
return [formatCompactSubagentRow(theme, width, {
|
|
689
|
+
glyph,
|
|
690
|
+
description: lead,
|
|
691
|
+
modelBadge,
|
|
692
|
+
tokens: progress.tokens,
|
|
693
|
+
durationMs: progress.durationMs
|
|
694
|
+
})];
|
|
695
|
+
}
|
|
696
|
+
function buildSingleWidgetLines(job, theme, width, _expanded, now = Date.now()) {
|
|
697
|
+
let steps = job.steps ?? [];
|
|
688
698
|
return steps.length <= 1 ? compactJobLines(job, steps[0], theme, width, now) : steps.flatMap((step) => compactJobLines(job, step, theme, width, now));
|
|
689
699
|
}
|
|
690
|
-
function compactSingleWidgetLines(job, theme, width) {
|
|
691
|
-
return buildSingleWidgetLines(job, theme, width, !1);
|
|
700
|
+
function compactSingleWidgetLines(job, theme, width, now = Date.now()) {
|
|
701
|
+
return buildSingleWidgetLines(job, theme, width, !1, now);
|
|
692
702
|
}
|
|
693
703
|
var RESERVED_NON_WIDGET_ROWS = 19, widgetLayoutSession;
|
|
694
704
|
function resetWidgetLayoutSession() {
|
|
@@ -718,7 +728,7 @@ function widgetHeaderCounts(jobs) {
|
|
|
718
728
|
};
|
|
719
729
|
}
|
|
720
730
|
function buildSingleLineWidgetLines(jobs, theme, width) {
|
|
721
|
-
let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "\u25CF" : "\u25CB", parts = [];
|
|
731
|
+
let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph((widgetJobsRunningSeed(counts.running) ?? 0) + widgetAnimFrame()) : hasActive ? "\u25CF" : "\u25CB", parts = [];
|
|
722
732
|
return counts.running.length > 0 && parts.push(`${counts.running.length}/${jobs.length} running`), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`), counts.failed.length > 0 && parts.push(`${counts.failed.length} failed`), counts.stopped.length > 0 && parts.push(`${counts.stopped.length} stopped`), counts.paused.length > 0 && parts.push(`${counts.paused.length} paused`), !hasActive && counts.complete.length > 0 && parts.push(`${counts.complete.length}/${jobs.length} done`), [truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "subagents")} (${parts.join(", ") || `${jobs.length} total`})`, width)];
|
|
723
733
|
}
|
|
724
734
|
function orderedWidgetJobs(jobs) {
|
|
@@ -763,17 +773,12 @@ function selectProgressiveJobKeys(jobs, previousKeys, bodyRows) {
|
|
|
763
773
|
return selected;
|
|
764
774
|
}
|
|
765
775
|
function progressiveHeaderLine(jobs, theme, width) {
|
|
766
|
-
let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph(widgetJobsRunningSeed(counts.running)) : hasActive ? "\u25CF" : "\u25CB", parts = [];
|
|
776
|
+
let counts = widgetHeaderCounts(jobs), hasActive = counts.running.length > 0 || counts.queued.length > 0, glyph = counts.running.length > 0 ? runningGlyph((widgetJobsRunningSeed(counts.running) ?? 0) + widgetAnimFrame()) : hasActive ? "\u25CF" : "\u25CB", parts = [];
|
|
767
777
|
return counts.running.length > 0 && parts.push(formatAgentRunningLabel(counts.running.length)), counts.queued.length > 0 && parts.push(`${counts.queued.length} queued`), hasActive || (counts.failed.length > 0 && parts.push(`${counts.failed.length} failed`), counts.stopped.length > 0 && parts.push(`${counts.stopped.length} stopped`), counts.paused.length > 0 && parts.push(`${counts.paused.length} paused`), counts.complete.length > 0 && parts.push(`${counts.complete.length}/${jobs.length} done`)), truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "\xB7")} ${theme.fg("dim", parts.join(", ") || `${jobs.length} total`)}`, width);
|
|
768
778
|
}
|
|
769
|
-
function progressiveJobLine(job, theme, width) {
|
|
770
|
-
let
|
|
771
|
-
|
|
772
|
-
theme.fg("dim", status),
|
|
773
|
-
stats,
|
|
774
|
-
activity && activity.toLowerCase() !== status ? theme.fg("dim", activity) : ""
|
|
775
|
-
].filter(Boolean);
|
|
776
|
-
return truncLine(` ${widgetStatusGlyph(job, theme)} ${parts.join(` ${theme.fg("dim", "\xB7")} `)}`, width);
|
|
779
|
+
function progressiveJobLine(job, theme, width, now = Date.now()) {
|
|
780
|
+
let row = compactJobLines(job, activeJobStep(job), theme, Math.max(1, width - 2), now)[0] ?? "";
|
|
781
|
+
return row ? ` ${row}` : "";
|
|
777
782
|
}
|
|
778
783
|
function progressiveHiddenLine(hiddenJobs, theme, width) {
|
|
779
784
|
let counts = widgetHeaderCounts(hiddenJobs), parts = [];
|
|
@@ -822,20 +827,12 @@ function fitAdaptiveWidgetLines(jobs, lines, theme, width, expanded) {
|
|
|
822
827
|
let lockedRows = Math.min(availableRows, collapsedWidgetLineBudget(rows)), rendered = buildProgressiveWidgetLines(jobs, theme, width, lockedRows, []);
|
|
823
828
|
return widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows, visibleJobKeys: rendered.visibleJobKeys }, rendered.lines;
|
|
824
829
|
}
|
|
825
|
-
function
|
|
826
|
-
return (_tui, theme) => {
|
|
827
|
-
let width = getTermWidth(), lines = expanded ? buildWidgetLines(jobs, theme, width, !0) : jobs.length === 1 ? compactSingleWidgetLines(jobs[0], theme, width) : buildWidgetLines(jobs, theme, width, !1), container = new Container();
|
|
828
|
-
for (let line of fitAdaptiveWidgetLines(jobs, lines, theme, width, expanded))
|
|
829
|
-
container.addChild(new Text(line, 1, 0));
|
|
830
|
-
return container;
|
|
831
|
-
};
|
|
832
|
-
}
|
|
833
|
-
function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1) {
|
|
830
|
+
function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1, now = Date.now()) {
|
|
834
831
|
if (jobs.length === 0)
|
|
835
832
|
return [];
|
|
836
833
|
if (jobs.length === 1)
|
|
837
|
-
return buildSingleWidgetLines(jobs[0], theme, width, expanded);
|
|
838
|
-
let running = jobs.filter((job) => job.status === "running"), queued = jobs.filter((job) => job.status === "queued"), finished = jobs.filter((job) => job.status !== "running" && job.status !== "queued"), lines = [], hasActive = running.length > 0 || queued.length > 0, headerGlyph = running.length > 0 ? runningGlyph(widgetJobsRunningSeed(running)) : hasActive ? "\u25CF" : "\u25CB";
|
|
834
|
+
return buildSingleWidgetLines(jobs[0], theme, width, expanded, now);
|
|
835
|
+
let running = jobs.filter((job) => job.status === "running"), queued = jobs.filter((job) => job.status === "queued"), finished = jobs.filter((job) => job.status !== "running" && job.status !== "queued"), lines = [], hasActive = running.length > 0 || queued.length > 0, headerGlyph = running.length > 0 ? runningGlyph((widgetJobsRunningSeed(running) ?? 0) + widgetAnimFrame(now)) : hasActive ? "\u25CF" : "\u25CB";
|
|
839
836
|
lines.push(truncLine(`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "\xB7 background")}`, width));
|
|
840
837
|
let items = [], hiddenRunning = 0, hiddenFinished = 0, queuedSummaryShown = !1, slots = MAX_WIDGET_JOBS;
|
|
841
838
|
for (let job of running) {
|
|
@@ -843,12 +840,8 @@ function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1) {
|
|
|
843
840
|
hiddenRunning++;
|
|
844
841
|
continue;
|
|
845
842
|
}
|
|
846
|
-
let
|
|
847
|
-
items.push([
|
|
848
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
|
|
849
|
-
` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
|
|
850
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
851
|
-
]), slots--;
|
|
843
|
+
let row = compactJobChildRows(job, theme, Math.max(1, width - 3), now);
|
|
844
|
+
items.push(expanded ? [...row, ...widgetParallelAgentDetails(job, theme, !0, width)] : row), slots--;
|
|
852
845
|
}
|
|
853
846
|
queued.length > 0 && slots > 0 && (items.push([`${theme.fg("muted", "\u25E6")} ${theme.fg("dim", `${queued.length} queued`)}`]), queuedSummaryShown = !0, slots--);
|
|
854
847
|
for (let job of finished) {
|
|
@@ -856,12 +849,8 @@ function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1) {
|
|
|
856
849
|
hiddenFinished++;
|
|
857
850
|
continue;
|
|
858
851
|
}
|
|
859
|
-
let
|
|
860
|
-
items.push([
|
|
861
|
-
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`,
|
|
862
|
-
` ${theme.fg("dim", `\u23BF ${widgetActivity(job)}`)}`,
|
|
863
|
-
...widgetParallelAgentDetails(job, theme, expanded, width)
|
|
864
|
-
]), slots--;
|
|
852
|
+
let row = compactJobChildRows(job, theme, Math.max(1, width - 3), now);
|
|
853
|
+
items.push(expanded ? [...row, ...widgetParallelAgentDetails(job, theme, !0, width)] : row), slots--;
|
|
865
854
|
}
|
|
866
855
|
let hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0, hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
|
|
867
856
|
if (hiddenTotal > 0) {
|
|
@@ -876,36 +865,91 @@ function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = !1) {
|
|
|
876
865
|
}
|
|
877
866
|
return lines;
|
|
878
867
|
}
|
|
879
|
-
var
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
868
|
+
var activeWidget, SubagentAsyncWidget = class {
|
|
869
|
+
jobs;
|
|
870
|
+
expanded;
|
|
871
|
+
theme;
|
|
872
|
+
requestRender;
|
|
873
|
+
nowFn;
|
|
874
|
+
timer;
|
|
875
|
+
disposed = !1;
|
|
876
|
+
cachedWidth;
|
|
877
|
+
cachedLines;
|
|
878
|
+
constructor(jobs, theme, options) {
|
|
879
|
+
this.jobs = jobs, this.theme = theme, this.expanded = options?.expanded ?? !1, this.requestRender = options?.requestRender, this.nowFn = options?.now ?? Date.now, this.syncTimer();
|
|
887
880
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
}
|
|
881
|
+
setTheme(theme) {
|
|
882
|
+
this.theme = theme, this.invalidate();
|
|
883
|
+
}
|
|
884
|
+
setJobs(jobs) {
|
|
885
|
+
this.jobs = jobs, this.invalidate(), this.syncTimer();
|
|
886
|
+
}
|
|
887
|
+
setExpanded(expanded) {
|
|
888
|
+
this.expanded !== expanded && (this.expanded = expanded, this.invalidate());
|
|
889
|
+
}
|
|
890
|
+
setRequestRender(requestRender) {
|
|
891
|
+
this.requestRender = requestRender;
|
|
892
|
+
}
|
|
893
|
+
hasRunning() {
|
|
894
|
+
return this.jobs.some((job) => job.status === "running");
|
|
895
|
+
}
|
|
896
|
+
syncTimer() {
|
|
897
|
+
this.disposed || (this.hasRunning() ? this.startTimer() : this.stopTimer());
|
|
898
|
+
}
|
|
899
|
+
startTimer() {
|
|
900
|
+
this.timer || (this.timer = setInterval(() => {
|
|
901
|
+
this.disposed || (this.invalidate(), this.requestRender?.());
|
|
902
|
+
}, 80), this.timer.unref?.());
|
|
903
|
+
}
|
|
904
|
+
stopTimer() {
|
|
905
|
+
this.timer && (clearInterval(this.timer), this.timer = void 0);
|
|
906
|
+
}
|
|
907
|
+
invalidate() {
|
|
908
|
+
this.cachedWidth = void 0, this.cachedLines = void 0;
|
|
909
|
+
}
|
|
910
|
+
dispose() {
|
|
911
|
+
this.disposed = !0, this.stopTimer(), activeWidget === this && (activeWidget = void 0);
|
|
912
|
+
}
|
|
913
|
+
render(width) {
|
|
914
|
+
let theme = this.theme;
|
|
915
|
+
if (!theme)
|
|
916
|
+
return [];
|
|
917
|
+
if (this.cachedLines && this.cachedWidth === width && !this.hasRunning())
|
|
918
|
+
return this.cachedLines;
|
|
919
|
+
let now = this.nowFn(), innerWidth = Math.max(1, width - 2), lines = this.expanded ? buildWidgetLines(this.jobs, theme, innerWidth, !0, now) : this.jobs.length === 1 ? compactSingleWidgetLines(this.jobs[0], theme, innerWidth, now) : buildWidgetLines(this.jobs, theme, innerWidth, !1, now), padded = fitAdaptiveWidgetLines(this.jobs, lines, theme, innerWidth, this.expanded).map((line) => {
|
|
920
|
+
let withPad = ` ${line}`, vis = visibleWidth(withPad);
|
|
921
|
+
return withPad + " ".repeat(Math.max(0, width - vis));
|
|
922
|
+
});
|
|
923
|
+
return this.cachedWidth = width, this.cachedLines = padded, padded;
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
function disposeSubagentWidget() {
|
|
927
|
+
activeWidget?.dispose(), activeWidget = void 0;
|
|
891
928
|
}
|
|
892
929
|
function renderWidget(ctx, jobs) {
|
|
893
930
|
if (jobs.length === 0) {
|
|
894
|
-
resetWidgetLayoutSession(),
|
|
931
|
+
resetWidgetLayoutSession(), disposeSubagentWidget(), ctx.hasUI && ctx.ui.setWidget(WIDGET_KEY, void 0);
|
|
895
932
|
return;
|
|
896
933
|
}
|
|
897
|
-
|
|
934
|
+
if (!ctx.hasUI)
|
|
935
|
+
return;
|
|
936
|
+
let expanded = ctx.ui.getToolsExpanded?.() ?? !1, requestRender = () => ctx.ui.requestRender?.();
|
|
937
|
+
if (activeWidget) {
|
|
938
|
+
activeWidget.setJobs(jobs), activeWidget.setExpanded(expanded), activeWidget.setRequestRender(requestRender), ctx.ui.requestRender?.();
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
let widget = new SubagentAsyncWidget(jobs, void 0, { expanded, requestRender });
|
|
942
|
+
activeWidget = widget, ctx.ui.setWidget(WIDGET_KEY, (_tui, theme) => (widget.setTheme(theme), widget));
|
|
898
943
|
}
|
|
899
944
|
function renderSingleCompact(d, r, theme, frame) {
|
|
900
|
-
let output = r.truncation?.text || getSingleResultOutput(r), progress = r.progress || r.progressSummary, isRunning = r.progress?.status === "running",
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
return preview && r.exitCode === 0 && !hasEmptyTextOutputWithoutOutputTarget(r.task, output) && c.addChild(new Text(truncLine(theme.fg("dim", ` ${preview}`), width), 0, 0)), r.sessionFile && c.addChild(new Text(truncLine(theme.fg("dim", ` session: ${shortenPath(r.sessionFile)}`), width), 0, 0)), c;
|
|
945
|
+
let output = r.truncation?.text || getSingleResultOutput(r), progress = r.progress || r.progressSummary, isRunning = r.progress?.status === "running", c = new Container(), width = getTermWidth() - 4, lead = compactRowLead({ description: r.description ?? r.agent, task: r.task }), modelBadge = formatCompactModelBadge(r.modelSelection ?? progress?.modelSelection, r.model ?? progress?.model);
|
|
946
|
+
return c.addChild(animatedLine((f, now) => formatCompactSubagentRow(theme, width, {
|
|
947
|
+
glyph: resultGlyph(r, output, theme, isRunning, void 0, f),
|
|
948
|
+
description: lead,
|
|
949
|
+
modelBadge,
|
|
950
|
+
tokens: compactResultTokens(r),
|
|
951
|
+
durationMs: liveDurationMs(progress, isRunning, now)
|
|
952
|
+
}), frame, isRunning)), !isRunning && (r.exitCode !== 0 || r.interrupted || r.detached || r.stopped) && c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` \u23BF ${resultStatusLine(r, output)}`), width), 0, 0)), c;
|
|
909
953
|
}
|
|
910
954
|
function renderMultiCompact(d, theme, frame) {
|
|
911
955
|
let hasRunning = d.progress?.some((p) => p.status === "running") || d.results.some((r) => r.progress?.status === "running") || workflowGraphHasStatus(d, ["running"]), stopped = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]), failed = d.results.some((r) => !r.stopped && r.exitCode !== 0 && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["failed"]), paused = d.results.some((r) => (r.interrupted || r.detached) && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["paused", "detached"]), totalSummary = d.progressSummary;
|
|
@@ -918,8 +962,8 @@ function renderMultiCompact(d, theme, frame) {
|
|
|
918
962
|
sawProgress && (totalSummary = summary);
|
|
919
963
|
}
|
|
920
964
|
let multiLabel = buildMultiProgressLabel(d, hasRunning), itemTitle = multiLabel.itemTitle, glyph = (f) => hasRunning ? theme.fg("accent", runningGlyph(f !== void 0 ? (runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex) ?? 0) + f : runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex))) : stopped ? theme.fg("warning", "\u25A0") : failed ? theme.fg("error", "\u2717") : paused ? theme.fg("warning", "\u25A0") : theme.fg("success", "\u2713"), contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "", c = new Container(), width = getTermWidth() - 4;
|
|
921
|
-
c.addChild(animatedLine((f
|
|
922
|
-
let stats = statJoin(theme, [multiLabel.headerLabel,
|
|
965
|
+
c.addChild(animatedLine((f) => {
|
|
966
|
+
let stats = statJoin(theme, [multiLabel.headerLabel, formatTotalCostStat(d.totalCost)]);
|
|
923
967
|
return truncLine(`${glyph(f)} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "\xB7")} ${stats}` : ""}`, width);
|
|
924
968
|
}, frame, hasRunning));
|
|
925
969
|
let useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length, displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0, displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : useResultsDirectly ? d.results.length : d.chainAgents.length, chainEntries = buildChainRenderEntries(d, multiLabel), renderEntries = chainEntries ?? Array.from({ length: displayEnd - displayStart }, (_, offset) => {
|
|
@@ -934,15 +978,22 @@ function renderMultiCompact(d, theme, frame) {
|
|
|
934
978
|
}
|
|
935
979
|
let i = entry.resultIndex, r = d.results[i], rowNumber = entry.rowNumber, agentName = entry.agentName;
|
|
936
980
|
if (!r) {
|
|
937
|
-
let
|
|
938
|
-
c.addChild(new Text(truncLine(theme.fg("dim", ` \u25E6 ${
|
|
981
|
+
let pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
|
|
982
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` \u25E6 ${pendingLabel}: ${agentName} \xB7 pending`), width), 0, 0));
|
|
939
983
|
continue;
|
|
940
984
|
}
|
|
941
|
-
let output = getSingleResultOutput(r), progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running"), rProg = r.progress || progressFromArray || r.progressSummary, rRunning = rProg && "status" in rProg && rProg.status === "running", rPending = rProg && "status" in rProg && rProg.status === "pending",
|
|
985
|
+
let output = getSingleResultOutput(r), progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running"), rProg = r.progress || progressFromArray || r.progressSummary, rRunning = rProg && "status" in rProg && rProg.status === "running", rPending = rProg && "status" in rProg && rProg.status === "pending", lead = compactRowLead({ description: r.description ?? agentName, task: r.task }), modelBadge = formatCompactModelBadge(r.modelSelection ?? (rProg && "modelSelection" in rProg ? rProg.modelSelection : void 0), r.model ?? (rProg && "model" in rProg ? rProg.model : void 0));
|
|
942
986
|
c.addChild(animatedLine((f, now) => {
|
|
943
|
-
let
|
|
944
|
-
return
|
|
945
|
-
|
|
987
|
+
let rowGlyph = rPending ? theme.fg("dim", "\u25E6") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), f);
|
|
988
|
+
return formatCompactSubagentRow(theme, width, {
|
|
989
|
+
glyph: rowGlyph,
|
|
990
|
+
description: lead,
|
|
991
|
+
modelBadge,
|
|
992
|
+
tokens: compactResultTokens(r),
|
|
993
|
+
durationMs: liveDurationMs(rProg, !!rRunning, now),
|
|
994
|
+
prefix: " "
|
|
995
|
+
});
|
|
996
|
+
}, frame, !!rRunning)), !rPending && !rRunning && (r.exitCode !== 0 || r.interrupted || r.detached || r.stopped) && c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` \u23BF ${resultStatusLine(r, output)}`), width), 0, 0));
|
|
946
997
|
}
|
|
947
998
|
return c;
|
|
948
999
|
}
|
|
@@ -1057,15 +1108,15 @@ import { Type } from "typebox";
|
|
|
1057
1108
|
function keepTopLevelParameterDescriptions(schema) {
|
|
1058
1109
|
return pruneNestedDescriptions(schema, []);
|
|
1059
1110
|
}
|
|
1060
|
-
function pruneNestedDescriptions(value,
|
|
1111
|
+
function pruneNestedDescriptions(value, path13) {
|
|
1061
1112
|
if (!value || typeof value != "object")
|
|
1062
1113
|
return value;
|
|
1063
1114
|
let result2 = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
|
|
1064
1115
|
for (let key of Reflect.ownKeys(value)) {
|
|
1065
1116
|
let descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1066
|
-
if (descriptor && !(key === "description" && !isPropertiesMap(
|
|
1117
|
+
if (descriptor && !(key === "description" && !isPropertiesMap(path13) && !isTopLevelParameterHelpText(path13))) {
|
|
1067
1118
|
if ("value" in descriptor) {
|
|
1068
|
-
let nextPath = typeof key == "string" ? [...
|
|
1119
|
+
let nextPath = typeof key == "string" ? [...path13, key] : path13;
|
|
1069
1120
|
descriptor.value = pruneNestedDescriptions(descriptor.value, nextPath);
|
|
1070
1121
|
}
|
|
1071
1122
|
Object.defineProperty(result2, key, descriptor);
|
|
@@ -1073,11 +1124,11 @@ function pruneNestedDescriptions(value, path12) {
|
|
|
1073
1124
|
}
|
|
1074
1125
|
return result2;
|
|
1075
1126
|
}
|
|
1076
|
-
function isPropertiesMap(
|
|
1077
|
-
return
|
|
1127
|
+
function isPropertiesMap(path13) {
|
|
1128
|
+
return path13[path13.length - 1] === "properties";
|
|
1078
1129
|
}
|
|
1079
|
-
function isTopLevelParameterHelpText(
|
|
1080
|
-
return
|
|
1130
|
+
function isTopLevelParameterHelpText(path13) {
|
|
1131
|
+
return path13.length === 2 && path13[0] === "properties";
|
|
1081
1132
|
}
|
|
1082
1133
|
var SkillOverride = Type.Unsafe({
|
|
1083
1134
|
anyOf: [
|
|
@@ -1095,10 +1146,16 @@ var SkillOverride = Type.Unsafe({
|
|
|
1095
1146
|
}), OutputModeOverride = Type.String({
|
|
1096
1147
|
enum: ["inline", "file-only"],
|
|
1097
1148
|
description: "Return saved output inline (default) or only a concise file reference. file-only requires output to be a path."
|
|
1098
|
-
}), TierOverride = Type.String({
|
|
1149
|
+
}), TIER_SELECTION_DESCRIPTION = "Choose the lowest model tier that can reliably complete the task: 'light' for quick checks, fact gathering, and small code inspections; 'standard' for everyday coding, clearly described bugs, and focused reviews; 'heavy' for complex implementation, difficult debugging, architectural decisions, and large or high-risk reviews.", TierOverride = Type.Optional(Type.String({
|
|
1099
1150
|
enum: ["light", "standard", "heavy"],
|
|
1100
|
-
description:
|
|
1101
|
-
}),
|
|
1151
|
+
description: `Required by default. ${TIER_SELECTION_DESCRIPTION} Do not combine with model. The configured tier model must be available and authenticated.`
|
|
1152
|
+
})), ModelOverride = Type.Optional(Type.String({
|
|
1153
|
+
minLength: 1,
|
|
1154
|
+
description: "Explicit provider/model id only when the user names a model. Do not combine with tier. Optional thinking belongs in the thinking field or as a known :level suffix."
|
|
1155
|
+
})), ThinkingOverride = Type.Optional(Type.String({
|
|
1156
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1157
|
+
description: "Thinking level for an explicit model launch. Omit for tier launches (they use configured tier thinking)."
|
|
1158
|
+
})), ReadsOverride = Type.Unsafe({
|
|
1102
1159
|
anyOf: [
|
|
1103
1160
|
{ type: "array", items: { type: "string" } },
|
|
1104
1161
|
{ type: "boolean", enum: [!1] }
|
|
@@ -1145,6 +1202,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1145
1202
|
reads: Type.Optional(ReadsOverride),
|
|
1146
1203
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking for this task" })),
|
|
1147
1204
|
tier: TierOverride,
|
|
1205
|
+
model: ModelOverride,
|
|
1206
|
+
thinking: ThinkingOverride,
|
|
1148
1207
|
skill: Type.Optional(SkillOverride),
|
|
1149
1208
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1150
1209
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
@@ -1164,6 +1223,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1164
1223
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1165
1224
|
skill: Type.Optional(SkillOverride),
|
|
1166
1225
|
tier: TierOverride,
|
|
1226
|
+
model: ModelOverride,
|
|
1227
|
+
thinking: ThinkingOverride,
|
|
1167
1228
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1168
1229
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1169
1230
|
}), DynamicExpandSchema = Type.Object({
|
|
@@ -1193,6 +1254,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1193
1254
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1194
1255
|
skill: Type.Optional(SkillOverride),
|
|
1195
1256
|
tier: TierOverride,
|
|
1257
|
+
model: ModelOverride,
|
|
1258
|
+
thinking: ThinkingOverride,
|
|
1196
1259
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1197
1260
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1198
1261
|
}, { additionalProperties: !1 }), DynamicCollectSchema = Type.Object({
|
|
@@ -1215,6 +1278,8 @@ var SkillOverride = Type.Unsafe({
|
|
|
1215
1278
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
1216
1279
|
skill: Type.Optional(SkillOverride),
|
|
1217
1280
|
tier: TierOverride,
|
|
1281
|
+
model: ModelOverride,
|
|
1282
|
+
thinking: ThinkingOverride,
|
|
1218
1283
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
1219
1284
|
acceptance: Type.Optional(AcceptanceOverride),
|
|
1220
1285
|
parallel: Type.Optional(Type.Unsafe({
|
|
@@ -1232,7 +1297,7 @@ var SkillOverride = Type.Unsafe({
|
|
|
1232
1297
|
description: "Create isolated git worktrees for each parallel task."
|
|
1233
1298
|
}))
|
|
1234
1299
|
}, {
|
|
1235
|
-
description: "Chain step: use {task, description, tier, permissions?} for sequential, {parallel: [...]} for static concurrent execution, or {expand, parallel: {...}, collect} for dynamic fanout. Every executable child
|
|
1300
|
+
description: "Chain step: use {task, description, tier|model, permissions?} for sequential, {parallel: [...]} for static concurrent execution, or {expand, parallel: {...}, collect} for dynamic fanout. Every executable child needs exactly one of tier or model.",
|
|
1236
1301
|
additionalProperties: !1
|
|
1237
1302
|
}), ControlOverrides = Type.Object({
|
|
1238
1303
|
enabled: Type.Optional(Type.Boolean({ description: "Enable/disable subagent control attention tracking for this run" })),
|
|
@@ -1271,12 +1336,9 @@ var SkillOverride = Type.Unsafe({
|
|
|
1271
1336
|
})),
|
|
1272
1337
|
lines: Type.Optional(Type.Integer({ minimum: 1, maximum: 500, description: "Maximum transcript lines for action='status', view='transcript'. Defaults to 80." })),
|
|
1273
1338
|
message: Type.Optional(Type.String({ description: "Follow-up message for action='resume' or non-terminal guidance for action='steer'. Use index to choose a child from multi-child runs." })),
|
|
1274
|
-
scope: Type.Optional(Type.String({ enum: ["session", "user", "project"], description: "Scope for action='watchdog.configure'. Defaults to session to avoid persistent settings writes unless user/project is explicit." })),
|
|
1275
|
-
target: Type.Optional(Type.String({ enum: ["main", "children"], description: "Target for action='watchdog.configure'. Defaults to main." })),
|
|
1276
|
-
thinking: Type.Optional(Type.Unsafe({ anyOf: [{ type: "string" }, { type: "boolean", enum: [!1] }], description: "Thinking level for action='watchdog.configure' (off/minimal/low/medium/high/xhigh/max, inherit, or false for off)." })),
|
|
1277
1339
|
schedule: Type.Optional(Type.String({ description: "Explicit one-shot schedule for action='schedule'. Only honored when scheduledRuns.enabled is true. Use '+10m' or a future ISO timestamp with timezone; scheduled runs always launch async with fresh context." })),
|
|
1278
1340
|
scheduleName: Type.Optional(Type.String({ description: "Optional display name for action='schedule'." })),
|
|
1279
|
-
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
|
|
1341
|
+
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{task, description, tier|model, thinking?, permissions?, count?, output?, outputMode?, reads?, progress?}, ...]" })),
|
|
1280
1342
|
concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or unlimited." })),
|
|
1281
1343
|
worktree: Type.Optional(Type.Boolean({
|
|
1282
1344
|
description: "Create isolated git worktrees for parallel tasks; requires clean git state."
|
|
@@ -1311,15 +1373,26 @@ var SkillOverride = Type.Unsafe({
|
|
|
1311
1373
|
skill: Type.Optional(SkillOverride),
|
|
1312
1374
|
tier: Type.Optional(Type.String({
|
|
1313
1375
|
enum: ["light", "standard", "heavy"],
|
|
1314
|
-
description:
|
|
1376
|
+
description: `Preferred for SINGLE execution. Required by default. Omit for control actions. Do not combine with model; PARALLEL and CHAIN children carry their own tier or model. ${TIER_SELECTION_DESCRIPTION}`
|
|
1377
|
+
})),
|
|
1378
|
+
model: Type.Optional(Type.String({
|
|
1379
|
+
minLength: 1,
|
|
1380
|
+
description: "Explicit provider/model for SINGLE execution only when the user names a model. Do not combine with tier."
|
|
1381
|
+
})),
|
|
1382
|
+
thinking: Type.Optional(Type.String({
|
|
1383
|
+
enum: ["off", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1384
|
+
description: "Thinking level for an explicit model launch. Tier launches use configured tier thinking instead."
|
|
1315
1385
|
})),
|
|
1316
1386
|
acceptance: Type.Optional(AcceptanceOverride)
|
|
1317
1387
|
}), SubagentParams = keepTopLevelParameterDescriptions(SubagentParamsSchema), SubagentWaitParamsSchema = Type.Object({
|
|
1318
1388
|
id: Type.Optional(Type.String({
|
|
1319
1389
|
description: "Async run or remembered detached foreground run id/prefix to wait for one specific run. Omit to wait across every active async run started in this session."
|
|
1320
1390
|
})),
|
|
1391
|
+
questionId: Type.Optional(Type.String({
|
|
1392
|
+
description: "Wait for one supervisor question id from subagent_supervisor action='ask'. Returns when that question is answered, expired, or cancelled. Separate from final async run results."
|
|
1393
|
+
})),
|
|
1321
1394
|
all: Type.Optional(Type.Boolean({
|
|
1322
|
-
description: "Wait for ALL active runs to finish. Default false: return as soon as the first run finishes, so a fleet manager can spawn a replacement and wait again. Ignored when id targets
|
|
1395
|
+
description: "Wait for ALL active runs to finish. Default false: return as soon as the first run finishes, so a fleet manager can spawn a replacement and wait again. Ignored when id or questionId targets one item."
|
|
1323
1396
|
})),
|
|
1324
1397
|
timeoutMs: Type.Optional(Type.Integer({
|
|
1325
1398
|
minimum: 1,
|
|
@@ -1344,28 +1417,28 @@ function typeName(value) {
|
|
|
1344
1417
|
function quoteList(keys) {
|
|
1345
1418
|
return keys.map((key) => `"${key}"`).join(", ");
|
|
1346
1419
|
}
|
|
1347
|
-
function disallowedMessage(
|
|
1420
|
+
function disallowedMessage(path13, keys, allowed, example) {
|
|
1348
1421
|
let subject = keys.length === 1 ? `property "${keys[0]}" is not allowed` : `properties ${quoteList(keys)} are not allowed`, examplePart = example ? `
|
|
1349
1422
|
|
|
1350
1423
|
Example: ${example}` : "";
|
|
1351
|
-
return `subagent ${
|
|
1424
|
+
return `subagent ${path13}: ${subject}.
|
|
1352
1425
|
Allowed properties: ${allowed.join(", ")}.${examplePart}`;
|
|
1353
1426
|
}
|
|
1354
|
-
function expectObject(value,
|
|
1427
|
+
function expectObject(value, path13, allowed, example) {
|
|
1355
1428
|
if (!isPlainObject(value)) {
|
|
1356
1429
|
let examplePart = example ? `
|
|
1357
1430
|
|
|
1358
1431
|
Example: ${example}` : "";
|
|
1359
|
-
throw new Error(`subagent ${
|
|
1432
|
+
throw new Error(`subagent ${path13}: expected an object, received ${typeName(value)}.
|
|
1360
1433
|
Allowed properties: ${allowed.join(", ")}.${examplePart}`);
|
|
1361
1434
|
}
|
|
1362
1435
|
}
|
|
1363
|
-
function checkNoExtraKeys(value,
|
|
1436
|
+
function checkNoExtraKeys(value, path13, schema, allowed, example) {
|
|
1364
1437
|
if (schema.additionalProperties !== !1)
|
|
1365
1438
|
return;
|
|
1366
1439
|
let extra = disallowedKeys(value, allowed);
|
|
1367
1440
|
if (extra.length > 0)
|
|
1368
|
-
throw new Error(disallowedMessage(
|
|
1441
|
+
throw new Error(disallowedMessage(path13, extra, allowed, example));
|
|
1369
1442
|
}
|
|
1370
1443
|
function validateChainInput(args) {
|
|
1371
1444
|
if (!isPlainObject(args))
|
|
@@ -1618,8 +1691,7 @@ function createAsyncJobTracker(pi, state, asyncDirRoot, options = {}) {
|
|
|
1618
1691
|
}
|
|
1619
1692
|
widgetRenderKey(job) !== widgetStateBefore && (widgetChanged = !0);
|
|
1620
1693
|
}
|
|
1621
|
-
|
|
1622
|
-
(widgetChanged || timerNeedsPaint) && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
|
|
1694
|
+
widgetChanged && state.lastUiContext?.hasUI && rerenderWidget(state.lastUiContext);
|
|
1623
1695
|
}, pollIntervalMs), state.poller.unref?.());
|
|
1624
1696
|
};
|
|
1625
1697
|
return { ensurePoller, refreshWidget, handleStarted: (data) => {
|
|
@@ -1759,15 +1831,15 @@ function createFileCoalescer(handler, defaultDelayMs, timerApi = defaultTimerApi
|
|
|
1759
1831
|
|
|
1760
1832
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js
|
|
1761
1833
|
var WATCHER_RESTART_DELAY_MS = 3e3, POLL_INTERVAL_MS2 = 3e3;
|
|
1762
|
-
function sanitizeNestedResultChildren(value, resultPath, label) {
|
|
1834
|
+
function sanitizeNestedResultChildren(value, resultPath, label, report) {
|
|
1763
1835
|
if (value === void 0)
|
|
1764
1836
|
return;
|
|
1765
1837
|
if (!Array.isArray(value)) {
|
|
1766
|
-
|
|
1838
|
+
report(`Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`);
|
|
1767
1839
|
return;
|
|
1768
1840
|
}
|
|
1769
1841
|
let children = value.map((child) => sanitizeSummary(child)).filter((child) => !!child);
|
|
1770
|
-
return children.length !== value.length &&
|
|
1842
|
+
return children.length !== value.length && report(`Ignoring ${value.length - children.length} invalid nested child record(s) in subagent result file '${resultPath}' at ${label}.`), children.length ? children : void 0;
|
|
1771
1843
|
}
|
|
1772
1844
|
function getErrorCode(error) {
|
|
1773
1845
|
return typeof error == "object" && error !== null && "code" in error ? error.code : void 0;
|
|
@@ -1780,27 +1852,27 @@ function shouldFallBackToPolling(error) {
|
|
|
1780
1852
|
return code === "EMFILE" || code === "ENOSPC";
|
|
1781
1853
|
}
|
|
1782
1854
|
function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {}) {
|
|
1783
|
-
let fsApi = deps.fs ?? fs2, timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval },
|
|
1784
|
-
|
|
1785
|
-
|
|
1855
|
+
let fsApi = deps.fs ?? fs2, timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval }, generation = 0, inFlight = /* @__PURE__ */ new Set(), attempts = /* @__PURE__ */ new Map(), report = (message, error) => {
|
|
1856
|
+
pi.appendEntry?.("subagent_result_delivery", { message, error: error instanceof Error ? error.message : error });
|
|
1857
|
+
}, handleResult = async (file) => {
|
|
1858
|
+
if (inFlight.has(file) || (attempts.get(file) ?? 0) >= 3)
|
|
1859
|
+
return;
|
|
1860
|
+
let currentGeneration = generation, sessionId = state.currentSessionId, isCurrent = () => generation === currentGeneration && state.currentSessionId === sessionId, resultPath = path2.join(resultsDir, file);
|
|
1861
|
+
if (fsApi.existsSync(resultPath)) {
|
|
1862
|
+
inFlight.add(file);
|
|
1786
1863
|
try {
|
|
1787
1864
|
let data = JSON.parse(fsApi.readFileSync(resultPath, "utf-8"));
|
|
1788
1865
|
if (typeof data.sessionId != "string" || data.sessionId !== state.currentSessionId)
|
|
1789
1866
|
return;
|
|
1790
|
-
let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren"));
|
|
1867
|
+
let runId = data.runId ?? data.id ?? file.replace(/\.json$/i, ""), hasExplicitNestedChildren = data.nestedChildren !== void 0, nestedChildren = compactNestedResultChildren(sanitizeNestedResultChildren(data.nestedChildren, resultPath, "nestedChildren", report));
|
|
1791
1868
|
if (!nestedChildren?.length && !hasExplicitNestedChildren)
|
|
1792
1869
|
try {
|
|
1793
1870
|
nestedChildren = compactNestedResultChildren(projectNestedRegistryForRoot(runId)?.children);
|
|
1794
1871
|
} catch (error) {
|
|
1795
|
-
|
|
1872
|
+
report(`Failed to enrich subagent result file '${resultPath}' with nested registry children; will retry later:`, error);
|
|
1796
1873
|
return;
|
|
1797
1874
|
}
|
|
1798
|
-
let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`)
|
|
1799
|
-
if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs)) {
|
|
1800
|
-
fsApi.unlinkSync(resultPath);
|
|
1801
|
-
return;
|
|
1802
|
-
}
|
|
1803
|
-
let hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
|
|
1875
|
+
let now = Date.now(), completionKey = buildCompletionKey(data, `result:${file}`), hasResultChildren = Array.isArray(data.results) && data.results.length > 0, resultChildren = hasResultChildren ? data.results : [{
|
|
1804
1876
|
agent: data.agent,
|
|
1805
1877
|
output: data.summary,
|
|
1806
1878
|
success: data.success
|
|
@@ -1808,7 +1880,7 @@ function createResultWatcher(pi, state, resultsDir, completionTtlMs, deps = {})
|
|
|
1808
1880
|
let baseOutput = result2.output ?? data.summary, hasRealOutput = typeof baseOutput == "string" && baseOutput.trim().length > 0, output = hasRealOutput ? baseOutput : "(no output)", summary = result2.success === !1 && result2.error ? `${result2.error}${hasRealOutput ? `
|
|
1809
1881
|
|
|
1810
1882
|
Output:
|
|
1811
|
-
${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : void 0), childNestedChildren = sanitizeNestedResultChildren(result2.children, resultPath, `results[${index}].children
|
|
1883
|
+
${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChildren.length === 1 ? data.sessionFile : void 0), childNestedChildren = sanitizeNestedResultChildren(result2.children, resultPath, `results[${index}].children`, report), childState = result2.state === "paused" || result2.state === "stopped" ? result2.state : result2.stopped === !0 ? "stopped" : data.state === "paused" || !hasResultChildren && (data.state === "stopped" || typeof result2.success != "boolean") ? data.state : void 0;
|
|
1812
1884
|
return {
|
|
1813
1885
|
agent: result2.agent ?? data.agent ?? `step-${index + 1}`,
|
|
1814
1886
|
status: resolveSubagentResultStatus({
|
|
@@ -1822,7 +1894,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1822
1894
|
...result2.intercomTarget ? { intercomTarget: result2.intercomTarget } : {},
|
|
1823
1895
|
...childNestedChildren ? { children: childNestedChildren } : {}
|
|
1824
1896
|
};
|
|
1825
|
-
}), nestedChildren)
|
|
1897
|
+
}), nestedChildren);
|
|
1898
|
+
if (markSeenWithTtl(state.completionSeen, completionKey, now, completionTtlMs) || pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, {
|
|
1899
|
+
...data,
|
|
1900
|
+
runId,
|
|
1901
|
+
...nestedChildren?.length ? { nestedChildren } : {},
|
|
1902
|
+
...Array.isArray(data.results) ? { results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
1903
|
+
...data.results[index],
|
|
1904
|
+
agent: child.agent,
|
|
1905
|
+
status: child.status,
|
|
1906
|
+
summary: child.summary,
|
|
1907
|
+
index: child.index,
|
|
1908
|
+
artifactPath: child.artifactPath,
|
|
1909
|
+
sessionPath: child.sessionPath,
|
|
1910
|
+
children: child.children
|
|
1911
|
+
})) : [] } : {}
|
|
1912
|
+
}), !isCurrent())
|
|
1913
|
+
return;
|
|
1914
|
+
let intercomTarget = data.state === "stopped" ? void 0 : data.intercomTarget?.trim();
|
|
1826
1915
|
if (intercomTarget) {
|
|
1827
1916
|
let mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain" ? data.mode : resultChildren.length > 1 ? "chain" : "single", payload = buildSubagentResultIntercomPayload({
|
|
1828
1917
|
to: intercomTarget,
|
|
@@ -1833,30 +1922,24 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1833
1922
|
asyncId: data.id,
|
|
1834
1923
|
asyncDir: data.asyncDir
|
|
1835
1924
|
});
|
|
1836
|
-
|
|
1925
|
+
payload.requestId = `async-result:${sessionId}:${completionKey}`, attempts.set(file, (attempts.get(file) ?? 0) + 1);
|
|
1926
|
+
let delivered = await deliverSubagentResultIntercomEvent(pi.events, payload);
|
|
1927
|
+
if (!isCurrent())
|
|
1928
|
+
return;
|
|
1929
|
+
if (!delivered) {
|
|
1930
|
+
report(`Async result delivery pending for '${runId}'; output retained.`, resultPath), attempts.get(file) < 3 && state.resultFileCoalescer.schedule(file, 1e3);
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1837
1933
|
}
|
|
1838
|
-
|
|
1839
|
-
...data,
|
|
1840
|
-
runId,
|
|
1841
|
-
...nestedChildren?.length ? { nestedChildren } : {},
|
|
1842
|
-
...Array.isArray(data.results) ? {
|
|
1843
|
-
results: hasResultChildren ? normalizedChildren.map((child, index) => ({
|
|
1844
|
-
...data.results[index],
|
|
1845
|
-
agent: child.agent,
|
|
1846
|
-
status: child.status,
|
|
1847
|
-
summary: child.summary,
|
|
1848
|
-
index: child.index,
|
|
1849
|
-
artifactPath: child.artifactPath,
|
|
1850
|
-
sessionPath: child.sessionPath,
|
|
1851
|
-
children: child.children
|
|
1852
|
-
})) : []
|
|
1853
|
-
} : {}
|
|
1854
|
-
}), fsApi.unlinkSync(resultPath);
|
|
1934
|
+
isCurrent() && fsApi.unlinkSync(resultPath);
|
|
1855
1935
|
} catch (error) {
|
|
1856
1936
|
if (isNotFoundError(error))
|
|
1857
1937
|
return;
|
|
1858
|
-
|
|
1938
|
+
report(`Failed to process subagent result file '${resultPath}'.`, error);
|
|
1939
|
+
} finally {
|
|
1940
|
+
inFlight.delete(file);
|
|
1859
1941
|
}
|
|
1942
|
+
}
|
|
1860
1943
|
};
|
|
1861
1944
|
state.resultFileCoalescer = createFileCoalescer((file) => {
|
|
1862
1945
|
handleResult(file);
|
|
@@ -1867,10 +1950,10 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1867
1950
|
} catch (error) {
|
|
1868
1951
|
if (isNotFoundError(error))
|
|
1869
1952
|
return;
|
|
1870
|
-
|
|
1953
|
+
report(`Failed to scan subagent result directory '${resultsDir}':`, error);
|
|
1871
1954
|
}
|
|
1872
1955
|
}, startPollingFallback = (reason) => {
|
|
1873
|
-
state.watcher?.close(), state.watcher = null, !state.watcherRestartTimer && (
|
|
1956
|
+
state.watcher?.close(), state.watcher = null, !state.watcherRestartTimer && (report(`Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`), primeExistingResults(), state.watcherRestartTimer = timers.setInterval(primeExistingResults, POLL_INTERVAL_MS2), state.watcherRestartTimer.unref?.());
|
|
1874
1957
|
}, scheduleRestart = () => {
|
|
1875
1958
|
state.watcherRestartTimer || (state.watcherRestartTimer = timers.setTimeout(() => {
|
|
1876
1959
|
state.watcherRestartTimer = null;
|
|
@@ -1881,7 +1964,7 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1881
1964
|
startPollingFallback(error);
|
|
1882
1965
|
return;
|
|
1883
1966
|
}
|
|
1884
|
-
|
|
1967
|
+
report(`Failed to restart subagent result watcher for '${resultsDir}':`, error), scheduleRestart();
|
|
1885
1968
|
}
|
|
1886
1969
|
}, WATCHER_RESTART_DELAY_MS), state.watcherRestartTimer.unref?.());
|
|
1887
1970
|
}, startResultWatcher = () => {
|
|
@@ -1899,19 +1982,19 @@ ${baseOutput}` : ""}` : output, sessionPath = result2.sessionFile ?? (resultChil
|
|
|
1899
1982
|
startPollingFallback(error);
|
|
1900
1983
|
return;
|
|
1901
1984
|
}
|
|
1902
|
-
|
|
1985
|
+
report(`Subagent result watcher failed for '${resultsDir}':`, error), state.watcher?.close(), state.watcher = null, scheduleRestart();
|
|
1903
1986
|
}), state.watcher.unref?.();
|
|
1904
1987
|
} catch (error) {
|
|
1905
1988
|
if (shouldFallBackToPolling(error)) {
|
|
1906
1989
|
startPollingFallback(error);
|
|
1907
1990
|
return;
|
|
1908
1991
|
}
|
|
1909
|
-
|
|
1992
|
+
report(`Failed to start subagent result watcher for '${resultsDir}':`, error), state.watcher = null, scheduleRestart();
|
|
1910
1993
|
}
|
|
1911
1994
|
}
|
|
1912
1995
|
};
|
|
1913
1996
|
return { startResultWatcher, primeExistingResults, stopResultWatcher: () => {
|
|
1914
|
-
state.watcher?.close(), state.watcher = null, state.watcherRestartTimer && (timers.clearTimeout(state.watcherRestartTimer), timers.clearInterval(state.watcherRestartTimer)), state.watcherRestartTimer = null, state.resultFileCoalescer.clear();
|
|
1997
|
+
generation++, attempts.clear(), state.watcher?.close(), state.watcher = null, state.watcherRestartTimer && (timers.clearTimeout(state.watcherRestartTimer), timers.clearInterval(state.watcherRestartTimer)), state.watcherRestartTimer = null, state.resultFileCoalescer.clear();
|
|
1915
1998
|
} };
|
|
1916
1999
|
}
|
|
1917
2000
|
|
|
@@ -3100,6 +3183,12 @@ var parseInlineConfig = (raw) => {
|
|
|
3100
3183
|
case "tier":
|
|
3101
3184
|
(val === "light" || val === "standard" || val === "heavy") && (config.tier = val);
|
|
3102
3185
|
break;
|
|
3186
|
+
case "model":
|
|
3187
|
+
val && (config.model = val);
|
|
3188
|
+
break;
|
|
3189
|
+
case "thinking":
|
|
3190
|
+
["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(val) && (config.thinking = val);
|
|
3191
|
+
break;
|
|
3103
3192
|
case "permissions":
|
|
3104
3193
|
(val === "full" || val === "read-only") && (config.permissions = val);
|
|
3105
3194
|
break;
|
|
@@ -3649,12 +3738,16 @@ function loadInlineOutputSchema(baseCwd, agent, value) {
|
|
|
3649
3738
|
}
|
|
3650
3739
|
var mapParsedTaskToStepObject = (step, fallbackTask, isFirst, opts) => {
|
|
3651
3740
|
let { name, config, task: stepTask } = step;
|
|
3652
|
-
if (!config.tier)
|
|
3653
|
-
throw new SlashParseError(`Step '${name}' requires tier=light
|
|
3741
|
+
if (!config.tier && !config.model)
|
|
3742
|
+
throw new SlashParseError(`Step '${name}' requires tier=light|standard|heavy or model=provider/id.`);
|
|
3743
|
+
if (config.tier && config.model)
|
|
3744
|
+
throw new SlashParseError(`Step '${name}' must choose exactly one of tier or model.`);
|
|
3654
3745
|
return config.acceptance !== void 0 && validateInlineAcceptanceInput(config.acceptance, name), {
|
|
3655
3746
|
description: name,
|
|
3656
3747
|
permissions: config.permissions ?? "full",
|
|
3657
|
-
tier: config.tier,
|
|
3748
|
+
...config.tier ? { tier: config.tier } : {},
|
|
3749
|
+
...config.model ? { model: config.model } : {},
|
|
3750
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
3658
3751
|
...stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {},
|
|
3659
3752
|
...config.output !== void 0 ? { output: config.output } : {},
|
|
3660
3753
|
...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
|
|
@@ -3765,7 +3858,9 @@ ${finalTask}`);
|
|
|
3765
3858
|
let tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
|
|
3766
3859
|
description: name,
|
|
3767
3860
|
permissions: config.permissions ?? "full",
|
|
3768
|
-
tier: config.tier,
|
|
3861
|
+
...config.tier ? { tier: config.tier } : {},
|
|
3862
|
+
...config.model ? { model: config.model } : {},
|
|
3863
|
+
...config.thinking ? { thinking: config.thinking } : {},
|
|
3769
3864
|
task: stepTask ?? parsed.task,
|
|
3770
3865
|
...config.output !== void 0 ? { output: config.output } : {},
|
|
3771
3866
|
...config.outputMode !== void 0 ? { outputMode: config.outputMode } : {},
|
|
@@ -3773,8 +3868,12 @@ ${finalTask}`);
|
|
|
3773
3868
|
...config.skill !== void 0 ? { skill: config.skill } : {},
|
|
3774
3869
|
...config.progress !== void 0 ? { progress: config.progress } : {}
|
|
3775
3870
|
}));
|
|
3776
|
-
if (tasks.some((task) => !task.tier)) {
|
|
3777
|
-
ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy].", "error");
|
|
3871
|
+
if (tasks.some((task) => !task.tier && !task.model)) {
|
|
3872
|
+
ctx.ui.notify("Every parallel child requires [tier=light|standard|heavy] or [model=provider/id].", "error");
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
if (tasks.some((task) => task.tier && task.model)) {
|
|
3876
|
+
ctx.ui.notify("Each parallel child must choose exactly one of tier or model.", "error");
|
|
3778
3877
|
return;
|
|
3779
3878
|
}
|
|
3780
3879
|
let params = { tasks, clarify: !1 };
|
|
@@ -4402,28 +4501,506 @@ function registerSlashSubagentBridge(options) {
|
|
|
4402
4501
|
}
|
|
4403
4502
|
|
|
4404
4503
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
|
|
4504
|
+
import * as fs7 from "node:fs";
|
|
4505
|
+
import * as path8 from "node:path";
|
|
4506
|
+
import { Type as Type2 } from "typebox";
|
|
4507
|
+
|
|
4508
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-questions.js
|
|
4509
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
4405
4510
|
import * as fs6 from "node:fs";
|
|
4406
4511
|
import * as path7 from "node:path";
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4512
|
+
var QUESTIONS_DIR = "questions", SUPERVISOR_CHANNEL_ROOT = path7.join(TEMP_ROOT_DIR, "supervisor-channels"), SUBAGENT_SUPERVISOR_ANSWER_TYPE = "subagent_supervisor_answer", SUBAGENT_QUESTION_EVENT = "subagent:supervisor-question", MAX_MESSAGE_BYTES = 64 * 1024, DEFAULT_ASK_TIMEOUT_MS = 600 * 1e3, activeQuestionWaits = /* @__PURE__ */ new Set();
|
|
4513
|
+
function askTimeoutMs() {
|
|
4514
|
+
let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
|
|
4515
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS;
|
|
4516
|
+
}
|
|
4517
|
+
function safeSegment(value) {
|
|
4518
|
+
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
4519
|
+
}
|
|
4520
|
+
function resolveSupervisorChannelDir(runId, agent, childIndex) {
|
|
4521
|
+
return path7.join(SUPERVISOR_CHANNEL_ROOT, `${safeSegment(runId)}-${safeSegment(agent)}-${childIndex}`);
|
|
4522
|
+
}
|
|
4523
|
+
function questionsDir(channelDir) {
|
|
4524
|
+
return path7.join(channelDir, QUESTIONS_DIR);
|
|
4525
|
+
}
|
|
4526
|
+
function questionFile(channelDir, questionId) {
|
|
4527
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.json`);
|
|
4528
|
+
}
|
|
4529
|
+
function deliveredFile(channelDir, questionId) {
|
|
4530
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.delivered.json`);
|
|
4531
|
+
}
|
|
4532
|
+
function terminalFile(channelDir, questionId) {
|
|
4533
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.terminal.json`);
|
|
4534
|
+
}
|
|
4535
|
+
function notifiedFile(channelDir, questionId) {
|
|
4536
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.notified.json`);
|
|
4537
|
+
}
|
|
4538
|
+
function lateFile(channelDir, questionId) {
|
|
4539
|
+
return path7.join(questionsDir(channelDir), `${safeSegment(questionId)}.late.json`);
|
|
4540
|
+
}
|
|
4541
|
+
function writeExclusiveJson(filePath, payload) {
|
|
4542
|
+
fs6.mkdirSync(path7.dirname(filePath), { recursive: !0, mode: 448 });
|
|
4543
|
+
let temporary = `${filePath}.${randomUUID3()}.tmp`;
|
|
4544
|
+
try {
|
|
4545
|
+
return fs6.writeFileSync(temporary, JSON.stringify(payload), { encoding: "utf-8", flag: "wx", mode: 384 }), fs6.linkSync(temporary, filePath), !0;
|
|
4546
|
+
} catch (error) {
|
|
4547
|
+
if (error.code === "EEXIST")
|
|
4548
|
+
return !1;
|
|
4549
|
+
throw error;
|
|
4550
|
+
} finally {
|
|
4551
|
+
fs6.rmSync(temporary, { force: !0 });
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
function readJsonFile(filePath) {
|
|
4555
|
+
return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
|
|
4556
|
+
}
|
|
4557
|
+
function tryReadJson(filePath) {
|
|
4558
|
+
try {
|
|
4559
|
+
return readJsonFile(filePath);
|
|
4560
|
+
} catch (error) {
|
|
4561
|
+
return error.code === "ENOENT", void 0;
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
function parseIncarnation(raw) {
|
|
4565
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4566
|
+
return;
|
|
4567
|
+
let input = raw, pid = input.pid, readyAt = input.readyAt;
|
|
4568
|
+
if (!(!Number.isInteger(pid) || pid === void 0 || pid <= 0) && !(typeof readyAt != "number" || !Number.isFinite(readyAt) || readyAt <= 0))
|
|
4569
|
+
return { pid, readyAt };
|
|
4570
|
+
}
|
|
4571
|
+
function parseQuestionRecord(raw) {
|
|
4572
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4573
|
+
return;
|
|
4574
|
+
let input = raw;
|
|
4575
|
+
if (input.type !== "subagent.supervisor.question" || typeof input.id != "string" || !input.id.trim() || typeof input.createdAt != "number" || !Number.isFinite(input.createdAt) || typeof input.expiresAt != "number" || !Number.isFinite(input.expiresAt) || typeof input.reason != "string" || !input.reason.trim() || typeof input.message != "string" || !input.message.trim() || typeof input.runId != "string" || !input.runId.trim())
|
|
4576
|
+
return;
|
|
4577
|
+
let childIndex = input.childIndex;
|
|
4578
|
+
if (!Number.isInteger(childIndex) || childIndex === void 0 || childIndex < 0 || typeof input.childId != "string" || !input.childId.trim() || typeof input.parentSessionId != "string" || !input.parentSessionId.trim())
|
|
4579
|
+
return;
|
|
4580
|
+
let parentGeneration = input.parentGeneration;
|
|
4581
|
+
if (!Number.isInteger(parentGeneration) || parentGeneration === void 0 || parentGeneration < 0)
|
|
4582
|
+
return;
|
|
4583
|
+
let incarnation = parseIncarnation(input.childIncarnation);
|
|
4584
|
+
return {
|
|
4585
|
+
type: "subagent.supervisor.question",
|
|
4586
|
+
id: input.id.trim(),
|
|
4587
|
+
createdAt: input.createdAt,
|
|
4588
|
+
expiresAt: input.expiresAt,
|
|
4589
|
+
reason: input.reason.trim(),
|
|
4590
|
+
message: input.message.trim(),
|
|
4591
|
+
runId: input.runId.trim(),
|
|
4592
|
+
childIndex,
|
|
4593
|
+
childId: input.childId.trim(),
|
|
4594
|
+
parentSessionId: input.parentSessionId.trim(),
|
|
4595
|
+
parentGeneration,
|
|
4596
|
+
...incarnation ? { childIncarnation: incarnation } : {}
|
|
4597
|
+
};
|
|
4598
|
+
}
|
|
4599
|
+
function parseDeliveredRecord(raw) {
|
|
4600
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4601
|
+
return;
|
|
4602
|
+
let input = raw;
|
|
4603
|
+
if (input.type !== "subagent.supervisor.delivered" || typeof input.id != "string" || !input.id.trim() || typeof input.at != "number" || !Number.isFinite(input.at))
|
|
4604
|
+
return;
|
|
4605
|
+
let incarnation = parseIncarnation(input);
|
|
4606
|
+
if (incarnation)
|
|
4607
|
+
return { type: "subagent.supervisor.delivered", id: input.id.trim(), at: input.at, pid: incarnation.pid, readyAt: incarnation.readyAt };
|
|
4608
|
+
}
|
|
4609
|
+
function parseTerminalRecord(raw) {
|
|
4610
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4611
|
+
return;
|
|
4612
|
+
let input = raw;
|
|
4613
|
+
if (!(input.type !== "subagent.supervisor.terminal" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at))) {
|
|
4614
|
+
if (input.state === "answered")
|
|
4615
|
+
return typeof input.message != "string" || !input.message.trim() ? void 0 : { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "answered", at: input.at, message: input.message };
|
|
4616
|
+
if (input.state === "expired")
|
|
4617
|
+
return { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "expired", at: input.at };
|
|
4618
|
+
if (input.state === "cancelled")
|
|
4619
|
+
return typeof input.reason != "string" || !input.reason.trim() ? void 0 : { type: "subagent.supervisor.terminal", id: input.id.trim(), state: "cancelled", at: input.at, reason: input.reason.trim() };
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
function parseNotifiedRecord(raw) {
|
|
4623
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4624
|
+
return;
|
|
4625
|
+
let input = raw;
|
|
4626
|
+
if (!(input.type !== "subagent.supervisor.notified" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at)))
|
|
4627
|
+
return { type: "subagent.supervisor.notified", id: input.id.trim(), at: input.at };
|
|
4628
|
+
}
|
|
4629
|
+
function parseLateRecord(raw) {
|
|
4630
|
+
if (!raw || typeof raw != "object" || Array.isArray(raw))
|
|
4631
|
+
return;
|
|
4632
|
+
let input = raw;
|
|
4633
|
+
if (!(input.type !== "subagent.supervisor.late-answer" || typeof input.id != "string" || !input.id.trim()) && !(typeof input.at != "number" || !Number.isFinite(input.at) || typeof input.message != "string"))
|
|
4634
|
+
return { type: "subagent.supervisor.late-answer", id: input.id.trim(), at: input.at, message: input.message };
|
|
4635
|
+
}
|
|
4636
|
+
function composeQuestion(record, channelDir) {
|
|
4637
|
+
let delivered = parseDeliveredRecord(tryReadJson(deliveredFile(channelDir, record.id))), terminal = parseTerminalRecord(tryReadJson(terminalFile(channelDir, record.id))), notified = parseNotifiedRecord(tryReadJson(notifiedFile(channelDir, record.id))), late = parseLateRecord(tryReadJson(lateFile(channelDir, record.id)));
|
|
4638
|
+
return {
|
|
4639
|
+
...record,
|
|
4640
|
+
...delivered ? { deliveredAt: delivered.at, childIncarnation: record.childIncarnation ?? { pid: delivered.pid, readyAt: delivered.readyAt } } : {},
|
|
4641
|
+
...terminal?.state === "answered" ? { answeredAt: terminal.at, answer: terminal.message } : {},
|
|
4642
|
+
...terminal?.state === "expired" ? { expiredAt: terminal.at } : {},
|
|
4643
|
+
...terminal?.state === "cancelled" ? { cancelledAt: terminal.at, cancelReason: terminal.reason } : {},
|
|
4644
|
+
...notified ? { notifiedAt: notified.at } : {},
|
|
4645
|
+
...late ? { lateAnswerAt: late.at, ...terminal?.state === "answered" ? {} : { answer: late.message } } : {}
|
|
4646
|
+
};
|
|
4647
|
+
}
|
|
4648
|
+
function assertMessageSize(value, label) {
|
|
4649
|
+
if (Buffer.byteLength(value, "utf-8") > MAX_MESSAGE_BYTES)
|
|
4650
|
+
throw new Error(`${label} is too large.`);
|
|
4651
|
+
}
|
|
4652
|
+
function ensureQuestionsDir(channelDir) {
|
|
4653
|
+
fs6.mkdirSync(questionsDir(channelDir), { recursive: !0, mode: 448 });
|
|
4654
|
+
}
|
|
4655
|
+
function supervisorQuestionPublicState(question) {
|
|
4656
|
+
return question.answeredAt ? "answered" : question.expiredAt ? "expired" : question.cancelledAt ? "cancelled" : "pending";
|
|
4657
|
+
}
|
|
4658
|
+
function supervisorQuestionIsTerminal(question) {
|
|
4659
|
+
return supervisorQuestionPublicState(question) !== "pending";
|
|
4660
|
+
}
|
|
4661
|
+
function readSupervisorQuestion(channelDir, questionId) {
|
|
4662
|
+
let record = parseQuestionRecord(tryReadJson(questionFile(channelDir, questionId)));
|
|
4663
|
+
if (!(!record || record.id !== questionId))
|
|
4664
|
+
return composeQuestion(record, channelDir);
|
|
4665
|
+
}
|
|
4666
|
+
function listSupervisorQuestions(channelDir) {
|
|
4667
|
+
let dir = questionsDir(channelDir), entries;
|
|
4668
|
+
try {
|
|
4669
|
+
entries = fs6.readdirSync(dir).filter((name) => name.endsWith(".json") && !name.endsWith(".delivered.json") && !name.endsWith(".terminal.json") && !name.endsWith(".notified.json") && !name.endsWith(".late.json"));
|
|
4670
|
+
} catch (error) {
|
|
4671
|
+
if (error.code === "ENOENT")
|
|
4672
|
+
return [];
|
|
4673
|
+
throw error;
|
|
4674
|
+
}
|
|
4675
|
+
let questions = [];
|
|
4676
|
+
for (let entry of entries.sort()) {
|
|
4677
|
+
let record = parseQuestionRecord(tryReadJson(path7.join(dir, entry)));
|
|
4678
|
+
record && questions.push(composeQuestion(record, channelDir));
|
|
4679
|
+
}
|
|
4680
|
+
return questions;
|
|
4681
|
+
}
|
|
4682
|
+
function writeTerminal(channelDir, record) {
|
|
4683
|
+
return writeExclusiveJson(terminalFile(channelDir, record.id), record);
|
|
4684
|
+
}
|
|
4685
|
+
function createSupervisorQuestion(input) {
|
|
4686
|
+
let reason = input.reason.trim(), message = input.message.trim();
|
|
4687
|
+
if (!reason)
|
|
4688
|
+
throw new Error("reason is required and must name the concrete decision the child's answer will change.");
|
|
4689
|
+
if (!message)
|
|
4690
|
+
throw new Error("message is required for supervisor questions.");
|
|
4691
|
+
assertMessageSize(reason, "reason"), assertMessageSize(message, "message");
|
|
4692
|
+
let createdAt = input.now ?? Date.now(), timeoutMs = input.timeoutMs && input.timeoutMs > 0 ? input.timeoutMs : askTimeoutMs(), record = {
|
|
4693
|
+
type: "subagent.supervisor.question",
|
|
4694
|
+
id: input.id ?? randomUUID3(),
|
|
4695
|
+
createdAt,
|
|
4696
|
+
expiresAt: createdAt + timeoutMs,
|
|
4697
|
+
reason,
|
|
4698
|
+
message,
|
|
4699
|
+
runId: input.runId,
|
|
4700
|
+
childIndex: input.childIndex,
|
|
4701
|
+
childId: input.childId,
|
|
4702
|
+
parentSessionId: input.parentSessionId,
|
|
4703
|
+
parentGeneration: input.parentGeneration,
|
|
4704
|
+
...input.childIncarnation ? { childIncarnation: input.childIncarnation } : {}
|
|
4705
|
+
};
|
|
4706
|
+
if (assertMessageSize(JSON.stringify(record), "Supervisor question"), ensureQuestionsDir(input.channelDir), !writeExclusiveJson(questionFile(input.channelDir, record.id), record))
|
|
4707
|
+
throw new Error(`Supervisor question '${record.id}' already exists.`);
|
|
4708
|
+
return composeQuestion(record, input.channelDir);
|
|
4709
|
+
}
|
|
4710
|
+
function outstandingQuestionsForChild(channelDir, childIndex, childId) {
|
|
4711
|
+
return refreshSupervisorQuestionLifecycle(channelDir), listSupervisorQuestions(channelDir).filter((question) => !(supervisorQuestionIsTerminal(question) || question.childIndex !== childIndex || childId && question.childId !== childId));
|
|
4712
|
+
}
|
|
4713
|
+
function hasBlockingSupervisorRequest(channelDir, runId, childIndex) {
|
|
4714
|
+
let directory = path7.join(channelDir, "requests");
|
|
4715
|
+
if (!fs6.existsSync(directory))
|
|
4716
|
+
return !1;
|
|
4717
|
+
for (let name of fs6.readdirSync(directory)) {
|
|
4718
|
+
if (!name.endsWith(".json"))
|
|
4719
|
+
continue;
|
|
4720
|
+
let raw = tryReadJson(path7.join(directory, name));
|
|
4721
|
+
if (!raw || typeof raw != "object")
|
|
4722
|
+
continue;
|
|
4723
|
+
let request = raw;
|
|
4724
|
+
if (!(request.type !== "subagent.supervisor.request" || request.expectsReply !== !0) && !(request.runId !== runId || request.childIndex !== childIndex || typeof request.id != "string") && !(typeof request.expiresAt == "number" && request.expiresAt <= Date.now()) && !fs6.existsSync(path7.join(channelDir, "replies", `${safeSegment(request.id)}.json`)))
|
|
4725
|
+
return !0;
|
|
4726
|
+
}
|
|
4727
|
+
return !1;
|
|
4728
|
+
}
|
|
4729
|
+
function hasOutstandingQuestionForChild(channelDir, childIndex, childId) {
|
|
4730
|
+
return outstandingQuestionsForChild(channelDir, childIndex, childId).length > 0;
|
|
4731
|
+
}
|
|
4732
|
+
function incarnationMatches(expected, actual) {
|
|
4733
|
+
return expected ? expected.pid === actual.pid && expected.readyAt === actual.readyAt : !0;
|
|
4734
|
+
}
|
|
4735
|
+
function expireSupervisorQuestion(channelDir, questionId, now = Date.now()) {
|
|
4736
|
+
let current = readSupervisorQuestion(channelDir, questionId);
|
|
4737
|
+
return !current || supervisorQuestionIsTerminal(current) || now <= current.expiresAt ? current && supervisorQuestionIsTerminal(current) ? current : void 0 : (writeTerminal(channelDir, { type: "subagent.supervisor.terminal", id: questionId, state: "expired", at: now }), readSupervisorQuestion(channelDir, questionId));
|
|
4738
|
+
}
|
|
4739
|
+
function cancelSupervisorQuestion(channelDir, questionId, reason, now = Date.now()) {
|
|
4740
|
+
let current = readSupervisorQuestion(channelDir, questionId);
|
|
4741
|
+
return !current || supervisorQuestionIsTerminal(current) ? current : (writeTerminal(channelDir, { type: "subagent.supervisor.terminal", id: questionId, state: "cancelled", at: now, reason: reason.trim() || "cancelled" }), readSupervisorQuestion(channelDir, questionId));
|
|
4742
|
+
}
|
|
4743
|
+
function refreshSupervisorQuestionLifecycle(channelDir, now = Date.now()) {
|
|
4744
|
+
let changed = [];
|
|
4745
|
+
for (let question of listSupervisorQuestions(channelDir))
|
|
4746
|
+
if (!supervisorQuestionIsTerminal(question) && now > question.expiresAt) {
|
|
4747
|
+
let expired = expireSupervisorQuestion(channelDir, question.id, now);
|
|
4748
|
+
expired && expired.expiredAt && changed.push(expired);
|
|
4749
|
+
}
|
|
4750
|
+
return changed;
|
|
4751
|
+
}
|
|
4752
|
+
function formatParentQuestionForChild(question) {
|
|
4753
|
+
return [
|
|
4754
|
+
`Parent question (${question.id})`,
|
|
4755
|
+
`Decision needed: ${question.reason}`,
|
|
4756
|
+
"",
|
|
4757
|
+
question.message,
|
|
4758
|
+
"",
|
|
4759
|
+
`Reply with: contact_supervisor({ action: "reply", replyTo: "${question.id}", message: "..." })`,
|
|
4760
|
+
"Answer from current findings and uncertainty. Do not silently start extra investigation. Continue the assigned task after replying."
|
|
4761
|
+
].join(`
|
|
4762
|
+
`);
|
|
4763
|
+
}
|
|
4764
|
+
function formatSupervisorQuestionAnswer(question) {
|
|
4765
|
+
let state = supervisorQuestionPublicState(question), lines = [
|
|
4766
|
+
state === "answered" ? `Subagent answer (${question.id})` : `Subagent question ${state} (${question.id})`,
|
|
4767
|
+
`Run: ${question.runId}`,
|
|
4768
|
+
`Child: ${question.childId} [#${question.childIndex}]`,
|
|
4769
|
+
`Decision: ${question.reason}`
|
|
4770
|
+
];
|
|
4771
|
+
return state === "answered" && question.answer?.trim() ? lines.push("", question.answer.trim()) : question.cancelReason && lines.push("", question.cancelReason), lines.join(`
|
|
4772
|
+
`);
|
|
4773
|
+
}
|
|
4774
|
+
function registerQuestionWait(questionIds) {
|
|
4775
|
+
let ids = [...questionIds];
|
|
4776
|
+
for (let id of ids)
|
|
4777
|
+
activeQuestionWaits.add(id);
|
|
4778
|
+
return () => {
|
|
4779
|
+
for (let id of ids)
|
|
4780
|
+
activeQuestionWaits.delete(id);
|
|
4781
|
+
};
|
|
4782
|
+
}
|
|
4783
|
+
function isQuestionWaitActive(questionId) {
|
|
4784
|
+
return activeQuestionWaits.has(questionId);
|
|
4785
|
+
}
|
|
4786
|
+
function claimQuestionNotification(channelDir, questionId, now = Date.now()) {
|
|
4787
|
+
return writeExclusiveJson(notifiedFile(channelDir, questionId), {
|
|
4788
|
+
type: "subagent.supervisor.notified",
|
|
4789
|
+
id: questionId,
|
|
4790
|
+
at: now
|
|
4791
|
+
});
|
|
4792
|
+
}
|
|
4793
|
+
function listSupervisorChannelDirs() {
|
|
4794
|
+
let entries;
|
|
4795
|
+
try {
|
|
4796
|
+
entries = fs6.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4797
|
+
} catch (error) {
|
|
4798
|
+
if (error.code === "ENOENT")
|
|
4799
|
+
return [];
|
|
4800
|
+
throw error;
|
|
4801
|
+
}
|
|
4802
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => path7.join(SUPERVISOR_CHANNEL_ROOT, entry.name));
|
|
4803
|
+
}
|
|
4804
|
+
function findSupervisorQuestion(questionId) {
|
|
4805
|
+
let id = questionId.trim();
|
|
4806
|
+
if (id)
|
|
4807
|
+
for (let channelDir of listSupervisorChannelDirs()) {
|
|
4808
|
+
let question = readSupervisorQuestion(channelDir, id);
|
|
4809
|
+
if (question)
|
|
4810
|
+
return { channelDir, question };
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
function cancelPendingSupervisorQuestionsForOwner(input) {
|
|
4814
|
+
if (!input.parentSessionId?.trim())
|
|
4815
|
+
return [];
|
|
4816
|
+
let now = input.now ?? Date.now(), cancelled = [];
|
|
4817
|
+
for (let channelDir of listSupervisorChannelDirs())
|
|
4818
|
+
for (let question of listSupervisorQuestions(channelDir)) {
|
|
4819
|
+
if (supervisorQuestionIsTerminal(question) || question.parentSessionId !== input.parentSessionId || input.parentGeneration !== void 0 && question.parentGeneration !== input.parentGeneration || input.runId && question.runId !== input.runId)
|
|
4820
|
+
continue;
|
|
4821
|
+
let next = cancelSupervisorQuestion(channelDir, question.id, input.reason, now);
|
|
4822
|
+
next?.cancelledAt && cancelled.push(next);
|
|
4823
|
+
}
|
|
4824
|
+
return cancelled;
|
|
4825
|
+
}
|
|
4826
|
+
|
|
4827
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/supervisor-ask.js
|
|
4828
|
+
function runningSteps(status) {
|
|
4829
|
+
return (status.steps ?? []).map((step, index) => ({
|
|
4830
|
+
index,
|
|
4831
|
+
childId: step.childId ?? step.agent,
|
|
4832
|
+
agent: step.agent,
|
|
4833
|
+
status: step.status
|
|
4834
|
+
})).filter((step) => step.status === "running").map((step) => ({ index: step.index, childId: step.childId, agent: step.agent }));
|
|
4835
|
+
}
|
|
4836
|
+
function resolveAskChild(status, params) {
|
|
4837
|
+
if (params.index !== void 0 && (!Number.isInteger(params.index) || params.index < 0))
|
|
4838
|
+
throw new Error("index must be a non-negative integer.");
|
|
4839
|
+
let running = runningSteps(status);
|
|
4840
|
+
if (params.childId?.trim()) {
|
|
4841
|
+
let childId = params.childId.trim(), matches = (status.steps ?? []).map((step, index) => ({ index, childId: step.childId ?? step.agent, agent: step.agent, status: step.status })).filter((step) => step.childId === childId);
|
|
4842
|
+
if (matches.length === 0)
|
|
4843
|
+
throw new Error(`Async run '${status.runId}' has no child '${childId}'.`);
|
|
4844
|
+
if (matches.length > 1)
|
|
4845
|
+
throw new Error(`Async run '${status.runId}' has multiple children named '${childId}'. Pass index.`);
|
|
4846
|
+
let match = matches[0];
|
|
4847
|
+
if (params.index !== void 0 && params.index !== match.index)
|
|
4848
|
+
throw new Error(`Child '${childId}' is index ${match.index}, not ${params.index}.`);
|
|
4849
|
+
if (match.status !== "running")
|
|
4850
|
+
throw new Error(`Async run '${status.runId}' child ${match.index} is ${match.status} and cannot be asked.`);
|
|
4851
|
+
return { index: match.index, childId: match.childId, agent: match.agent };
|
|
4852
|
+
}
|
|
4853
|
+
if (params.index !== void 0) {
|
|
4854
|
+
let steps = status.steps ?? [];
|
|
4855
|
+
if (params.index >= steps.length)
|
|
4856
|
+
throw new Error(`Async run '${status.runId}' has ${steps.length} children. Index ${params.index} is out of range.`);
|
|
4857
|
+
let step = steps[params.index];
|
|
4858
|
+
if (step.status !== "running")
|
|
4859
|
+
throw new Error(`Async run '${status.runId}' child ${params.index} is ${step.status} and cannot be asked.`);
|
|
4860
|
+
return { index: params.index, childId: step.childId ?? step.agent, agent: step.agent };
|
|
4861
|
+
}
|
|
4862
|
+
if (running.length === 1)
|
|
4863
|
+
return running[0];
|
|
4864
|
+
throw running.length === 0 ? new Error(`Async run '${status.runId}' has no running child to ask.`) : new Error(`Async run '${status.runId}' has ${running.length} running children. Pass index or childId; broadcast asks are not supported.`);
|
|
4865
|
+
}
|
|
4866
|
+
function requestMatchesChild(request, runId, child) {
|
|
4867
|
+
if (!request.expectsReply || typeof request.runId != "string" || request.runId !== runId)
|
|
4868
|
+
return !1;
|
|
4869
|
+
let indexMatches = Number.isInteger(request.childIndex) && request.childIndex === child.index, idMatches = request.agent === child.childId || request.agent === child.agent || request.childTarget === child.childId;
|
|
4870
|
+
return indexMatches && idMatches;
|
|
4871
|
+
}
|
|
4872
|
+
function reconcileAsyncQuestion(channelDir, question, state) {
|
|
4873
|
+
expireSupervisorQuestion(channelDir, question.id);
|
|
4874
|
+
let current = readSupervisorQuestion(channelDir, question.id) ?? question;
|
|
4875
|
+
if (supervisorQuestionIsTerminal(current))
|
|
4876
|
+
return current;
|
|
4877
|
+
let resolved = resolveSubagentRunId(current.runId, { state });
|
|
4878
|
+
if (resolved?.kind !== "async" || !resolved.location.asyncDir)
|
|
4879
|
+
return current;
|
|
4880
|
+
let status = readStatus(resolved.location.asyncDir);
|
|
4881
|
+
if (!status)
|
|
4882
|
+
return current;
|
|
4883
|
+
let step = status.steps?.[current.childIndex], capability = readSteerCapability(resolved.location.asyncDir, current.childIndex), reason;
|
|
4884
|
+
status.sessionId !== current.parentSessionId ? reason = "async run ownership changed" : status.state !== "running" || !step || step.status !== "running" ? reason = "target child is no longer running" : (step.childId ?? step.agent) !== current.childId ? reason = "target child identity changed" : capability && !incarnationMatches(current.childIncarnation, capability) ? reason = "target child process changed" : capability && !capability.supported && (reason = "target child no longer accepts questions");
|
|
4885
|
+
let delivery = status.steering?.recent.find((request) => request.id === current.id)?.targets.find((target) => target.index === current.childIndex);
|
|
4886
|
+
return (delivery?.state === "failed" || delivery?.state === "late") && (reason = delivery.reason ?? "question delivery failed"), reason ? cancelSupervisorQuestion(channelDir, current.id, reason) ?? current : current;
|
|
4887
|
+
}
|
|
4888
|
+
function askRunningAsyncChild(input) {
|
|
4889
|
+
let runId = input.params.id?.trim();
|
|
4890
|
+
if (!runId)
|
|
4891
|
+
throw new Error("action='ask' requires id for the async run.");
|
|
4892
|
+
let reason = input.params.reason?.trim(), message = input.params.message?.trim();
|
|
4893
|
+
if (!reason)
|
|
4894
|
+
throw new Error("action='ask' requires reason naming the concrete decision the child's answer will change.");
|
|
4895
|
+
if (!message)
|
|
4896
|
+
throw new Error("action='ask' requires message.");
|
|
4897
|
+
let sessionId = input.state.currentSessionId;
|
|
4898
|
+
if (!sessionId)
|
|
4899
|
+
throw new Error("action='ask' requires an active session identity.");
|
|
4900
|
+
let resolved = resolveSubagentRunId(runId, { state: input.state });
|
|
4901
|
+
if (!resolved)
|
|
4902
|
+
throw new Error(`No async run matched '${runId}'.`);
|
|
4903
|
+
if (resolved.kind !== "async")
|
|
4904
|
+
throw new Error("action='ask' targets a running async child only. It does not revive, reassign, or broadcast.");
|
|
4905
|
+
if (!resolved.location.asyncDir)
|
|
4906
|
+
throw new Error(`Async run '${resolved.id}' has no live run directory.`);
|
|
4907
|
+
let status = readStatus(resolved.location.asyncDir);
|
|
4908
|
+
if (!status)
|
|
4909
|
+
throw new Error(`Async run '${resolved.id}' has no status.`);
|
|
4910
|
+
if (!status.sessionId || status.sessionId !== sessionId)
|
|
4911
|
+
throw new Error(`Async run '${resolved.id}' was not found in the active session.`);
|
|
4912
|
+
if (status.state !== "running")
|
|
4913
|
+
throw new Error(`Async run '${resolved.id}' is ${status.state} and cannot be asked.`);
|
|
4914
|
+
let child = resolveAskChild(status, input.params), channelDir = resolveSupervisorChannelDir(status.runId, child.childId, child.index);
|
|
4915
|
+
if (hasOutstandingQuestionForChild(channelDir, child.index, child.childId))
|
|
4916
|
+
throw new Error(`A question is already outstanding for async run '${status.runId}' child ${child.index}. Wait for that answer or expiry before asking again.`);
|
|
4917
|
+
for (let request of input.childRequests ?? [])
|
|
4918
|
+
if (requestMatchesChild(request, status.runId, child))
|
|
4919
|
+
throw new Error(`Child ${child.index} is waiting for a supervisor reply. Asking now would deadlock. Reply to the child request first.`);
|
|
4920
|
+
if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
|
|
4921
|
+
throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
|
|
4922
|
+
let capability = readSteerCapability(resolved.location.asyncDir, child.index);
|
|
4923
|
+
if (!capability?.supported)
|
|
4924
|
+
throw new Error("This child is not ready to receive questions. Continue other work until it is ready; no question was queued.");
|
|
4925
|
+
let incarnation = { pid: capability.pid, readyAt: capability.readyAt }, question = createSupervisorQuestion({
|
|
4926
|
+
channelDir,
|
|
4927
|
+
reason,
|
|
4928
|
+
message,
|
|
4929
|
+
runId: status.runId,
|
|
4930
|
+
childIndex: child.index,
|
|
4931
|
+
childId: child.childId,
|
|
4932
|
+
parentSessionId: sessionId,
|
|
4933
|
+
parentGeneration: input.state.sessionGeneration ?? 0,
|
|
4934
|
+
childIncarnation: incarnation
|
|
4935
|
+
});
|
|
4936
|
+
try {
|
|
4937
|
+
if (hasBlockingSupervisorRequest(channelDir, status.runId, child.index))
|
|
4938
|
+
throw new Error("Child is waiting for a supervisor reply. Reply to its request before asking a question.");
|
|
4939
|
+
requestAsyncSteer(resolved.location.asyncDir, {
|
|
4940
|
+
message: formatParentQuestionForChild(question),
|
|
4941
|
+
targetIndex: child.index,
|
|
4942
|
+
source: "supervisor-question",
|
|
4943
|
+
id: question.id
|
|
4944
|
+
});
|
|
4945
|
+
} catch (error) {
|
|
4946
|
+
throw cancelSupervisorQuestion(channelDir, question.id, "failed to queue delivery to child"), error instanceof Error ? error : new Error(String(error));
|
|
4947
|
+
}
|
|
4948
|
+
return {
|
|
4949
|
+
content: [{ type: "text", text: `Question ${question.id} queued for async run ${status.runId} child ${child.index}. The child's answer is delivered separately from the run result.` }],
|
|
4950
|
+
details: {
|
|
4951
|
+
questionId: question.id,
|
|
4952
|
+
state: "pending",
|
|
4953
|
+
runId: status.runId,
|
|
4954
|
+
childIndex: child.index,
|
|
4955
|
+
childId: child.childId,
|
|
4956
|
+
delivered: !1,
|
|
4957
|
+
answered: !1
|
|
4958
|
+
}
|
|
4959
|
+
};
|
|
4960
|
+
}
|
|
4961
|
+
|
|
4962
|
+
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js
|
|
4963
|
+
var REQUESTS_DIR = "requests", REPLIES_DIR = "replies", NATIVE_SUPERVISOR_TOOL_NAME = "subagent_supervisor";
|
|
4964
|
+
var MAX_MESSAGE_BYTES2 = 64 * 1024, DEFAULT_ASK_TIMEOUT_MS2 = 600 * 1e3, CHANNEL_POLL_MS = Math.min(POLL_INTERVAL_MS, 500), STALE_EMPTY_CHANNEL_AGE_MS = 60 * 1e3, STALE_EMPTY_CHANNEL_CLEANUP_INTERVAL_MS = 60 * 1e3, PARENT_ASK_DESCRIPTION = [
|
|
4965
|
+
"Ask a specific running async child, or reply to a child supervisor request.",
|
|
4966
|
+
"Ask only when the child owns missing context, the answer changes a concrete next decision, and waiting risks a block or rework.",
|
|
4967
|
+
"Use available results first, batch related questions, and keep working while awaiting the answer. Do not use ask for status checks, polling, duplicate queries, or step-by-step supervision. Follow up only when the answer leaves the original decision unresolved.",
|
|
4968
|
+
"Returns a question id immediately. The child's explicit answer is delivered separately from the final run result.",
|
|
4969
|
+
'Ask: { action: "ask", id: "<runId>", index?: 0, childId?: "...", reason: "<concrete decision>", message: "..." }',
|
|
4970
|
+
"Use pending or list to find child requests awaiting your reply; status reports channel availability. Free-form send is unsupported. Asking never revives a child or changes its assignment.",
|
|
4971
|
+
'Reply: { action: "reply", replyTo: "<requestId>", message: "..." }'
|
|
4972
|
+
].join(" "), CHILD_CONTACT_DESCRIPTION = [
|
|
4973
|
+
"Contact the parent/supervisor session for a blocking decision, structured interview, progress update, or reply to a parent question.",
|
|
4974
|
+
"For parent questions use action='reply' with replyTo set to the question id. Answer from current findings and uncertainty; do not start extra investigation just to reply. Continue the assigned task after replying.",
|
|
4975
|
+
"Do not open a blocking need_decision while a parent question is already outstanding for this child."
|
|
4976
|
+
].join(" "), ContactSupervisorParamsSchema = Type2.Object({
|
|
4977
|
+
reason: Type2.Optional(Type2.String({ enum: ["need_decision", "interview_request", "progress_update"] })),
|
|
4410
4978
|
message: Type2.Optional(Type2.String()),
|
|
4411
|
-
interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 }))
|
|
4979
|
+
interview: Type2.Optional(Type2.Unsafe({ type: "object", additionalProperties: !0 })),
|
|
4980
|
+
action: Type2.Optional(Type2.String({ enum: ["reply"] })),
|
|
4981
|
+
replyTo: Type2.Optional(Type2.String())
|
|
4412
4982
|
}, { additionalProperties: !1 }), IntercomParamsSchema = Type2.Object({
|
|
4413
4983
|
action: Type2.String({ enum: ["list", "send", "ask", "reply", "pending", "status"] }),
|
|
4414
4984
|
to: Type2.Optional(Type2.String()),
|
|
4415
4985
|
message: Type2.Optional(Type2.String()),
|
|
4416
|
-
replyTo: Type2.Optional(Type2.String())
|
|
4986
|
+
replyTo: Type2.Optional(Type2.String()),
|
|
4987
|
+
id: Type2.Optional(Type2.String({ description: "Async run id for action='ask'." })),
|
|
4988
|
+
index: Type2.Optional(Type2.Integer({ minimum: 0, description: "Zero-based child index for action='ask'. Select index or childId when more than one child is running." })),
|
|
4989
|
+
childId: Type2.Optional(Type2.String({ description: "Private child identity for action='ask'." })),
|
|
4990
|
+
reason: Type2.Optional(Type2.String({ description: "Concrete decision the child's answer will change. Required for action='ask'." }))
|
|
4991
|
+
}, { additionalProperties: !1 }), ParentSupervisorParamsSchema = Type2.Object({
|
|
4992
|
+
...IntercomParamsSchema.properties,
|
|
4993
|
+
action: Type2.String({ enum: ["list", "ask", "reply", "pending", "status"] })
|
|
4417
4994
|
}, { additionalProperties: !1 });
|
|
4418
|
-
function
|
|
4995
|
+
function safeSegment2(value) {
|
|
4419
4996
|
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
4420
4997
|
}
|
|
4421
4998
|
function replyPath(channelDir, requestId) {
|
|
4422
|
-
return
|
|
4999
|
+
return path8.join(channelDir, REPLIES_DIR, `${safeSegment2(requestId)}.json`);
|
|
4423
5000
|
}
|
|
4424
|
-
function
|
|
5001
|
+
function askTimeoutMs2() {
|
|
4425
5002
|
let parsed = Number(process.env.PI_INTERCOM_ASK_TIMEOUT_MS);
|
|
4426
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed :
|
|
5003
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ASK_TIMEOUT_MS2;
|
|
4427
5004
|
}
|
|
4428
5005
|
function hasTool(pi, name) {
|
|
4429
5006
|
try {
|
|
@@ -4434,16 +5011,26 @@ function hasTool(pi, name) {
|
|
|
4434
5011
|
}
|
|
4435
5012
|
function parseRequestFile(file, channelDir) {
|
|
4436
5013
|
try {
|
|
4437
|
-
let parsed = JSON.parse(
|
|
5014
|
+
let parsed = JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
4438
5015
|
return parsed.type !== "subagent.supervisor.request" || typeof parsed.id != "string" || !parsed.id || parsed.reason !== "need_decision" && parsed.reason !== "interview_request" && parsed.reason !== "progress_update" || typeof parsed.message != "string" || !parsed.message || typeof parsed.runId != "string" || typeof parsed.agent != "string" || typeof parsed.childIndex != "number" ? void 0 : { ...parsed, channelDir, requestFile: file };
|
|
4439
5016
|
} catch {
|
|
4440
5017
|
return;
|
|
4441
5018
|
}
|
|
4442
5019
|
}
|
|
5020
|
+
function hasPendingBlockingSupervisorRequest(runId, sessionId, now = Date.now()) {
|
|
5021
|
+
if (!runId)
|
|
5022
|
+
return !1;
|
|
5023
|
+
for (let { channelDir, file } of listRequestFiles()) {
|
|
5024
|
+
let request = parseRequestFile(file, channelDir);
|
|
5025
|
+
if (request?.expectsReply && request.runId === runId && !(sessionId && request.orchestratorSessionId && request.orchestratorSessionId !== sessionId) && !fs7.existsSync(replyPath(request.channelDir, request.id)) && !(now > requestExpiresAt(request, now)))
|
|
5026
|
+
return !0;
|
|
5027
|
+
}
|
|
5028
|
+
return !1;
|
|
5029
|
+
}
|
|
4443
5030
|
function listRequestFiles() {
|
|
4444
5031
|
let channelEntries;
|
|
4445
5032
|
try {
|
|
4446
|
-
channelEntries =
|
|
5033
|
+
channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4447
5034
|
} catch (error) {
|
|
4448
5035
|
if (error.code === "ENOENT")
|
|
4449
5036
|
return [];
|
|
@@ -4453,34 +5040,34 @@ function listRequestFiles() {
|
|
|
4453
5040
|
for (let entry of channelEntries) {
|
|
4454
5041
|
if (!entry.isDirectory())
|
|
4455
5042
|
continue;
|
|
4456
|
-
let channelDir =
|
|
5043
|
+
let channelDir = path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), requestsDir = path8.join(channelDir, REQUESTS_DIR), requestEntries;
|
|
4457
5044
|
try {
|
|
4458
|
-
requestEntries =
|
|
5045
|
+
requestEntries = fs7.readdirSync(requestsDir, { withFileTypes: !0 });
|
|
4459
5046
|
} catch {
|
|
4460
5047
|
continue;
|
|
4461
5048
|
}
|
|
4462
5049
|
for (let requestEntry of requestEntries)
|
|
4463
|
-
requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file:
|
|
5050
|
+
requestEntry.isFile() && requestEntry.name.endsWith(".json") && files.push({ channelDir, file: path8.join(requestsDir, requestEntry.name) });
|
|
4464
5051
|
}
|
|
4465
5052
|
return files;
|
|
4466
5053
|
}
|
|
4467
5054
|
function readDirectoryEntries(dir) {
|
|
4468
5055
|
try {
|
|
4469
|
-
return
|
|
5056
|
+
return fs7.readdirSync(dir, { withFileTypes: !0 });
|
|
4470
5057
|
} catch (error) {
|
|
4471
5058
|
return error.code === "ENOENT" ? [] : void 0;
|
|
4472
5059
|
}
|
|
4473
5060
|
}
|
|
4474
5061
|
function directoryMtimeMs(dir) {
|
|
4475
5062
|
try {
|
|
4476
|
-
return
|
|
5063
|
+
return fs7.statSync(dir).mtimeMs;
|
|
4477
5064
|
} catch {
|
|
4478
5065
|
return 0;
|
|
4479
5066
|
}
|
|
4480
5067
|
}
|
|
4481
5068
|
function removeEmptyDirectory(dir) {
|
|
4482
5069
|
try {
|
|
4483
|
-
return
|
|
5070
|
+
return fs7.rmdirSync(dir), !0;
|
|
4484
5071
|
} catch (error) {
|
|
4485
5072
|
let code = error.code;
|
|
4486
5073
|
if (code === "ENOENT")
|
|
@@ -4491,19 +5078,22 @@ function removeEmptyDirectory(dir) {
|
|
|
4491
5078
|
}
|
|
4492
5079
|
}
|
|
4493
5080
|
function removeStaleEmptySupervisorChannel(channelDir, nowMs) {
|
|
4494
|
-
let requestsDir =
|
|
5081
|
+
let requestsDir = path8.join(channelDir, REQUESTS_DIR), repliesDir = path8.join(channelDir, REPLIES_DIR), questionsPath = path8.join(channelDir, QUESTIONS_DIR), newestKnownMtimeMs = Math.max(directoryMtimeMs(channelDir), directoryMtimeMs(requestsDir), directoryMtimeMs(repliesDir), directoryMtimeMs(questionsPath));
|
|
4495
5082
|
if (nowMs - newestKnownMtimeMs < STALE_EMPTY_CHANNEL_AGE_MS)
|
|
4496
5083
|
return !1;
|
|
4497
5084
|
let requestEntries = readDirectoryEntries(requestsDir);
|
|
4498
5085
|
if (!requestEntries || requestEntries.length > 0)
|
|
4499
5086
|
return !1;
|
|
4500
5087
|
let replyEntries = readDirectoryEntries(repliesDir);
|
|
4501
|
-
|
|
5088
|
+
if (!replyEntries || replyEntries.length > 0)
|
|
5089
|
+
return !1;
|
|
5090
|
+
let questionEntries = readDirectoryEntries(questionsPath);
|
|
5091
|
+
return !(!questionEntries || questionEntries.length > 0 || !removeEmptyDirectory(requestsDir) || !removeEmptyDirectory(repliesDir) || !removeEmptyDirectory(questionsPath) || !removeEmptyDirectory(channelDir));
|
|
4502
5092
|
}
|
|
4503
5093
|
function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
|
|
4504
5094
|
let channelEntries;
|
|
4505
5095
|
try {
|
|
4506
|
-
channelEntries =
|
|
5096
|
+
channelEntries = fs7.readdirSync(SUPERVISOR_CHANNEL_ROOT, { withFileTypes: !0 });
|
|
4507
5097
|
} catch (error) {
|
|
4508
5098
|
if (error.code === "ENOENT")
|
|
4509
5099
|
return 0;
|
|
@@ -4513,7 +5103,7 @@ function cleanupStaleEmptySupervisorChannels(nowMs = Date.now()) {
|
|
|
4513
5103
|
for (let entry of channelEntries)
|
|
4514
5104
|
if (entry.isDirectory())
|
|
4515
5105
|
try {
|
|
4516
|
-
removeStaleEmptySupervisorChannel(
|
|
5106
|
+
removeStaleEmptySupervisorChannel(path8.join(SUPERVISOR_CHANNEL_ROOT, entry.name), nowMs) && removed++;
|
|
4517
5107
|
} catch {
|
|
4518
5108
|
}
|
|
4519
5109
|
return removed;
|
|
@@ -4533,13 +5123,13 @@ function requestMatchesContext(request, state, ctx) {
|
|
|
4533
5123
|
}
|
|
4534
5124
|
function removeRequestFile(file) {
|
|
4535
5125
|
try {
|
|
4536
|
-
|
|
5126
|
+
fs7.rmSync(file, { force: !0 });
|
|
4537
5127
|
} catch {
|
|
4538
5128
|
}
|
|
4539
5129
|
}
|
|
4540
5130
|
function requestExpiresAt(request, now) {
|
|
4541
5131
|
let expiresAt = request.expiresAt;
|
|
4542
|
-
return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt +
|
|
5132
|
+
return typeof expiresAt == "number" && Number.isFinite(expiresAt) ? expiresAt : Number.isFinite(request.createdAt) ? request.createdAt + askTimeoutMs2() : now;
|
|
4543
5133
|
}
|
|
4544
5134
|
function requestRunInactive(request, state) {
|
|
4545
5135
|
if (state.foregroundControls.has(request.runId))
|
|
@@ -4556,7 +5146,7 @@ function requestRunInactive(request, state) {
|
|
|
4556
5146
|
return stepStatus === "complete" || stepStatus === "completed" || stepStatus === "failed" || stepStatus === "paused";
|
|
4557
5147
|
}
|
|
4558
5148
|
function requestLifecycle(request, state, ctx, now) {
|
|
4559
|
-
return ctx && !requestMatchesContext(request, state, ctx) ? "wrong-session" :
|
|
5149
|
+
return ctx && !requestMatchesContext(request, state, ctx) ? "wrong-session" : fs7.existsSync(request.requestFile) ? request.expectsReply && fs7.existsSync(replyPath(request.channelDir, request.id)) ? "resolved" : request.expectsReply && now > requestExpiresAt(request, now) ? "expired" : request.expectsReply && requestRunInactive(request, state) ? "inactive" : "pending" : "missing";
|
|
4560
5150
|
}
|
|
4561
5151
|
function cleanupRequestLifecycle(request, lifecycle) {
|
|
4562
5152
|
(lifecycle === "resolved" || lifecycle === "expired" || lifecycle === "inactive") && removeRequestFile(request.requestFile);
|
|
@@ -4621,8 +5211,8 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
|
|
|
4621
5211
|
return {
|
|
4622
5212
|
name,
|
|
4623
5213
|
label: name === "intercom" ? "Intercom" : "Subagent Supervisor",
|
|
4624
|
-
description:
|
|
4625
|
-
parameters:
|
|
5214
|
+
description: PARENT_ASK_DESCRIPTION,
|
|
5215
|
+
parameters: ParentSupervisorParamsSchema,
|
|
4626
5216
|
async execute(_id, params) {
|
|
4627
5217
|
refreshPendingRequests(pending, state, state.lastUiContext ?? void 0);
|
|
4628
5218
|
let input = params;
|
|
@@ -4637,10 +5227,59 @@ function buildParentIntercomTool(pending, state, name = "intercom") {
|
|
|
4637
5227
|
let request = resolvePendingRequest(pending, input);
|
|
4638
5228
|
return writeReply(request, input.message ?? ""), pending.delete(request.id), { content: [{ type: "text", text: `Replied to supervisor request ${request.id}.` }], details: { replyTo: request.id, runId: request.runId, agent: request.agent } };
|
|
4639
5229
|
}
|
|
4640
|
-
|
|
5230
|
+
if (input.action === "ask")
|
|
5231
|
+
return askRunningAsyncChild({
|
|
5232
|
+
params: {
|
|
5233
|
+
id: input.id,
|
|
5234
|
+
index: input.index,
|
|
5235
|
+
childId: input.childId,
|
|
5236
|
+
reason: input.reason,
|
|
5237
|
+
message: input.message
|
|
5238
|
+
},
|
|
5239
|
+
state,
|
|
5240
|
+
childRequests: pending.values()
|
|
5241
|
+
});
|
|
5242
|
+
throw input.action === "send" ? new Error("Native pi-subagents intercom does not support free-form send. Use action='ask' for a running async child or action='reply' for a pending child request.") : new Error(`Unsupported intercom action: ${input.action}`);
|
|
4641
5243
|
}
|
|
4642
5244
|
};
|
|
4643
5245
|
}
|
|
5246
|
+
function questionMatchesParentContext(question, state, ctx) {
|
|
5247
|
+
let sessionId = currentContextSessionId(state, ctx);
|
|
5248
|
+
return !(!sessionId || question.parentSessionId !== sessionId || (state.sessionGeneration ?? 0) !== question.parentGeneration);
|
|
5249
|
+
}
|
|
5250
|
+
function notifyQuestionTerminal(pi, question, channelDir) {
|
|
5251
|
+
if (!supervisorQuestionIsTerminal(question) || question.notifiedAt || (pi.events?.emit(SUBAGENT_QUESTION_EVENT, {
|
|
5252
|
+
questionId: question.id,
|
|
5253
|
+
state: supervisorQuestionPublicState(question),
|
|
5254
|
+
runId: question.runId
|
|
5255
|
+
}), isQuestionWaitActive(question.id)) || !claimQuestionNotification(channelDir, question.id))
|
|
5256
|
+
return;
|
|
5257
|
+
let state = supervisorQuestionPublicState(question);
|
|
5258
|
+
pi.sendMessage({
|
|
5259
|
+
customType: SUBAGENT_SUPERVISOR_ANSWER_TYPE,
|
|
5260
|
+
content: formatSupervisorQuestionAnswer(question),
|
|
5261
|
+
display: state === "answered",
|
|
5262
|
+
details: {
|
|
5263
|
+
questionId: question.id,
|
|
5264
|
+
state,
|
|
5265
|
+
runId: question.runId,
|
|
5266
|
+
childIndex: question.childIndex,
|
|
5267
|
+
childId: question.childId,
|
|
5268
|
+
reason: question.reason,
|
|
5269
|
+
answered: state === "answered",
|
|
5270
|
+
delivered: !!question.deliveredAt
|
|
5271
|
+
}
|
|
5272
|
+
}, { triggerTurn: state === "answered" });
|
|
5273
|
+
}
|
|
5274
|
+
function pollSupervisorQuestions(pi, state, ctx) {
|
|
5275
|
+
for (let channelDir of listSupervisorChannelDirs())
|
|
5276
|
+
for (let question of listSupervisorQuestions(channelDir)) {
|
|
5277
|
+
if (!questionMatchesParentContext(question, state, ctx))
|
|
5278
|
+
continue;
|
|
5279
|
+
let current = reconcileAsyncQuestion(channelDir, question, state);
|
|
5280
|
+
notifyQuestionTerminal(pi, current, channelDir);
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
4644
5283
|
function createNativeSupervisorChannel(pi, state) {
|
|
4645
5284
|
let pending = /* @__PURE__ */ new Map(), seenFiles = /* @__PURE__ */ new Set(), poller, lastStaleCleanupAt = 0, registerParentTools = () => {
|
|
4646
5285
|
hasTool(pi, NATIVE_SUPERVISOR_TOOL_NAME) || pi.registerTool(buildParentIntercomTool(pending, state, NATIVE_SUPERVISOR_TOOL_NAME)), hasTool(pi, "intercom") || pi.registerTool(buildParentIntercomTool(pending, state));
|
|
@@ -4653,12 +5292,24 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4653
5292
|
} catch {
|
|
4654
5293
|
}
|
|
4655
5294
|
}
|
|
5295
|
+
}, cancelOwnedQuestions = (reason) => {
|
|
5296
|
+
state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
|
|
5297
|
+
parentSessionId: state.currentSessionId,
|
|
5298
|
+
parentGeneration: state.sessionGeneration ?? 0,
|
|
5299
|
+
reason
|
|
5300
|
+
});
|
|
5301
|
+
}, settleRunQuestions = (runId, reason) => {
|
|
5302
|
+
state.currentSessionId && cancelPendingSupervisorQuestionsForOwner({
|
|
5303
|
+
parentSessionId: state.currentSessionId,
|
|
5304
|
+
runId,
|
|
5305
|
+
reason
|
|
5306
|
+
});
|
|
4656
5307
|
}, poll = () => {
|
|
4657
5308
|
cleanupStaleChannelsIfDue();
|
|
4658
5309
|
let ctx = state.lastUiContext;
|
|
4659
5310
|
if (!ctx)
|
|
4660
5311
|
return;
|
|
4661
|
-
refreshPendingRequests(pending, state, ctx);
|
|
5312
|
+
refreshPendingRequests(pending, state, ctx), pollSupervisorQuestions(pi, state, ctx);
|
|
4662
5313
|
let now = Date.now();
|
|
4663
5314
|
for (let { channelDir, file } of listRequestFiles()) {
|
|
4664
5315
|
if (seenFiles.has(file))
|
|
@@ -4671,7 +5322,7 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4671
5322
|
seenFiles.add(file), cleanupRequestLifecycle(request, lifecycle);
|
|
4672
5323
|
continue;
|
|
4673
5324
|
}
|
|
4674
|
-
seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
|
|
5325
|
+
if (seenFiles.add(file), request.expectsReply ? pending.set(request.id, request) : removeRequestFile(request.requestFile), pi.sendMessage({
|
|
4675
5326
|
customType: "subagent_supervisor_request",
|
|
4676
5327
|
content: requestVisibleText(request),
|
|
4677
5328
|
display: !0,
|
|
@@ -4683,12 +5334,23 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4683
5334
|
agent: request.agent,
|
|
4684
5335
|
childIndex: request.childIndex
|
|
4685
5336
|
}
|
|
4686
|
-
}), request.expectsReply
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
5337
|
+
}), request.expectsReply) {
|
|
5338
|
+
let job = state.asyncJobs.get(request.runId);
|
|
5339
|
+
job && (job.activityState = "needs_attention");
|
|
5340
|
+
let events = pi.events;
|
|
5341
|
+
events?.emit(INTERCOM_DETACH_REQUEST_EVENT, {
|
|
5342
|
+
requestId: request.id,
|
|
5343
|
+
runId: request.runId,
|
|
5344
|
+
agent: request.agent,
|
|
5345
|
+
childIndex: request.childIndex
|
|
5346
|
+
}), events?.emit(SUBAGENT_CONTROL_EVENT, {
|
|
5347
|
+
type: "needs_attention",
|
|
5348
|
+
to: "needs_attention",
|
|
5349
|
+
runId: request.runId,
|
|
5350
|
+
agent: request.agent,
|
|
5351
|
+
index: request.childIndex
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
4692
5354
|
}
|
|
4693
5355
|
};
|
|
4694
5356
|
return {
|
|
@@ -4696,14 +5358,16 @@ function createNativeSupervisorChannel(pi, state) {
|
|
|
4696
5358
|
poller || (registerParentTools(), poll(), poller = setInterval(poll, CHANNEL_POLL_MS), poller.unref?.());
|
|
4697
5359
|
},
|
|
4698
5360
|
dispose: () => {
|
|
4699
|
-
poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
|
|
5361
|
+
cancelOwnedQuestions("parent supervisor channel disposed"), poller && clearInterval(poller), poller = void 0, pending.clear(), seenFiles.clear();
|
|
4700
5362
|
},
|
|
4701
|
-
pending
|
|
5363
|
+
pending,
|
|
5364
|
+
cancelOwnedQuestions,
|
|
5365
|
+
settleRunQuestions
|
|
4702
5366
|
};
|
|
4703
5367
|
}
|
|
4704
5368
|
|
|
4705
5369
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/rpc.js
|
|
4706
|
-
import * as
|
|
5370
|
+
import * as path9 from "node:path";
|
|
4707
5371
|
import { Compile } from "typebox/compile";
|
|
4708
5372
|
var SUBAGENT_RPC_PROTOCOL_VERSION = 1, SUBAGENT_RPC_REQUEST_EVENT = "subagents:rpc:v1:request", SUBAGENT_RPC_READY_EVENT = "subagents:rpc:v1:ready", SUBAGENT_RPC_REPLY_EVENT_PREFIX = "subagents:rpc:v1:reply:", SUBAGENT_RPC_METHODS = ["ping", "status", "spawn", "interrupt", "stop"], SubagentRpcError = class extends Error {
|
|
4709
5373
|
code;
|
|
@@ -4810,7 +5474,7 @@ function stopAsyncRun(params, options, ctx) {
|
|
|
4810
5474
|
}
|
|
4811
5475
|
if (!location.asyncDir)
|
|
4812
5476
|
throw new SubagentRpcError("not_found", "Async run not found or already completed; stop requires a live async run directory.");
|
|
4813
|
-
let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ??
|
|
5477
|
+
let currentSessionId = ctx.sessionManager.getSessionId(), initialStatus = readStatus(location.asyncDir), initialRunId = initialStatus?.runId ?? location.resolvedId ?? path9.basename(location.asyncDir);
|
|
4814
5478
|
if (!initialStatus)
|
|
4815
5479
|
throw new SubagentRpcError("not_found", `Status file not found for async run '${initialRunId}'.`);
|
|
4816
5480
|
if (!currentSessionId || initialStatus.sessionId !== currentSessionId)
|
|
@@ -5042,7 +5706,9 @@ var ACTIVE_STATES = ["queued", "running"], DEFAULT_TIMEOUT_MS = 1800 * 1e3, MIN_
|
|
|
5042
5706
|
SUBAGENT_FOREGROUND_COMPLETE_EVENT,
|
|
5043
5707
|
SUBAGENT_CONTROL_EVENT,
|
|
5044
5708
|
SUBAGENT_CONTROL_INTERCOM_EVENT,
|
|
5045
|
-
SUBAGENT_RESULT_INTERCOM_EVENT
|
|
5709
|
+
SUBAGENT_RESULT_INTERCOM_EVENT,
|
|
5710
|
+
INTERCOM_DETACH_REQUEST_EVENT,
|
|
5711
|
+
SUBAGENT_QUESTION_EVENT
|
|
5046
5712
|
];
|
|
5047
5713
|
function defaultSleep(ms, signal) {
|
|
5048
5714
|
return new Promise((resolve3) => {
|
|
@@ -5111,8 +5777,8 @@ function summarizeForegroundChildren(run, indices) {
|
|
|
5111
5777
|
!indices.has(child.index) || child.status === "detached" || counts.set(child.status, (counts.get(child.status) ?? 0) + 1);
|
|
5112
5778
|
return [...counts.entries()].map(([status, count]) => `${count} ${status}`).join(", ");
|
|
5113
5779
|
}
|
|
5114
|
-
function needsAttention(run) {
|
|
5115
|
-
return run.activityState === "needs_attention";
|
|
5780
|
+
function needsAttention(run, sessionId) {
|
|
5781
|
+
return run.activityState === "needs_attention" || hasPendingBlockingSupervisorRequest(run.id, sessionId);
|
|
5116
5782
|
}
|
|
5117
5783
|
function backgroundWorkIdentity(item) {
|
|
5118
5784
|
return `${item.provider}\0${item.sessionId}\0${item.id}`;
|
|
@@ -5134,7 +5800,7 @@ function activeRunsForSession(params, deps) {
|
|
|
5134
5800
|
return params.id ? runs.filter((run) => matchesId(run, params.id)) : runs;
|
|
5135
5801
|
}
|
|
5136
5802
|
function attentionRunsForSession(params, deps, initialIds) {
|
|
5137
|
-
return activeRunsForSession(params, deps).filter((run) => needsAttention(run) && initialIds.has(run.id));
|
|
5803
|
+
return activeRunsForSession(params, deps).filter((run) => needsAttention(run, deps.state.currentSessionId) && initialIds.has(run.id));
|
|
5138
5804
|
}
|
|
5139
5805
|
function allRunsForSession(params, deps) {
|
|
5140
5806
|
let asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR, resultsDir = deps.resultsDir ?? RESULTS_DIR, runs = listAsyncRuns(asyncDirRoot, {
|
|
@@ -5180,12 +5846,57 @@ async function waitForDetachedForegroundRun(run, signal, deps, startedAt, now, p
|
|
|
5180
5846
|
await waitForWake(pollIntervalMs, signal, deps);
|
|
5181
5847
|
}
|
|
5182
5848
|
}
|
|
5849
|
+
async function waitForSupervisorQuestion(questionId, signal, deps, startedAt, now, pollIntervalMs, timeoutMs) {
|
|
5850
|
+
let sessionId = deps.state.currentSessionId;
|
|
5851
|
+
if (!sessionId)
|
|
5852
|
+
return result("subagent_wait requires an active session identity to scope background work safely.", !0);
|
|
5853
|
+
let releaseWait = registerQuestionWait([questionId]);
|
|
5854
|
+
try {
|
|
5855
|
+
for (; ; ) {
|
|
5856
|
+
let found = findSupervisorQuestion(questionId);
|
|
5857
|
+
if (!found)
|
|
5858
|
+
return result(`No supervisor question matched "${questionId}".`, !0);
|
|
5859
|
+
let { channelDir } = found, { question } = found;
|
|
5860
|
+
if (question.parentSessionId !== sessionId || deps.state.currentSessionId !== sessionId)
|
|
5861
|
+
return result(`Supervisor question "${questionId}" belongs to another session.`, !0);
|
|
5862
|
+
if ((deps.state.sessionGeneration ?? 0) !== question.parentGeneration)
|
|
5863
|
+
return result(`Supervisor question "${questionId}" was cancelled by a session replacement.`, !0);
|
|
5864
|
+
if (question = reconcileAsyncQuestion(channelDir, question, deps.state), supervisorQuestionIsTerminal(question)) {
|
|
5865
|
+
claimQuestionNotification(channelDir, question.id);
|
|
5866
|
+
let state = supervisorQuestionPublicState(question);
|
|
5867
|
+
return {
|
|
5868
|
+
content: [{ type: "text", text: formatSupervisorQuestionAnswer(question) }],
|
|
5869
|
+
...state === "answered" ? {} : { isError: !0 },
|
|
5870
|
+
details: {
|
|
5871
|
+
mode: "management",
|
|
5872
|
+
results: [],
|
|
5873
|
+
questionId: question.id,
|
|
5874
|
+
state,
|
|
5875
|
+
runId: question.runId,
|
|
5876
|
+
answered: state === "answered",
|
|
5877
|
+
delivered: !!question.deliveredAt
|
|
5878
|
+
}
|
|
5879
|
+
};
|
|
5880
|
+
}
|
|
5881
|
+
if (signal?.aborted)
|
|
5882
|
+
return result(`Wait aborted after ${formatDuration(now() - startedAt)} for supervisor question "${questionId}".`, !0);
|
|
5883
|
+
if (now() - startedAt >= timeoutMs)
|
|
5884
|
+
return result(`Wait timed out after ${formatDuration(timeoutMs)} for supervisor question "${questionId}". The question remains pending until answered, expired, or cancelled.`, !0);
|
|
5885
|
+
await waitForWake(pollIntervalMs, signal, deps);
|
|
5886
|
+
}
|
|
5887
|
+
} finally {
|
|
5888
|
+
releaseWait();
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5183
5891
|
async function waitForSubagents(params, signal, deps) {
|
|
5184
5892
|
if (deps.enabled === !1)
|
|
5185
5893
|
return result('subagent_wait is disabled by config.waitTool or PI_SUBAGENT_WAIT_TOOL_ENABLED; returning immediately without blocking background work. Active work keeps going, and you can inspect subagents with subagent({ action: "status" }) or rely on completion notifications.');
|
|
5186
5894
|
if (!deps.state.currentSessionId)
|
|
5187
5895
|
return result("subagent_wait requires an active session identity to scope background work safely.", !0);
|
|
5188
|
-
let now = deps.now ?? Date.now, pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS), timeoutMs = params.timeoutMs !== void 0 && params.timeoutMs > 0 ? params.timeoutMs : DEFAULT_TIMEOUT_MS, startedAt = now()
|
|
5896
|
+
let now = deps.now ?? Date.now, pollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS), timeoutMs = params.timeoutMs !== void 0 && params.timeoutMs > 0 ? params.timeoutMs : DEFAULT_TIMEOUT_MS, startedAt = now();
|
|
5897
|
+
if (params.questionId?.trim())
|
|
5898
|
+
return waitForSupervisorQuestion(params.questionId.trim(), signal, deps, startedAt, now, pollIntervalMs, timeoutMs);
|
|
5899
|
+
let waitForAll = params.id ? !0 : params.all === !0, active, foreground, providerSnapshot;
|
|
5189
5900
|
try {
|
|
5190
5901
|
active = activeRunsForSession(params, deps), foreground = activeDetachedForegroundRuns(params, deps), providerSnapshot = params.id ? { providers: [], items: [] } : backgroundWorkForSession(deps, startedAt);
|
|
5191
5902
|
} catch (error) {
|
|
@@ -5206,7 +5917,7 @@ async function waitForSubagents(params, signal, deps) {
|
|
|
5206
5917
|
let providerActive = providerSnapshot.items;
|
|
5207
5918
|
if (active.length === 0 && providerActive.length === 0)
|
|
5208
5919
|
return result(params.id ? `No active run matched "${params.id}". Nothing to wait for.` : "No active async runs or registered provider work in this session. Nothing to wait for.");
|
|
5209
|
-
let waitParams = params.id ? { ...params, id: active[0].id } : params, initialAsyncIds = new Set(active.map((run) => run.id)), initialProviderIds = new Set(providerActive.map(backgroundWorkIdentity)), initialProviderNames = new Set(providerActive.map((item) => item.provider)), initialCount = initialAsyncIds.size + initialProviderIds.size, stopOnAttention = deps.stopOnAttention !== !1, attention = active.filter((run) => needsAttention(run)), isDone = () => {
|
|
5920
|
+
let waitParams = params.id ? { ...params, id: active[0].id } : params, initialAsyncIds = new Set(active.map((run) => run.id)), initialProviderIds = new Set(providerActive.map(backgroundWorkIdentity)), initialProviderNames = new Set(providerActive.map((item) => item.provider)), initialCount = initialAsyncIds.size + initialProviderIds.size, stopOnAttention = deps.stopOnAttention !== !1, attention = active.filter((run) => needsAttention(run, deps.state.currentSessionId)), isDone = () => {
|
|
5210
5921
|
if (stopOnAttention && attention.some((run) => initialAsyncIds.has(run.id)))
|
|
5211
5922
|
return !0;
|
|
5212
5923
|
let activeAsyncIds = new Set(active.map((run) => run.id)), activeProviderIds2 = new Set(providerActive.map(backgroundWorkIdentity));
|
|
@@ -5259,6 +5970,7 @@ In an interactive chat, do not call this merely to wait: return control to the u
|
|
|
5259
5970
|
\u2022 { } \u2014 return when the first initially active async run or registered provider item finishes, or when a subagent needs attention.
|
|
5260
5971
|
\u2022 { all: true } \u2014 wait for every async run and provider item that was active when the call began.
|
|
5261
5972
|
\u2022 { id: "..." } \u2014 wait for one async or remembered detached foreground subagent run (id or prefix).
|
|
5973
|
+
\u2022 { questionId: "..." } \u2014 wait for one supervisor question from subagent_supervisor action='ask' (answered/expired/cancelled). Separate from final run results.
|
|
5262
5974
|
\u2022 { timeoutMs: 600000 } \u2014 stop waiting after N ms; active work keeps running.
|
|
5263
5975
|
|
|
5264
5976
|
Provider jobs are session-scoped and identified exactly, so replacing one job with another cannot hide a completion. Provider extensions must be explicitly loaded in this process. In a child agent, keep \`subagent_wait\` in the child tool allowlist and load each provider through the agent's extensions or subagentOnlyExtensions; this tool never loads providers or grants tools itself.${enabled ? "" : `
|
|
@@ -5393,7 +6105,7 @@ function formatSingleCompletion(details) {
|
|
|
5393
6105
|
}
|
|
5394
6106
|
function parseSubagentNotifyContent(content) {
|
|
5395
6107
|
let lines = content.split(`
|
|
5396
|
-
`), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
6108
|
+
`), match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
5397
6109
|
if (!match)
|
|
5398
6110
|
return;
|
|
5399
6111
|
let body = lines.slice(2), sessionIndex = -1;
|
|
@@ -5437,7 +6149,7 @@ function sendCompletion(pi, details) {
|
|
|
5437
6149
|
customType: "subagent-notify",
|
|
5438
6150
|
content,
|
|
5439
6151
|
display: !0
|
|
5440
|
-
}, { triggerTurn:
|
|
6152
|
+
}, { triggerTurn: details.some((detail) => detail.status !== "stopped") });
|
|
5441
6153
|
}
|
|
5442
6154
|
function completionBatchKey(result2) {
|
|
5443
6155
|
let sessionId = typeof result2.sessionId == "string" ? result2.sessionId.trim() : "";
|
|
@@ -5447,7 +6159,7 @@ function completionBatchKey(result2) {
|
|
|
5447
6159
|
return cwd ? `cwd:${cwd}` : "unknown";
|
|
5448
6160
|
}
|
|
5449
6161
|
function buildCompletionDetails(result2) {
|
|
5450
|
-
let agent = result2.agent ?? "unknown", summary = typeof result2.summary == "string" ? result2.summary : "",
|
|
6162
|
+
let agent = result2.agent ?? "unknown", summary = typeof result2.summary == "string" ? result2.summary : "", paused = !result2.success && (result2.exitCode === 0 || result2.state === "paused" || summary.startsWith("Paused after interrupt.")), status = result2.state === "stopped" ? "stopped" : paused ? "paused" : result2.success ? "completed" : "failed", taskInfo = result2.taskIndex !== void 0 && result2.totalTasks !== void 0 ? ` (${result2.taskIndex + 1}/${result2.totalTasks})` : void 0, session = result2.shareUrl ? { label: "Session", value: result2.shareUrl } : result2.shareError ? { label: "Session share error", value: result2.shareError } : result2.sessionFile ? { label: "Session file", value: result2.sessionFile } : void 0;
|
|
5451
6163
|
return {
|
|
5452
6164
|
agent,
|
|
5453
6165
|
status,
|
|
@@ -5536,15 +6248,15 @@ function handleSubagentSteeringNotice(input) {
|
|
|
5536
6248
|
}
|
|
5537
6249
|
|
|
5538
6250
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/config.js
|
|
5539
|
-
import * as
|
|
5540
|
-
import * as
|
|
6251
|
+
import * as fs8 from "node:fs";
|
|
6252
|
+
import * as path10 from "node:path";
|
|
5541
6253
|
function getConfigPath() {
|
|
5542
|
-
return
|
|
6254
|
+
return path10.join(getAgentDir(), "extensions", "subagent", "config.json");
|
|
5543
6255
|
}
|
|
5544
6256
|
function readConfigForUpdate(configPath = getConfigPath()) {
|
|
5545
|
-
if (!
|
|
6257
|
+
if (!fs8.existsSync(configPath))
|
|
5546
6258
|
return {};
|
|
5547
|
-
let parsed = JSON.parse(
|
|
6259
|
+
let parsed = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
|
|
5548
6260
|
if (!parsed || typeof parsed != "object" || Array.isArray(parsed))
|
|
5549
6261
|
throw new Error(`Subagent config at '${configPath}' must be a JSON object`);
|
|
5550
6262
|
return parsed;
|
|
@@ -5560,25 +6272,31 @@ function loadConfig() {
|
|
|
5560
6272
|
}
|
|
5561
6273
|
|
|
5562
6274
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js
|
|
5563
|
-
import * as
|
|
5564
|
-
import * as
|
|
5565
|
-
var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024,
|
|
6275
|
+
import * as fs9 from "node:fs";
|
|
6276
|
+
import * as path11 from "node:path";
|
|
6277
|
+
var CUSTOM_TOOL_DESCRIPTION_FILE = "subagent-tool-description.md", CUSTOM_TOOL_DESCRIPTION_MAX_BYTES = 50 * 1024, SUBAGENT_TIER_GUIDANCE = `TIER SELECTION:
|
|
6278
|
+
\u2022 Choose the lowest tier that can reliably complete the task.
|
|
6279
|
+
\u2022 light: Use for simple, straightforward tasks that require little reasoning. Good for quick checks, finding information, collecting facts for research, inspecting a small amount of code, or verifying a specific fact.
|
|
6280
|
+
\u2022 standard: Use for moderately difficult work and everyday coding tasks. Good for writing simple code, fixing a clearly described bug, reviewing a focused change, or completing work with clear requirements. Use heavy when the task requires deep architectural understanding or complex reasoning.
|
|
6281
|
+
\u2022 heavy: Use for complex or ambiguous tasks that require deep reasoning and strong code understanding. Good for implementing complex plans, debugging difficult or poorly understood problems, reviewing large or high-risk changes, and making architectural decisions.`, SUBAGENT_SAFETY_GUIDANCE = `SAFETY-CRITICAL SUBAGENT GUIDANCE:
|
|
5566
6282
|
\u2022 Prompt children directly. There is no agent roster. Every execution task needs task (the full child prompt) and description (a concise UI label).
|
|
5567
|
-
\u2022 Every executable child
|
|
6283
|
+
\u2022 Every executable child chooses exactly one of tier: "light"|"standard"|"heavy" (default) or model: "provider/id" when the user names a model. Do not pass both, and do not use inherit. Missing, disabled, unconfigured, unavailable, or unauthenticated selections fail before launch.
|
|
6284
|
+
\u2022 Optional thinking (off/minimal/low/medium/high/xhigh/max) is only valid with an explicit model. Tier launches use the configured tier thinking level.
|
|
6285
|
+
${SUBAGENT_TIER_GUIDANCE}
|
|
5568
6286
|
\u2022 permissions is "full" or "read-only". Omitted permissions means full. Plan-mode parents must pass permissions: "read-only"; full or omitted launches are rejected.
|
|
5569
|
-
\u2022 Keep execution and control separate: omit action for SINGLE/PARALLEL/CHAIN execution; use action only for status/interrupt/stop/resume/steer/append-step/doctor/watchdog
|
|
6287
|
+
\u2022 Keep execution and control separate: omit action for SINGLE/PARALLEL/CHAIN execution; use action only for status/interrupt/stop/resume/steer/append-step/doctor/watchdog.status|check|recommend-model/schedule*.
|
|
5570
6288
|
\u2022 Async/background runs: launch with async:true only when work can proceed independently. Do not sleep or poll status just to wait. In an interactive session, normally return control and let lunR wake you; use subagent_wait when this request must run to completion in the current turn or skill. Headless sessions auto-drain current-session work.
|
|
5571
6289
|
\u2022 Child-safety boundary: ordinary children are not orchestrators and must not run subagents. Only explicitly configured fanout children may use the child-safe subagent tool, still bounded by depth/session limits.
|
|
5572
6290
|
\u2022 Writing/review safety: keep one full-access writer for the same cwd/worktree. Use fresh-context permissions: "read-only" children for independent review, then have the parent synthesize and apply fixes as the sole writer unless an isolated worktree was intentionally requested.
|
|
5573
6291
|
\u2022 Artifacts/status essentials: chain outputs live under {chain_dir}; async runs expose asyncId/asyncDir with status.json, events.jsonl, output logs, and status via { action: "status", id }. Include output paths and residual risks when reporting results.`, FULL_SUBAGENT_TOOL_DESCRIPTION = `Delegate work to generic children by prompting them directly. There are no named agent types.
|
|
5574
6292
|
|
|
5575
6293
|
EXECUTION (use exactly ONE mode):
|
|
5576
|
-
\u2022 SINGLE: { task, description, tier, permissions? } \u2014 one child. Multiple SINGLE calls in the same turn run concurrently; explicit concurrency/run limits still apply.
|
|
5577
|
-
\u2022 PARALLEL: { tasks: [{task, description, tier, permissions?, count?, output?, reads?, progress?}, ...], concurrency?: number, worktree?: true } \u2014 one-call concurrent execution (default: all tasks at once; worktree: isolate each task in a git worktree)
|
|
5578
|
-
\u2022 CHAIN: { chain: [{task, description, tier, permissions?}, {parallel:[{task, description, tier, permissions?, count:3}]}] } \u2014 sequential pipeline with optional parallel fan-out. Use chain when a later child needs an earlier result.
|
|
6294
|
+
\u2022 SINGLE: { task, description, tier|model, thinking?, permissions? } \u2014 one child. Multiple SINGLE calls in the same turn run concurrently; explicit concurrency/run limits still apply.
|
|
6295
|
+
\u2022 PARALLEL: { tasks: [{task, description, tier|model, thinking?, permissions?, count?, output?, reads?, progress?}, ...], concurrency?: number, worktree?: true } \u2014 one-call concurrent execution (default: all tasks at once; worktree: isolate each task in a git worktree)
|
|
6296
|
+
\u2022 CHAIN: { chain: [{task, description, tier|model, thinking?, permissions?}, {parallel:[{task, description, tier|model, thinking?, permissions?, count:3}]}] } \u2014 sequential pipeline with optional parallel fan-out. Use chain when a later child needs an earlier result.
|
|
5579
6297
|
\u2022 description is required, single-line, max 80 characters. It is UI metadata only. task is the complete child prompt.
|
|
5580
6298
|
\u2022 permissions: "full" (default) or "read-only". Full includes coding tools (read, search, shell, edit, write, web, LSP, MCP) and excludes parent-owned tools (cron, memory, behavior, goals, nested subagents). Plan-mode parents may launch only permissions: "read-only".
|
|
5581
|
-
\u2022
|
|
6299
|
+
\u2022 Model selection: pass tier by default ("light"|"standard"|"heavy"). Pass model: "provider/id" only when the user specifies a model. Exactly one of tier or model. Optional thinking only with model; tier uses configured thinking. Direct model launches do not require tier mode to be enabled. No inherit and no silent fallback for an explicit model.
|
|
5582
6300
|
\u2022 Children always start with a fresh session (no inherited parent transcript).
|
|
5583
6301
|
\u2022 Optional timeout: { timeoutMs } or { maxRuntimeMs } sets a run-level max runtime for foreground and async/background runs
|
|
5584
6302
|
|
|
@@ -5593,8 +6311,7 @@ CHAIN EXAMPLES:
|
|
|
5593
6311
|
\u2022 Mixed: { chain: [{task:"Research {task}", description:"Research request", tier:"standard", permissions:"read-only"}, {parallel: [{task:"Review {previous}", description:"Review findings", tier:"light", permissions:"read-only", count: 2}]}, {task:"Summarize {previous}", description:"Summarize reviews", tier:"standard"}] }
|
|
5594
6312
|
|
|
5595
6313
|
CONTROL (use action field, omit task/chain/tasks):
|
|
5596
|
-
\u2022 { action: "watchdog.status" | "watchdog.check" | "watchdog.recommend-model" } - inspect the opt-in subagent watchdog and its strong complementary model recommendation
|
|
5597
|
-
\u2022 { action: "watchdog.configure", model: "recommended" | "inherit" | "provider/model[:thinking]", scope?: "session" | "user" | "project", target?: "main" | "children", thinking?: "inherit" | "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max" } - configure watchdog model selection; default scope is session, use persistent scopes only when the user asks
|
|
6314
|
+
\u2022 { action: "watchdog.status" | "watchdog.check" | "watchdog.recommend-model" } - inspect the opt-in subagent watchdog and its strong complementary model recommendation (read-only; there is no configure action)
|
|
5598
6315
|
\u2022 { action: "status", id: "..." } - inspect an async/background run by id or prefix
|
|
5599
6316
|
\u2022 { action: "status", view: "fleet" } - read-only active foreground/async fleet view with transcript commands
|
|
5600
6317
|
\u2022 { action: "status", id: "...", view: "transcript", index?: 0, lines?: 80 } - tail a run or child output/session transcript
|
|
@@ -5602,10 +6319,12 @@ CONTROL (use action field, omit task/chain/tasks):
|
|
|
5602
6319
|
\u2022 { action: "stop", id: "..." } - stop a current-session top-level async run; stopped runs finish with state "stopped"
|
|
5603
6320
|
\u2022 { action: "resume", id: "...", message: "...", index?: 0 } - interrupt then follow up with a live async child, or revive a completed async/foreground child from its session
|
|
5604
6321
|
\u2022 { action: "steer", id: "...", message: "...", index?: 0 } - await correlated child input acceptance for up to 3 seconds; returns delivered, scheduled, pending, partial, recovered, or failed with a request id. Only top-level single runs may recover after a further 15-second pause/revival bound; chain, parallel, and nested runs never auto-interrupt.
|
|
6322
|
+
\u2022 subagent_supervisor({ action: "ask", id, reason, message, index?, childId? }) - ask one running async child; returns question id immediately. Ask only when the child owns missing context, the answer changes a concrete next decision, and waiting risks block/rework. No broadcast, revive, task replacement, status polling, or micromanagement.
|
|
6323
|
+
\u2022 subagent_wait({ questionId }) - wait for that supervisor question answer/expiry/cancel. Answers stay separate from final run results.
|
|
5605
6324
|
\u2022 { action: "append-step", id: "...", chain: [{task:"Use {previous}", description:"Follow-up step", tier:"standard"}] } - append one step to the tail of a running async chain
|
|
5606
6325
|
|
|
5607
6326
|
SCHEDULE (opt-in; requires { "scheduledRuns": { "enabled": true } } in config.json):
|
|
5608
|
-
\u2022 { action: "schedule", task, description, tier, permissions?, schedule: "+10m" | "2030-01-01T09:00:00Z", scheduleName? } - defer a child launch until a future time. Also accepts tasks[] or chain[]. Scheduled runs always launch async with fresh context; they become normal tracked async runs once they fire. Only schedule explicit delayed runs the user asked for.
|
|
6327
|
+
\u2022 { action: "schedule", task, description, tier|model, thinking?, permissions?, schedule: "+10m" | "2030-01-01T09:00:00Z", scheduleName? } - defer a child launch until a future time. Also accepts tasks[] or chain[]. Scheduled runs always launch async with fresh context; they become normal tracked async runs once they fire. Only schedule explicit delayed runs the user asked for.
|
|
5609
6328
|
\u2022 { action: "schedule-list" } - list scheduled runs for this session
|
|
5610
6329
|
\u2022 { action: "schedule-status", id: "..." } - inspect one scheduled run
|
|
5611
6330
|
\u2022 { action: "schedule-cancel", id: "..." } - cancel a scheduled run before it fires
|
|
@@ -5616,14 +6335,15 @@ DIAGNOSTICS:
|
|
|
5616
6335
|
${SUBAGENT_SAFETY_GUIDANCE}`, COMPACT_SUBAGENT_TOOL_DESCRIPTION = `Delegate to generic children by prompting them. Use exactly one mode per call.
|
|
5617
6336
|
|
|
5618
6337
|
EXECUTE:
|
|
5619
|
-
\u2022 SINGLE {task, description, tier, permissions?} (same-turn singles overlap); PARALLEL {tasks:[{task,description,tier,permissions?,count?,output?,reads?,progress?}], concurrency?, worktree?}; CHAIN {chain:[{task,description,tier,permissions?},{parallel:[...]}]} for sequential work.
|
|
6338
|
+
\u2022 SINGLE {task, description, tier|model, thinking?, permissions?} (same-turn singles overlap); PARALLEL {tasks:[{task,description,tier|model,thinking?,permissions?,count?,output?,reads?,progress?}], concurrency?, worktree?}; CHAIN {chain:[{task,description,tier|model,thinking?,permissions?},{parallel:[...]}]} for sequential work.
|
|
5620
6339
|
\u2022 description is required (single-line, max 80 chars, UI only). task is the full child prompt. permissions omitted = full. Plan-mode parents must pass permissions:"read-only".
|
|
5621
|
-
\u2022
|
|
6340
|
+
\u2022 Exactly one of tier:"light"|"standard"|"heavy" (default) or model:"provider/id" when the user names a model. thinking only with model. No inherit; fail closed on unavailable selections. Children always start fresh. timeoutMs/maxRuntimeMs apply to foreground and async/background runs.
|
|
6341
|
+
${SUBAGENT_TIER_GUIDANCE}
|
|
5622
6342
|
\u2022 Chain templates may use {task}, {previous}, {chain_dir}, and named outputs. Parallel worktree isolation requires a clean git repo.
|
|
5623
6343
|
\u2022 Chain example: { chain: [{task:"Analyze {task}", description:"Analyze request", tier:"standard"}, {parallel: [{task:"Check {previous}", description:"Check prior result", tier:"light", permissions:"read-only", count: 3}]}] }
|
|
5624
6344
|
|
|
5625
6345
|
CONTROL:
|
|
5626
|
-
\u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model,
|
|
6346
|
+
\u2022 Use action without execution fields: doctor, watchdog.status, watchdog.check, watchdog.recommend-model, status, interrupt, stop, resume, steer, append-step.
|
|
5627
6347
|
\u2022 Async control actions: status, interrupt, stop, resume, steer, append-step. Use stop with an id for current-session top-level async runs. Use status view:"fleet" for active-run overview, view:"transcript" to tail child output, and steer for acknowledged live guidance. Steering delivery means lunR accepted the correlated user input, not model compliance; use index for a specific child.
|
|
5628
6348
|
\u2022 Opt-in schedule actions: schedule, schedule-list, schedule-status, schedule-cancel. Schedule only explicit delayed runs the user asked for.
|
|
5629
6349
|
|
|
@@ -5647,8 +6367,8 @@ function resolveToolDescriptionMode(config, options) {
|
|
|
5647
6367
|
function customDescriptionPaths(options) {
|
|
5648
6368
|
let cwd = options?.cwd ?? process.cwd(), agentDir = options?.agentDir ?? getAgentDir();
|
|
5649
6369
|
return [
|
|
5650
|
-
|
|
5651
|
-
|
|
6370
|
+
path11.join(getProjectConfigDir(cwd), CUSTOM_TOOL_DESCRIPTION_FILE),
|
|
6371
|
+
path11.join(agentDir, CUSTOM_TOOL_DESCRIPTION_FILE)
|
|
5652
6372
|
];
|
|
5653
6373
|
}
|
|
5654
6374
|
function renderCustomTemplate(template, options) {
|
|
@@ -5671,7 +6391,7 @@ function loadCustomToolDescription(options) {
|
|
|
5671
6391
|
for (let filePath of customDescriptionPaths(options)) {
|
|
5672
6392
|
let stat;
|
|
5673
6393
|
try {
|
|
5674
|
-
stat =
|
|
6394
|
+
stat = fs9.statSync(filePath);
|
|
5675
6395
|
} catch (error) {
|
|
5676
6396
|
if (typeof error == "object" && error !== null && "code" in error && error.code === "ENOENT")
|
|
5677
6397
|
continue;
|
|
@@ -5687,7 +6407,7 @@ function loadCustomToolDescription(options) {
|
|
|
5687
6407
|
continue;
|
|
5688
6408
|
}
|
|
5689
6409
|
try {
|
|
5690
|
-
let template =
|
|
6410
|
+
let template = fs9.readFileSync(filePath, "utf-8").trim();
|
|
5691
6411
|
if (!template) {
|
|
5692
6412
|
warn(options, `Ignoring empty custom tool description '${filePath}'.`);
|
|
5693
6413
|
continue;
|
|
@@ -5726,24 +6446,24 @@ function buildSubagentToolDescription(config = {}, options) {
|
|
|
5726
6446
|
// packages/coding-agent/dist/builtin-extensions/pi-subagents/src/extension/index.js
|
|
5727
6447
|
function getSubagentSessionRoot(parentSessionFile) {
|
|
5728
6448
|
if (parentSessionFile) {
|
|
5729
|
-
let baseName =
|
|
5730
|
-
return
|
|
6449
|
+
let baseName = path12.basename(parentSessionFile, ".jsonl"), sessionsDir = path12.dirname(parentSessionFile);
|
|
6450
|
+
return path12.join(sessionsDir, baseName);
|
|
5731
6451
|
}
|
|
5732
|
-
return
|
|
6452
|
+
return fs10.mkdtempSync(path12.join(os.tmpdir(), "pi-subagent-session-"));
|
|
5733
6453
|
}
|
|
5734
6454
|
function expandTilde(p) {
|
|
5735
|
-
return p.startsWith("~/") ?
|
|
6455
|
+
return p.startsWith("~/") ? path12.join(os.homedir(), p.slice(2)) : p;
|
|
5736
6456
|
}
|
|
5737
6457
|
function ensureAccessibleDir(dirPath) {
|
|
5738
|
-
|
|
6458
|
+
fs10.mkdirSync(dirPath, { recursive: !0 });
|
|
5739
6459
|
try {
|
|
5740
|
-
|
|
6460
|
+
fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
|
|
5741
6461
|
} catch {
|
|
5742
6462
|
try {
|
|
5743
|
-
|
|
6463
|
+
fs10.rmSync(dirPath, { recursive: !0, force: !0 });
|
|
5744
6464
|
} catch {
|
|
5745
6465
|
}
|
|
5746
|
-
|
|
6466
|
+
fs10.mkdirSync(dirPath, { recursive: !0 }), fs10.accessSync(dirPath, fs10.constants.R_OK | fs10.constants.W_OK);
|
|
5747
6467
|
}
|
|
5748
6468
|
}
|
|
5749
6469
|
function isSlashResultRunning(result2) {
|
|
@@ -5758,18 +6478,25 @@ function effectiveParallelTaskCount(tasks) {
|
|
|
5758
6478
|
return total + count;
|
|
5759
6479
|
}, 0);
|
|
5760
6480
|
}
|
|
5761
|
-
function
|
|
6481
|
+
function callIsAsync(args, asyncByDefault) {
|
|
6482
|
+
return args?.clarify === !0 ? !1 : args?.async === !0 ? !0 : args?.async === !1 ? !1 : asyncByDefault === !0;
|
|
6483
|
+
}
|
|
6484
|
+
function renderSubagentCall(args, theme, context, options) {
|
|
5762
6485
|
if (args.action) {
|
|
5763
6486
|
let target = args.id || args.runId || "";
|
|
5764
6487
|
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${args.action}${target ? ` ${theme.fg("accent", target)}` : ""}`, 0, 0);
|
|
5765
6488
|
}
|
|
5766
|
-
let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks),
|
|
6489
|
+
let isParallel = (args.tasks?.length ?? 0) > 0, parallelCount = effectiveParallelTaskCount(args.tasks), asyncMark = callIsAsync(args, options?.asyncByDefault) ? `${theme.fg("warning", "async")} ` : "";
|
|
5767
6490
|
if (args.chain?.length)
|
|
5768
|
-
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}chain (${args.chain.length})
|
|
6491
|
+
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}chain (${args.chain.length})`, 0, 0);
|
|
5769
6492
|
if (isParallel)
|
|
5770
|
-
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}parallel (${parallelCount})
|
|
5771
|
-
let description = !context?.isPartial && !context?.expanded && !context?.isError ? theme.fg("accent", args.description || args.task || "?") : "";
|
|
5772
|
-
return new Text2(
|
|
6493
|
+
return new Text2(`${theme.fg("toolTitle", theme.bold("subagent "))}${asyncMark}parallel (${parallelCount})`, 0, 0);
|
|
6494
|
+
let description = !context?.result && !context?.isPartial && !context?.expanded && !context?.isError ? theme.fg("accent", args.description || args.task || "?") : "", title = theme.fg("toolTitle", theme.bold("subagent")), rest = [asyncMark.trim() ? theme.fg("warning", "async") : "", description].filter(Boolean).join(" ");
|
|
6495
|
+
return new Text2(rest ? `${title} ${rest}` : title, 0, 0);
|
|
6496
|
+
}
|
|
6497
|
+
function renderSubagentNotify(details, theme) {
|
|
6498
|
+
let status = typeof details?.status == "string" ? details.status : "", icon = status === "completed" ? theme.fg("success", "\u2713") : status === "failed" ? theme.fg("error", "\u2717") : theme.fg("warning", "\u25A0"), title = details?.agent || "child";
|
|
6499
|
+
return new Text2(`${icon} ${theme.bold(title)} ${theme.fg("dim", status)}`, 0, 0);
|
|
5773
6500
|
}
|
|
5774
6501
|
function ensureSubagentResultAnimation(context) {
|
|
5775
6502
|
let state = context.state;
|
|
@@ -5821,6 +6548,7 @@ function registerSubagentExtension(pi) {
|
|
|
5821
6548
|
let state = {
|
|
5822
6549
|
baseCwd: "",
|
|
5823
6550
|
currentSessionId: null,
|
|
6551
|
+
sessionGeneration: 0,
|
|
5824
6552
|
foregroundSubagentInFlight: 0,
|
|
5825
6553
|
subagentSpawns: { sessionId: null, count: 0 },
|
|
5826
6554
|
asyncJobs: /* @__PURE__ */ new Map(),
|
|
@@ -5842,15 +6570,19 @@ function registerSubagentExtension(pi) {
|
|
|
5842
6570
|
}
|
|
5843
6571
|
}, supervisorChannel = createNativeSupervisorChannel(pi, state), mainWatchdog = registerMainWatchdog(pi), { startResultWatcher, primeExistingResults, stopResultWatcher } = createResultWatcher(pi, state, RESULTS_DIR, 600 * 1e3);
|
|
5844
6572
|
startResultWatcher(), primeExistingResults();
|
|
5845
|
-
let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), runtimeCleanup = () => {
|
|
5846
|
-
generation++, executorPromise = void 0, pendingLaunches.clear(), mainWatchdog.dispose(), stopResultWatcher(), scheduledRunManager.stop(), supervisorChannel.dispose(), clearPendingForegroundControlNotices(state), state.poller && (clearInterval(state.poller), state.poller = null);
|
|
6573
|
+
let generation = 0, executorPromise, pendingLaunches = /* @__PURE__ */ new Set(), pendingAsyncLaunches = /* @__PURE__ */ new Set(), unregisterCancellation, runtimeCleanup = () => {
|
|
6574
|
+
generation++, supervisorChannel.cancelOwnedQuestions("parent runtime replaced"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), mainWatchdog.dispose(), stopResultWatcher(), scheduledRunManager.stop(), supervisorChannel.dispose(), clearPendingForegroundControlNotices(state), disposeSubagentWidget(), state.poller && (clearInterval(state.poller), state.poller = null);
|
|
5847
6575
|
};
|
|
5848
6576
|
globalStore[runtimeCleanupStoreKey] = runtimeCleanup;
|
|
5849
|
-
let { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
|
|
6577
|
+
let { ensurePoller, refreshWidget, handleStarted, handleComplete: trackComplete, resetJobs, restoreActiveJobs } = createAsyncJobTracker(pi, state, ASYNC_DIR, {
|
|
5850
6578
|
widgetEnabled: config.asyncWidget !== !1
|
|
5851
|
-
}),
|
|
6579
|
+
}), handleComplete = (data) => {
|
|
6580
|
+
trackComplete(data);
|
|
6581
|
+
let result2 = data;
|
|
6582
|
+
typeof state.currentSessionId == "string" && result2.sessionId !== state.currentSessionId || typeof result2.id == "string" && result2.id.trim() && supervisorChannel.settleRunQuestions(result2.id, "async run settled");
|
|
6583
|
+
}, executorExecute, scheduledRunManager = createScheduledRunManager({
|
|
5852
6584
|
config,
|
|
5853
|
-
launch: (params, ctx, signal) => executorExecute ? executorExecute(
|
|
6585
|
+
launch: (params, ctx, signal) => executorExecute ? executorExecute(randomUUID4(), params, signal, void 0, ctx) : Promise.resolve({
|
|
5854
6586
|
content: [{ type: "text", text: "Scheduled subagent launch is unavailable (executor not ready)." }],
|
|
5855
6587
|
isError: !0,
|
|
5856
6588
|
details: { mode: "management", results: [] }
|
|
@@ -5860,13 +6592,13 @@ function registerSubagentExtension(pi) {
|
|
|
5860
6592
|
let resolveLaunchReady, launchReady = !1, launchReadyPromise = new Promise((resolve3) => {
|
|
5861
6593
|
resolveLaunchReady = resolve3;
|
|
5862
6594
|
}), markLaunchReady = () => {
|
|
5863
|
-
launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), resolveLaunchReady());
|
|
6595
|
+
launchReady || (launchReady = !0, pendingLaunches.delete(launchReadyPromise), pendingAsyncLaunches.delete(launchReadyPromise), resolveLaunchReady());
|
|
5864
6596
|
};
|
|
5865
|
-
pendingLaunches.add(launchReadyPromise);
|
|
6597
|
+
pendingLaunches.add(launchReadyPromise), (params.task || params.tasks || params.chain) && !params.clarify && (params.async === !0 || params.async === void 0 && asyncByDefault || config.forceTopLevelAsync === !0) && pendingAsyncLaunches.add(launchReadyPromise);
|
|
5866
6598
|
try {
|
|
5867
6599
|
let currentGeneration = generation;
|
|
5868
6600
|
if (signal?.throwIfAborted(), !executorPromise) {
|
|
5869
|
-
let pending = import("./subagent-executor-
|
|
6601
|
+
let pending = import("./subagent-executor-SRFC26SJ.js").then(({ createSubagentExecutor }) => {
|
|
5870
6602
|
if (generation !== currentGeneration)
|
|
5871
6603
|
throw new Error("Subagent session changed before initialization");
|
|
5872
6604
|
return createSubagentExecutor({
|
|
@@ -5904,28 +6636,9 @@ function registerSubagentExtension(pi) {
|
|
|
5904
6636
|
let content = typeof message.content == "string" ? message.content : message.content.filter((entry) => entry.type === "text").map((entry) => entry.text).join(`
|
|
5905
6637
|
`);
|
|
5906
6638
|
return new Text2(content, 0, 0);
|
|
5907
|
-
}), pi.registerMessageRenderer("subagent-notify", (message,
|
|
6639
|
+
}), pi.registerMessageRenderer("subagent-notify", (message, _options, theme) => {
|
|
5908
6640
|
let content = typeof message.content == "string" ? message.content : "", details = message.details ?? parseSubagentNotifyContent(content);
|
|
5909
|
-
|
|
5910
|
-
return new Text2(content, 0, 0);
|
|
5911
|
-
let icon = details.status === "completed" ? theme.fg("success", "\u2713") : details.status === "paused" ? theme.fg("warning", "\u25A0") : theme.fg("error", "\u2717"), parts = [];
|
|
5912
|
-
details.taskInfo && parts.push(details.taskInfo), details.durationMs !== void 0 && parts.push(formatDuration(details.durationMs));
|
|
5913
|
-
let notifyLabel = details.taskInfo || details.agent || "child", text = `${icon} ${theme.bold(notifyLabel)} ${theme.fg("dim", details.status)}`;
|
|
5914
|
-
parts.length > 0 && (text += ` ${theme.fg("dim", "\xB7")} ${parts.map((part) => theme.fg("dim", part)).join(` ${theme.fg("dim", "\xB7")} `)}`);
|
|
5915
|
-
let trimmedPreview = details.resultPreview.trim(), previewLines = options.expanded ? trimmedPreview.split(`
|
|
5916
|
-
`).filter((line) => line.trim()) : [trimmedPreview.split(`
|
|
5917
|
-
`, 1)[0] ?? ""].filter((line) => line.trim());
|
|
5918
|
-
for (let line of previewLines.length > 0 ? previewLines : ["(no output)"])
|
|
5919
|
-
text += `
|
|
5920
|
-
${theme.fg("dim", `\u23BF ${line}`)}`;
|
|
5921
|
-
if (!options.expanded && trimmedPreview.includes(`
|
|
5922
|
-
`)) {
|
|
5923
|
-
let expandKey = keyText("app.tools.expand");
|
|
5924
|
-
text += `
|
|
5925
|
-
${theme.fg("dim", `${expandKey} full notification`)}`;
|
|
5926
|
-
}
|
|
5927
|
-
return details.sessionLabel && details.sessionValue && (text += `
|
|
5928
|
-
${theme.fg("muted", `${details.sessionLabel}: ${shortenPath(details.sessionValue)}`)}`), new Text2(text, 0, 0);
|
|
6641
|
+
return details ? renderSubagentNotify(details, theme) : new Text2(content, 0, 0);
|
|
5929
6642
|
}), pi.registerMessageRenderer(SUBAGENT_STEERING_MESSAGE_TYPE, (message, _options, theme) => {
|
|
5930
6643
|
let details = message.details;
|
|
5931
6644
|
if (details)
|
|
@@ -5957,7 +6670,7 @@ function registerSubagentExtension(pi) {
|
|
|
5957
6670
|
execute(id, params, signal, onUpdate, ctx) {
|
|
5958
6671
|
return executeSubagentCollapsed(id, params, signal, onUpdate, ctx);
|
|
5959
6672
|
},
|
|
5960
|
-
renderCall: renderSubagentCall,
|
|
6673
|
+
renderCall: (args, theme, context) => renderSubagentCall(args, theme, context, { asyncByDefault }),
|
|
5961
6674
|
renderResult(result2, options, theme, context) {
|
|
5962
6675
|
subagentResultIsRunning(result2) ? ensureSubagentResultAnimation(context) : clearLegacyResultAnimationTimer(context);
|
|
5963
6676
|
let state2 = context.state, entries = [];
|
|
@@ -6016,16 +6729,25 @@ function registerSubagentExtension(pi) {
|
|
|
6016
6729
|
} catch {
|
|
6017
6730
|
}
|
|
6018
6731
|
}, resetSessionState = (ctx) => {
|
|
6019
|
-
if (generation++, executorPromise = void 0, pendingLaunches.clear(), state.baseCwd = ctx.cwd, state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager), state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 }, !process.env[SUBAGENT_CHILD_ENV]) {
|
|
6020
|
-
let
|
|
6021
|
-
|
|
6022
|
-
}
|
|
6023
|
-
state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx)
|
|
6732
|
+
if (generation++, supervisorChannel.cancelOwnedQuestions("parent session replaced"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), state.baseCwd = ctx.cwd, state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager), state.subagentSpawns = { sessionId: state.currentSessionId, count: 0 }, !process.env[SUBAGENT_CHILD_ENV]) {
|
|
6733
|
+
let sessionId2 = ctx.sessionManager.getSessionId();
|
|
6734
|
+
sessionId2 && (process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId2);
|
|
6735
|
+
}
|
|
6736
|
+
state.lastUiContext = ctx, cleanupSessionArtifacts(ctx), clearPendingForegroundControlNotices(state), resetJobs(ctx), restoreActiveJobs(ctx);
|
|
6737
|
+
let sessionId = state.currentSessionId, sessionGeneration = generation;
|
|
6738
|
+
sessionId && (unregisterCancellation = registerSubagentCancellation(sessionId, createSubagentCancellation({
|
|
6739
|
+
pendingLaunches: pendingAsyncLaunches,
|
|
6740
|
+
isCurrent: () => generation === sessionGeneration && state.currentSessionId === sessionId,
|
|
6741
|
+
getActiveRunIds: () => [...state.asyncJobs.values()].filter((job) => job.sessionId === sessionId && (job.status === "queued" || job.status === "running")).map((job) => job.asyncId),
|
|
6742
|
+
async stopRun(id) {
|
|
6743
|
+
return !(await executor.execute(randomUUID4(), { action: "stop", id }, new AbortController().signal, void 0, ctx)).isError;
|
|
6744
|
+
}
|
|
6745
|
+
}))), scheduledRunManager.bindSession(ctx), restoreSlashFinalSnapshots(ctx.sessionManager.getEntries()), primeExistingResults();
|
|
6024
6746
|
};
|
|
6025
6747
|
pi.on("session_start", (_event, ctx) => {
|
|
6026
6748
|
resetSessionState(ctx), rpcBridge.emitReady(ctx), supervisorChannel.start();
|
|
6027
6749
|
}), pi.on("session_shutdown", () => {
|
|
6028
|
-
generation++, executorPromise = void 0, pendingLaunches.clear(), delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
6750
|
+
generation++, supervisorChannel.cancelOwnedQuestions("parent session shutdown"), state.sessionGeneration = (state.sessionGeneration ?? 0) + 1, executorPromise = void 0, pendingLaunches.clear(), pendingAsyncLaunches.clear(), unregisterCancellation?.(), delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
6029
6751
|
for (let unsubscribe of eventUnsubscribes)
|
|
6030
6752
|
try {
|
|
6031
6753
|
unsubscribe();
|
|
@@ -6034,7 +6756,7 @@ function registerSubagentExtension(pi) {
|
|
|
6034
6756
|
globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes && delete globalStore[eventUnsubscribeStoreKey], stopResultWatcher(), scheduledRunManager.stop(), state.poller && clearInterval(state.poller), state.poller = null, clearPendingForegroundControlNotices(state);
|
|
6035
6757
|
for (let timer of state.cleanupTimers.values())
|
|
6036
6758
|
clearTimeout(timer);
|
|
6037
|
-
state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey];
|
|
6759
|
+
state.cleanupTimers.clear(), state.asyncJobs.clear(), clearSlashSnapshots(), slashBridge.cancelAll(), slashBridge.dispose(), promptTemplateBridge.cancelAll(), promptTemplateBridge.dispose(), supervisorChannel.dispose(), globalStore[runtimeCleanupStoreKey] === runtimeCleanup && delete globalStore[runtimeCleanupStoreKey], disposeSubagentWidget();
|
|
6038
6760
|
try {
|
|
6039
6761
|
state.lastUiContext?.hasUI && state.lastUiContext.ui.setWidget(WIDGET_KEY, void 0);
|
|
6040
6762
|
} catch (error) {
|