@astralyn/pi 0.81.1-1 → 0.82.0
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 +53 -0
- package/README.md +690 -677
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.js +0 -0
- package/dist/core/agent-session.d.ts +11 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +7 -8
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +11 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +4 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +9 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/model-config.d.ts +30 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +6 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +7 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +0 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +3 -5
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +5 -10
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +114 -24
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -0
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -3
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +5 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +2 -0
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +1 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts +16 -0
- package/dist/extensions/subagent/activity.d.ts.map +1 -0
- package/dist/extensions/subagent/activity.js +150 -0
- package/dist/extensions/subagent/activity.js.map +1 -0
- package/dist/extensions/subagent/agents.d.ts +7 -0
- package/dist/extensions/subagent/agents.d.ts.map +1 -0
- package/dist/extensions/subagent/agents.js +218 -0
- package/dist/extensions/subagent/agents.js.map +1 -0
- package/dist/extensions/subagent/constants.d.ts +22 -0
- package/dist/extensions/subagent/constants.d.ts.map +1 -0
- package/dist/extensions/subagent/constants.js +32 -0
- package/dist/extensions/subagent/constants.js.map +1 -0
- package/dist/extensions/subagent/index.d.ts +3 -0
- package/dist/extensions/subagent/index.d.ts.map +1 -0
- package/dist/extensions/subagent/index.js +249 -0
- package/dist/extensions/subagent/index.js.map +1 -0
- package/dist/extensions/subagent/picker.d.ts +31 -0
- package/dist/extensions/subagent/picker.d.ts.map +1 -0
- package/dist/extensions/subagent/picker.js +116 -0
- package/dist/extensions/subagent/picker.js.map +1 -0
- package/dist/extensions/subagent/render.d.ts +8 -0
- package/dist/extensions/subagent/render.d.ts.map +1 -0
- package/dist/extensions/subagent/render.js +322 -0
- package/dist/extensions/subagent/render.js.map +1 -0
- package/dist/extensions/subagent/resolve.d.ts +16 -0
- package/dist/extensions/subagent/resolve.d.ts.map +1 -0
- package/dist/extensions/subagent/resolve.js +103 -0
- package/dist/extensions/subagent/resolve.js.map +1 -0
- package/dist/extensions/subagent/runner.d.ts +30 -0
- package/dist/extensions/subagent/runner.d.ts.map +1 -0
- package/dist/extensions/subagent/runner.js +355 -0
- package/dist/extensions/subagent/runner.js.map +1 -0
- package/dist/extensions/subagent/schema.d.ts +28 -0
- package/dist/extensions/subagent/schema.d.ts.map +1 -0
- package/dist/extensions/subagent/schema.js +33 -0
- package/dist/extensions/subagent/schema.js.map +1 -0
- package/dist/extensions/subagent/sdk-runner.d.ts +14 -0
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -0
- package/dist/extensions/subagent/sdk-runner.js +210 -0
- package/dist/extensions/subagent/sdk-runner.js.map +1 -0
- package/dist/extensions/subagent/settings.d.ts +9 -0
- package/dist/extensions/subagent/settings.d.ts.map +1 -0
- package/dist/extensions/subagent/settings.js +131 -0
- package/dist/extensions/subagent/settings.js.map +1 -0
- package/dist/extensions/subagent/types.d.ts +99 -0
- package/dist/extensions/subagent/types.d.ts.map +1 -0
- package/dist/extensions/subagent/types.js +2 -0
- package/dist/extensions/subagent/types.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +16 -46
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +2 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +25 -4
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts +16 -0
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-group.js +64 -0
- package/dist/modes/interactive/components/tool-group.js.map +1 -0
- package/dist/modes/interactive/external-editor.d.ts +12 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -0
- package/dist/modes/interactive/external-editor.js +37 -0
- package/dist/modes/interactive/external-editor.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +3 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +61 -62
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +1 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/rpc-entry.js +0 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +19 -8
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +3 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +401 -401
- package/docs/containerization.md +111 -111
- package/docs/custom-provider.md +766 -763
- package/docs/environment-variables.md +88 -0
- package/docs/extensions.md +2961 -2953
- package/docs/index.md +80 -77
- package/docs/models.md +6 -2
- package/docs/packages.md +228 -228
- package/docs/providers.md +8 -1
- package/docs/quickstart.md +165 -165
- package/docs/rpc.md +1576 -1559
- package/docs/sdk.md +1186 -1186
- package/docs/session-format.md +436 -436
- package/docs/settings.md +1 -1
- package/docs/termux.md +127 -127
- package/docs/tui.md +942 -942
- package/docs/usage.md +0 -13
- package/examples/extensions/README.md +213 -213
- package/examples/extensions/custom-compaction.ts +3 -0
- 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/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +0 -0
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/handoff.ts +9 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/summarize.ts +3 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/README.md +140 -140
- package/npm-shrinkwrap.json +16 -15
- package/package.json +10 -4
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
|
2
|
+
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
|
3
|
+
import { getMarkdownTheme } from "../../modes/interactive/theme/theme.js";
|
|
4
|
+
import { statusSummary } from "./runner.js";
|
|
5
|
+
const SINGLE_EXCERPT_LIMIT = 200;
|
|
6
|
+
const RUN_LINE_EXCERPT_LIMIT = 64;
|
|
7
|
+
const LIVE_TAIL_LIMIT = 100;
|
|
8
|
+
const COLLAPSED_RUN_LIMIT = 4;
|
|
9
|
+
const ACTIVITY_GROUPS = [
|
|
10
|
+
{ toolName: "read", verb: "read", singular: "file", plural: "files" },
|
|
11
|
+
{ toolName: "grep", verb: "searched", singular: "pattern", plural: "patterns" },
|
|
12
|
+
{ toolName: "find", verb: "searched", singular: "path pattern", plural: "path patterns" },
|
|
13
|
+
{ toolName: "ls", verb: "listed", singular: "directory", plural: "directories" },
|
|
14
|
+
{ toolName: "bash", verb: "ran", singular: "command", plural: "commands" },
|
|
15
|
+
{ toolName: "edit", verb: "edited", singular: "file", plural: "files" },
|
|
16
|
+
{ toolName: "write", verb: "wrote", singular: "file", plural: "files" },
|
|
17
|
+
];
|
|
18
|
+
function singleAgentName(args) {
|
|
19
|
+
return args.agent ?? "general";
|
|
20
|
+
}
|
|
21
|
+
function truncate(text, limit) {
|
|
22
|
+
return text.length <= limit ? text : `${text.slice(0, Math.max(0, limit - 1))}…`;
|
|
23
|
+
}
|
|
24
|
+
// Transcript excerpts render inside plain Text components, so markdown
|
|
25
|
+
// punctuation would show up literally; strip the common inline syntax.
|
|
26
|
+
function stripInlineMarkdown(text) {
|
|
27
|
+
return text
|
|
28
|
+
.replace(/```[a-zA-Z0-9-]*/gu, "")
|
|
29
|
+
.replace(/`([^`]*)`/gu, "$1")
|
|
30
|
+
.replace(/\*\*([^*]+)\*\*/gu, "$1")
|
|
31
|
+
.replace(/__([^_]+)__/gu, "$1")
|
|
32
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/gu, "$1")
|
|
33
|
+
.replace(/^#{1,6}\s+/gmu, "");
|
|
34
|
+
}
|
|
35
|
+
// Markdown tables and horizontal rules turn into symbol soup when
|
|
36
|
+
// flattened onto one line; drop those lines before inline stripping.
|
|
37
|
+
function stripBlockMarkdown(text) {
|
|
38
|
+
return text
|
|
39
|
+
.split("\n")
|
|
40
|
+
.filter((line) => {
|
|
41
|
+
const trimmed = line.trim();
|
|
42
|
+
if (trimmed.startsWith("|"))
|
|
43
|
+
return false;
|
|
44
|
+
return !(trimmed.length >= 3 && /^[-—─═=*_|:\s]+$/u.test(trimmed));
|
|
45
|
+
})
|
|
46
|
+
.join("\n");
|
|
47
|
+
}
|
|
48
|
+
function excerpt(text, limit) {
|
|
49
|
+
// finalOutput/liveText carry model-facing truncation notices from
|
|
50
|
+
// boundText/tailText; in the transcript an ellipsis is enough.
|
|
51
|
+
const cleaned = text.replace(/\[(?:Output truncated(?:: \d+ bytes omitted\.)?|Earlier output omitted\.)\]/gu, "…");
|
|
52
|
+
return truncate(stripInlineMarkdown(stripBlockMarkdown(cleaned)).replace(/\s+/gu, " ").trim(), limit);
|
|
53
|
+
}
|
|
54
|
+
function liveTail(run) {
|
|
55
|
+
const lines = run.liveText
|
|
56
|
+
.split("\n")
|
|
57
|
+
.map((line) => line.trim())
|
|
58
|
+
.filter(Boolean);
|
|
59
|
+
const last = lines.at(-1);
|
|
60
|
+
return last ? excerpt(last, LIVE_TAIL_LIMIT) : undefined;
|
|
61
|
+
}
|
|
62
|
+
function statusMarker(status, theme) {
|
|
63
|
+
switch (status) {
|
|
64
|
+
case "completed":
|
|
65
|
+
return theme.fg("success", "✓");
|
|
66
|
+
case "failed":
|
|
67
|
+
return theme.fg("error", "×");
|
|
68
|
+
case "aborted":
|
|
69
|
+
return theme.fg("warning", "■");
|
|
70
|
+
case "running":
|
|
71
|
+
return theme.fg("accent", "●");
|
|
72
|
+
default:
|
|
73
|
+
return theme.fg("muted", "○");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function runTitle(run, theme, mode) {
|
|
77
|
+
const step = mode === "chain" && run.step ? theme.fg("muted", `${run.step}. `) : "";
|
|
78
|
+
return `${statusMarker(run.status, theme)} ${step}${theme.fg("accent", run.agent)}${theme.fg("dim", ` · ${truncate(run.description, 48)}`)}`;
|
|
79
|
+
}
|
|
80
|
+
function runProgressText(run) {
|
|
81
|
+
const items = [`${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? "" : "s"}`];
|
|
82
|
+
if (run.usage.totalTokens)
|
|
83
|
+
items.push(`${formatTokens(run.usage.totalTokens)} tokens`);
|
|
84
|
+
return items.join(" · ");
|
|
85
|
+
}
|
|
86
|
+
function runLine(run, theme, mode) {
|
|
87
|
+
let line = runTitle(run, theme, mode);
|
|
88
|
+
const detail = run.status === "running"
|
|
89
|
+
? run.currentActivity
|
|
90
|
+
: run.status === "failed" || run.status === "aborted"
|
|
91
|
+
? run.error && excerpt(run.error, RUN_LINE_EXCERPT_LIMIT)
|
|
92
|
+
: run.finalOutput && excerpt(run.finalOutput, RUN_LINE_EXCERPT_LIMIT);
|
|
93
|
+
if (detail)
|
|
94
|
+
line += theme.fg(run.status === "failed" ? "error" : "dim", ` — ${detail}`);
|
|
95
|
+
if (run.status === "running")
|
|
96
|
+
line += theme.fg("dim", ` · ${runProgressText(run)}`);
|
|
97
|
+
const settled = run.status !== "running" && run.status !== "queued";
|
|
98
|
+
if (settled && run.startedAt !== undefined && run.endedAt !== undefined) {
|
|
99
|
+
line += theme.fg("dim", ` · ${formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000))}`);
|
|
100
|
+
}
|
|
101
|
+
return line;
|
|
102
|
+
}
|
|
103
|
+
// Keep active work visible while a batch is in flight: completed runs
|
|
104
|
+
// may otherwise crowd a still-running task out of the collapsed window.
|
|
105
|
+
function selectCollapsedRuns(runs, isPartial) {
|
|
106
|
+
if (runs.length <= COLLAPSED_RUN_LIMIT)
|
|
107
|
+
return runs;
|
|
108
|
+
if (!isPartial)
|
|
109
|
+
return runs.slice(0, COLLAPSED_RUN_LIMIT);
|
|
110
|
+
const active = runs.filter((run) => run.status === "running" || run.status === "queued");
|
|
111
|
+
const settled = runs.filter((run) => run.status !== "running" && run.status !== "queued");
|
|
112
|
+
return [...active, ...settled].slice(0, COLLAPSED_RUN_LIMIT);
|
|
113
|
+
}
|
|
114
|
+
function singleCollapsedLines(details, theme) {
|
|
115
|
+
const run = details.runs[0];
|
|
116
|
+
if (!run)
|
|
117
|
+
return [theme.fg("muted", "Initializing…")];
|
|
118
|
+
if (run.status === "running" || run.status === "queued") {
|
|
119
|
+
const isInitializing = run.status === "running" && run.usage.turns === 0 && run.usage.toolUses === 0 && run.liveText.length === 0;
|
|
120
|
+
const lines = [
|
|
121
|
+
`${statusMarker(run.status, theme)} ${theme.fg("dim", run.currentActivity ?? (run.status === "queued" ? "queued" : isInitializing ? "Initializing…" : "Thinking…"))}`,
|
|
122
|
+
];
|
|
123
|
+
const tail = liveTail(run);
|
|
124
|
+
if (tail)
|
|
125
|
+
lines.push(theme.fg("dim", tail));
|
|
126
|
+
return lines;
|
|
127
|
+
}
|
|
128
|
+
if (run.error)
|
|
129
|
+
return [theme.fg("error", excerpt(run.error, SINGLE_EXCERPT_LIMIT))];
|
|
130
|
+
if (run.finalOutput)
|
|
131
|
+
return [theme.fg("toolOutput", excerpt(run.finalOutput, SINGLE_EXCERPT_LIMIT))];
|
|
132
|
+
return [theme.fg("muted", run.status)];
|
|
133
|
+
}
|
|
134
|
+
function activitySummaryText(details) {
|
|
135
|
+
const toolCounts = new Map();
|
|
136
|
+
let activityCount = 0;
|
|
137
|
+
for (const run of details.runs) {
|
|
138
|
+
for (const activity of run.activities) {
|
|
139
|
+
activityCount++;
|
|
140
|
+
toolCounts.set(activity.toolName, (toolCounts.get(activity.toolName) ?? 0) + 1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const items = [];
|
|
144
|
+
let groupedCount = 0;
|
|
145
|
+
for (const group of ACTIVITY_GROUPS) {
|
|
146
|
+
const count = toolCounts.get(group.toolName) ?? 0;
|
|
147
|
+
if (count === 0)
|
|
148
|
+
continue;
|
|
149
|
+
groupedCount += count;
|
|
150
|
+
items.push(`${group.verb} ${count} ${count === 1 ? group.singular : group.plural}`);
|
|
151
|
+
}
|
|
152
|
+
const otherCount = activityCount - groupedCount;
|
|
153
|
+
if (otherCount > 0)
|
|
154
|
+
items.push(`used ${otherCount} other tool${otherCount === 1 ? "" : "s"}`);
|
|
155
|
+
if (items.length === 0)
|
|
156
|
+
return "";
|
|
157
|
+
const earlierCount = Math.max(0, details.usage.toolUses - activityCount);
|
|
158
|
+
if (earlierCount > 0)
|
|
159
|
+
items.push(`${earlierCount} earlier tool use${earlierCount === 1 ? "" : "s"}`);
|
|
160
|
+
return `${items[0][0].toUpperCase()}${items[0].slice(1)}${items
|
|
161
|
+
.slice(1)
|
|
162
|
+
.map((item) => ` · ${item}`)
|
|
163
|
+
.join("")}`;
|
|
164
|
+
}
|
|
165
|
+
function usageText(details, includeToolUses = true) {
|
|
166
|
+
const usage = details.usage;
|
|
167
|
+
const items = [];
|
|
168
|
+
if (includeToolUses && usage.toolUses)
|
|
169
|
+
items.push(`${usage.toolUses} tool use${usage.toolUses === 1 ? "" : "s"}`);
|
|
170
|
+
if (usage.turns)
|
|
171
|
+
items.push(`${usage.turns} turn${usage.turns === 1 ? "" : "s"}`);
|
|
172
|
+
if (usage.output)
|
|
173
|
+
items.push(`↓${formatTokens(usage.output)}`);
|
|
174
|
+
if (usage.cost)
|
|
175
|
+
items.push(`$${usage.cost.toFixed(3)}`);
|
|
176
|
+
const duration = details.endedAt ? Math.max(0, (details.endedAt - details.startedAt) / 1000) : undefined;
|
|
177
|
+
if (duration !== undefined)
|
|
178
|
+
items.push(formatDuration(duration));
|
|
179
|
+
return items.join(" · ");
|
|
180
|
+
}
|
|
181
|
+
function formatTokens(value) {
|
|
182
|
+
if (value < 1_000)
|
|
183
|
+
return String(value);
|
|
184
|
+
if (value < 1_000_000)
|
|
185
|
+
return `${(value / 1_000).toFixed(value < 10_000 ? 1 : 0)}k`;
|
|
186
|
+
return `${(value / 1_000_000).toFixed(1)}M`;
|
|
187
|
+
}
|
|
188
|
+
function formatDuration(seconds) {
|
|
189
|
+
if (seconds >= 90)
|
|
190
|
+
return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;
|
|
191
|
+
return `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;
|
|
192
|
+
}
|
|
193
|
+
// A single run's title and usage would duplicate the call header and
|
|
194
|
+
// the call-level usage line, so `single` drops both and folds the
|
|
195
|
+
// duration into the metadata line instead.
|
|
196
|
+
function renderRunDetails(run, theme, mode, single) {
|
|
197
|
+
const container = new Container();
|
|
198
|
+
const duration = run.startedAt !== undefined && run.endedAt !== undefined
|
|
199
|
+
? formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000))
|
|
200
|
+
: undefined;
|
|
201
|
+
if (!single) {
|
|
202
|
+
let title = runTitle(run, theme, mode);
|
|
203
|
+
if (run.status === "running")
|
|
204
|
+
title += theme.fg("dim", ` · ${runProgressText(run)}`);
|
|
205
|
+
else if (duration)
|
|
206
|
+
title += theme.fg("dim", ` · ${duration}`);
|
|
207
|
+
container.addChild(new Text(title, 0, 0));
|
|
208
|
+
}
|
|
209
|
+
container.addChild(new Text(theme.fg("muted", "Task"), 0, 0));
|
|
210
|
+
container.addChild(new Text(theme.fg("dim", run.prompt), 0, 0));
|
|
211
|
+
if (run.activities.length > 0) {
|
|
212
|
+
container.addChild(new Spacer(1));
|
|
213
|
+
container.addChild(new Text(theme.fg("muted", "Activity"), 0, 0));
|
|
214
|
+
for (const activity of run.activities) {
|
|
215
|
+
const marker = activity.status === "succeeded"
|
|
216
|
+
? theme.fg("success", "✓")
|
|
217
|
+
: activity.status === "failed"
|
|
218
|
+
? theme.fg("error", "×")
|
|
219
|
+
: theme.fg("accent", "●");
|
|
220
|
+
let text = `${marker} ${theme.fg("toolOutput", excerpt(activity.summary, 96))}`;
|
|
221
|
+
if (activity.resultSummary)
|
|
222
|
+
text += ` ${theme.fg("dim", `· ${excerpt(activity.resultSummary, 64)}`)}`;
|
|
223
|
+
container.addChild(new Text(text, 0, 0));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (run.error) {
|
|
227
|
+
container.addChild(new Spacer(1));
|
|
228
|
+
container.addChild(new Text(theme.fg("error", `Error: ${run.error}`), 0, 0));
|
|
229
|
+
}
|
|
230
|
+
if (run.finalOutput) {
|
|
231
|
+
container.addChild(new Spacer(1));
|
|
232
|
+
container.addChild(new Text(theme.fg("muted", "Response"), 0, 0));
|
|
233
|
+
container.addChild(new Markdown(run.finalOutput, 0, 0, getMarkdownTheme()));
|
|
234
|
+
}
|
|
235
|
+
const metadata = [
|
|
236
|
+
`${run.model} · ${run.thinking}`,
|
|
237
|
+
run.usage.toolUses ? `${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? "" : "s"}` : undefined,
|
|
238
|
+
run.usage.turns ? `${run.usage.turns} turn${run.usage.turns === 1 ? "" : "s"}` : undefined,
|
|
239
|
+
run.usage.output ? `↓${formatTokens(run.usage.output)}` : undefined,
|
|
240
|
+
run.usage.cost ? `$${run.usage.cost.toFixed(3)}` : undefined,
|
|
241
|
+
single ? duration : undefined,
|
|
242
|
+
]
|
|
243
|
+
.filter((item) => Boolean(item))
|
|
244
|
+
.join(" · ");
|
|
245
|
+
if (metadata) {
|
|
246
|
+
container.addChild(new Spacer(1));
|
|
247
|
+
container.addChild(new Text(theme.fg("dim", metadata), 0, 0));
|
|
248
|
+
}
|
|
249
|
+
return container;
|
|
250
|
+
}
|
|
251
|
+
// The call header stays a single line: run rows in the result area take
|
|
252
|
+
// over within the first update and carry richer per-task state, so a
|
|
253
|
+
// task list here would render everything twice.
|
|
254
|
+
export function renderSubagentCall(args, theme) {
|
|
255
|
+
let text = theme.fg("toolTitle", theme.bold("Subagent "));
|
|
256
|
+
const modes = [args.prompt != null && "prompt", args.tasks != null && "tasks", args.chain != null && "chain"].filter((mode) => Boolean(mode));
|
|
257
|
+
if (modes.length > 1) {
|
|
258
|
+
text += theme.fg("error", `invalid · ${modes.join(" + ")}`);
|
|
259
|
+
}
|
|
260
|
+
else if (args.tasks) {
|
|
261
|
+
text += theme.fg("accent", `parallel · ${args.tasks.length} tasks`);
|
|
262
|
+
}
|
|
263
|
+
else if (args.chain) {
|
|
264
|
+
text += theme.fg("accent", `chain · ${args.chain.length} steps`);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
text += theme.fg("accent", singleAgentName(args));
|
|
268
|
+
if (args.description)
|
|
269
|
+
text += theme.fg("dim", ` · ${truncate(args.description, 72)}`);
|
|
270
|
+
}
|
|
271
|
+
return new Text(text, 0, 0);
|
|
272
|
+
}
|
|
273
|
+
export function renderSubagentResult(result, options, theme, isError) {
|
|
274
|
+
const details = result.details;
|
|
275
|
+
if (!details) {
|
|
276
|
+
const text = result.content.find((part) => part.type === "text");
|
|
277
|
+
return new Text(text?.type === "text" ? text.text : "(no output)", 0, 0);
|
|
278
|
+
}
|
|
279
|
+
if (!options.expanded) {
|
|
280
|
+
const lines = [];
|
|
281
|
+
if (details.mode === "single") {
|
|
282
|
+
lines.push(...singleCollapsedLines(details, theme));
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
lines.push(theme.fg(isError ? "error" : options.isPartial ? "accent" : "muted", statusSummary(details)));
|
|
286
|
+
const shown = selectCollapsedRuns(details.runs, Boolean(options.isPartial));
|
|
287
|
+
for (const run of shown)
|
|
288
|
+
lines.push(runLine(run, theme, details.mode));
|
|
289
|
+
const hidden = details.runs.length - shown.length;
|
|
290
|
+
if (hidden > 0)
|
|
291
|
+
lines.push(theme.fg("muted", `+${hidden} more`));
|
|
292
|
+
}
|
|
293
|
+
const activitySummary = activitySummaryText(details);
|
|
294
|
+
if (activitySummary)
|
|
295
|
+
lines.push(theme.fg("dim", activitySummary));
|
|
296
|
+
const usage = usageText(details, !activitySummary);
|
|
297
|
+
if (usage)
|
|
298
|
+
lines.push(theme.fg("dim", usage));
|
|
299
|
+
if (!options.isPartial)
|
|
300
|
+
lines.push(theme.fg("muted", `(${keyHint("app.tools.expand", "to expand")})`));
|
|
301
|
+
return new Text(lines.join("\n"), 0, 0);
|
|
302
|
+
}
|
|
303
|
+
const container = new Container();
|
|
304
|
+
const single = details.mode === "single" && details.runs.length === 1;
|
|
305
|
+
if (!single) {
|
|
306
|
+
container.addChild(new Text(theme.fg(isError ? "error" : "toolTitle", statusSummary(details)), 0, 0));
|
|
307
|
+
}
|
|
308
|
+
details.runs.forEach((run, index) => {
|
|
309
|
+
if (!single || index > 0)
|
|
310
|
+
container.addChild(new Spacer(1));
|
|
311
|
+
container.addChild(renderRunDetails(run, theme, details.mode, single));
|
|
312
|
+
});
|
|
313
|
+
if (!single) {
|
|
314
|
+
const usage = usageText(details);
|
|
315
|
+
if (usage) {
|
|
316
|
+
container.addChild(new Spacer(1));
|
|
317
|
+
container.addChild(new Text(theme.fg("dim", usage), 0, 0));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return container;
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/extensions/subagent/render.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, Container, Markdown, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport type { AgentToolResult, ToolRenderResultOptions } from \"../../core/extensions/types.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getMarkdownTheme, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { statusSummary } from \"./runner.ts\";\nimport type { SubagentParams } from \"./schema.ts\";\nimport type { SubagentDetails, SubagentRunDetails, SubagentRunStatus } from \"./types.ts\";\n\nconst SINGLE_EXCERPT_LIMIT = 200;\nconst RUN_LINE_EXCERPT_LIMIT = 64;\nconst LIVE_TAIL_LIMIT = 100;\nconst COLLAPSED_RUN_LIMIT = 4;\n\nconst ACTIVITY_GROUPS = [\n\t{ toolName: \"read\", verb: \"read\", singular: \"file\", plural: \"files\" },\n\t{ toolName: \"grep\", verb: \"searched\", singular: \"pattern\", plural: \"patterns\" },\n\t{ toolName: \"find\", verb: \"searched\", singular: \"path pattern\", plural: \"path patterns\" },\n\t{ toolName: \"ls\", verb: \"listed\", singular: \"directory\", plural: \"directories\" },\n\t{ toolName: \"bash\", verb: \"ran\", singular: \"command\", plural: \"commands\" },\n\t{ toolName: \"edit\", verb: \"edited\", singular: \"file\", plural: \"files\" },\n\t{ toolName: \"write\", verb: \"wrote\", singular: \"file\", plural: \"files\" },\n] as const;\n\nfunction singleAgentName(args: { agent?: string | null }): string {\n\treturn args.agent ?? \"general\";\n}\n\nfunction truncate(text: string, limit: number): string {\n\treturn text.length <= limit ? text : `${text.slice(0, Math.max(0, limit - 1))}…`;\n}\n\n// Transcript excerpts render inside plain Text components, so markdown\n// punctuation would show up literally; strip the common inline syntax.\nfunction stripInlineMarkdown(text: string): string {\n\treturn text\n\t\t.replace(/```[a-zA-Z0-9-]*/gu, \"\")\n\t\t.replace(/`([^`]*)`/gu, \"$1\")\n\t\t.replace(/\\*\\*([^*]+)\\*\\*/gu, \"$1\")\n\t\t.replace(/__([^_]+)__/gu, \"$1\")\n\t\t.replace(/\\[([^\\]]+)\\]\\([^)]*\\)/gu, \"$1\")\n\t\t.replace(/^#{1,6}\\s+/gmu, \"\");\n}\n\n// Markdown tables and horizontal rules turn into symbol soup when\n// flattened onto one line; drop those lines before inline stripping.\nfunction stripBlockMarkdown(text: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.filter((line) => {\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (trimmed.startsWith(\"|\")) return false;\n\t\t\treturn !(trimmed.length >= 3 && /^[-—─═=*_|:\\s]+$/u.test(trimmed));\n\t\t})\n\t\t.join(\"\\n\");\n}\n\nfunction excerpt(text: string, limit: number): string {\n\t// finalOutput/liveText carry model-facing truncation notices from\n\t// boundText/tailText; in the transcript an ellipsis is enough.\n\tconst cleaned = text.replace(/\\[(?:Output truncated(?:: \\d+ bytes omitted\\.)?|Earlier output omitted\\.)\\]/gu, \"…\");\n\treturn truncate(stripInlineMarkdown(stripBlockMarkdown(cleaned)).replace(/\\s+/gu, \" \").trim(), limit);\n}\n\nfunction liveTail(run: SubagentRunDetails): string | undefined {\n\tconst lines = run.liveText\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n\tconst last = lines.at(-1);\n\treturn last ? excerpt(last, LIVE_TAIL_LIMIT) : undefined;\n}\n\nfunction statusMarker(status: SubagentRunStatus, theme: Theme): string {\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn theme.fg(\"success\", \"✓\");\n\t\tcase \"failed\":\n\t\t\treturn theme.fg(\"error\", \"×\");\n\t\tcase \"aborted\":\n\t\t\treturn theme.fg(\"warning\", \"■\");\n\t\tcase \"running\":\n\t\t\treturn theme.fg(\"accent\", \"●\");\n\t\tdefault:\n\t\t\treturn theme.fg(\"muted\", \"○\");\n\t}\n}\n\nfunction runTitle(run: SubagentRunDetails, theme: Theme, mode: SubagentDetails[\"mode\"]): string {\n\tconst step = mode === \"chain\" && run.step ? theme.fg(\"muted\", `${run.step}. `) : \"\";\n\treturn `${statusMarker(run.status, theme)} ${step}${theme.fg(\"accent\", run.agent)}${theme.fg(\"dim\", ` · ${truncate(run.description, 48)}`)}`;\n}\n\nfunction runProgressText(run: SubagentRunDetails): string {\n\tconst items = [`${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? \"\" : \"s\"}`];\n\tif (run.usage.totalTokens) items.push(`${formatTokens(run.usage.totalTokens)} tokens`);\n\treturn items.join(\" · \");\n}\n\nfunction runLine(run: SubagentRunDetails, theme: Theme, mode: SubagentDetails[\"mode\"]): string {\n\tlet line = runTitle(run, theme, mode);\n\tconst detail =\n\t\trun.status === \"running\"\n\t\t\t? run.currentActivity\n\t\t\t: run.status === \"failed\" || run.status === \"aborted\"\n\t\t\t\t? run.error && excerpt(run.error, RUN_LINE_EXCERPT_LIMIT)\n\t\t\t\t: run.finalOutput && excerpt(run.finalOutput, RUN_LINE_EXCERPT_LIMIT);\n\tif (detail) line += theme.fg(run.status === \"failed\" ? \"error\" : \"dim\", ` — ${detail}`);\n\tif (run.status === \"running\") line += theme.fg(\"dim\", ` · ${runProgressText(run)}`);\n\tconst settled = run.status !== \"running\" && run.status !== \"queued\";\n\tif (settled && run.startedAt !== undefined && run.endedAt !== undefined) {\n\t\tline += theme.fg(\"dim\", ` · ${formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000))}`);\n\t}\n\treturn line;\n}\n\n// Keep active work visible while a batch is in flight: completed runs\n// may otherwise crowd a still-running task out of the collapsed window.\nfunction selectCollapsedRuns(runs: SubagentRunDetails[], isPartial: boolean): SubagentRunDetails[] {\n\tif (runs.length <= COLLAPSED_RUN_LIMIT) return runs;\n\tif (!isPartial) return runs.slice(0, COLLAPSED_RUN_LIMIT);\n\tconst active = runs.filter((run) => run.status === \"running\" || run.status === \"queued\");\n\tconst settled = runs.filter((run) => run.status !== \"running\" && run.status !== \"queued\");\n\treturn [...active, ...settled].slice(0, COLLAPSED_RUN_LIMIT);\n}\n\nfunction singleCollapsedLines(details: SubagentDetails, theme: Theme): string[] {\n\tconst run = details.runs[0];\n\tif (!run) return [theme.fg(\"muted\", \"Initializing…\")];\n\tif (run.status === \"running\" || run.status === \"queued\") {\n\t\tconst isInitializing =\n\t\t\trun.status === \"running\" && run.usage.turns === 0 && run.usage.toolUses === 0 && run.liveText.length === 0;\n\t\tconst lines = [\n\t\t\t`${statusMarker(run.status, theme)} ${theme.fg(\"dim\", run.currentActivity ?? (run.status === \"queued\" ? \"queued\" : isInitializing ? \"Initializing…\" : \"Thinking…\"))}`,\n\t\t];\n\t\tconst tail = liveTail(run);\n\t\tif (tail) lines.push(theme.fg(\"dim\", tail));\n\t\treturn lines;\n\t}\n\tif (run.error) return [theme.fg(\"error\", excerpt(run.error, SINGLE_EXCERPT_LIMIT))];\n\tif (run.finalOutput) return [theme.fg(\"toolOutput\", excerpt(run.finalOutput, SINGLE_EXCERPT_LIMIT))];\n\treturn [theme.fg(\"muted\", run.status)];\n}\n\nfunction activitySummaryText(details: SubagentDetails): string {\n\tconst toolCounts = new Map<string, number>();\n\tlet activityCount = 0;\n\tfor (const run of details.runs) {\n\t\tfor (const activity of run.activities) {\n\t\t\tactivityCount++;\n\t\t\ttoolCounts.set(activity.toolName, (toolCounts.get(activity.toolName) ?? 0) + 1);\n\t\t}\n\t}\n\tconst items: string[] = [];\n\tlet groupedCount = 0;\n\tfor (const group of ACTIVITY_GROUPS) {\n\t\tconst count = toolCounts.get(group.toolName) ?? 0;\n\t\tif (count === 0) continue;\n\t\tgroupedCount += count;\n\t\titems.push(`${group.verb} ${count} ${count === 1 ? group.singular : group.plural}`);\n\t}\n\tconst otherCount = activityCount - groupedCount;\n\tif (otherCount > 0) items.push(`used ${otherCount} other tool${otherCount === 1 ? \"\" : \"s\"}`);\n\tif (items.length === 0) return \"\";\n\tconst earlierCount = Math.max(0, details.usage.toolUses - activityCount);\n\tif (earlierCount > 0) items.push(`${earlierCount} earlier tool use${earlierCount === 1 ? \"\" : \"s\"}`);\n\treturn `${items[0]![0]!.toUpperCase()}${items[0]!.slice(1)}${items\n\t\t.slice(1)\n\t\t.map((item) => ` · ${item}`)\n\t\t.join(\"\")}`;\n}\n\nfunction usageText(details: SubagentDetails, includeToolUses = true): string {\n\tconst usage = details.usage;\n\tconst items: string[] = [];\n\tif (includeToolUses && usage.toolUses) items.push(`${usage.toolUses} tool use${usage.toolUses === 1 ? \"\" : \"s\"}`);\n\tif (usage.turns) items.push(`${usage.turns} turn${usage.turns === 1 ? \"\" : \"s\"}`);\n\tif (usage.output) items.push(`↓${formatTokens(usage.output)}`);\n\tif (usage.cost) items.push(`$${usage.cost.toFixed(3)}`);\n\tconst duration = details.endedAt ? Math.max(0, (details.endedAt - details.startedAt) / 1000) : undefined;\n\tif (duration !== undefined) items.push(formatDuration(duration));\n\treturn items.join(\" · \");\n}\n\nfunction formatTokens(value: number): string {\n\tif (value < 1_000) return String(value);\n\tif (value < 1_000_000) return `${(value / 1_000).toFixed(value < 10_000 ? 1 : 0)}k`;\n\treturn `${(value / 1_000_000).toFixed(1)}M`;\n}\n\nfunction formatDuration(seconds: number): string {\n\tif (seconds >= 90) return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;\n\treturn `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;\n}\n\n// A single run's title and usage would duplicate the call header and\n// the call-level usage line, so `single` drops both and folds the\n// duration into the metadata line instead.\nfunction renderRunDetails(\n\trun: SubagentRunDetails,\n\ttheme: Theme,\n\tmode: SubagentDetails[\"mode\"],\n\tsingle: boolean,\n): Component {\n\tconst container = new Container();\n\tconst duration =\n\t\trun.startedAt !== undefined && run.endedAt !== undefined\n\t\t\t? formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000))\n\t\t\t: undefined;\n\tif (!single) {\n\t\tlet title = runTitle(run, theme, mode);\n\t\tif (run.status === \"running\") title += theme.fg(\"dim\", ` · ${runProgressText(run)}`);\n\t\telse if (duration) title += theme.fg(\"dim\", ` · ${duration}`);\n\t\tcontainer.addChild(new Text(title, 0, 0));\n\t}\n\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Task\"), 0, 0));\n\tcontainer.addChild(new Text(theme.fg(\"dim\", run.prompt), 0, 0));\n\tif (run.activities.length > 0) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Activity\"), 0, 0));\n\t\tfor (const activity of run.activities) {\n\t\t\tconst marker =\n\t\t\t\tactivity.status === \"succeeded\"\n\t\t\t\t\t? theme.fg(\"success\", \"✓\")\n\t\t\t\t\t: activity.status === \"failed\"\n\t\t\t\t\t\t? theme.fg(\"error\", \"×\")\n\t\t\t\t\t\t: theme.fg(\"accent\", \"●\");\n\t\t\tlet text = `${marker} ${theme.fg(\"toolOutput\", excerpt(activity.summary, 96))}`;\n\t\t\tif (activity.resultSummary) text += ` ${theme.fg(\"dim\", `· ${excerpt(activity.resultSummary, 64)}`)}`;\n\t\t\tcontainer.addChild(new Text(text, 0, 0));\n\t\t}\n\t}\n\tif (run.error) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"error\", `Error: ${run.error}`), 0, 0));\n\t}\n\tif (run.finalOutput) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Response\"), 0, 0));\n\t\tcontainer.addChild(new Markdown(run.finalOutput, 0, 0, getMarkdownTheme()));\n\t}\n\tconst metadata = [\n\t\t`${run.model} · ${run.thinking}`,\n\t\trun.usage.toolUses ? `${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? \"\" : \"s\"}` : undefined,\n\t\trun.usage.turns ? `${run.usage.turns} turn${run.usage.turns === 1 ? \"\" : \"s\"}` : undefined,\n\t\trun.usage.output ? `↓${formatTokens(run.usage.output)}` : undefined,\n\t\trun.usage.cost ? `$${run.usage.cost.toFixed(3)}` : undefined,\n\t\tsingle ? duration : undefined,\n\t]\n\t\t.filter((item): item is string => Boolean(item))\n\t\t.join(\" · \");\n\tif (metadata) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"dim\", metadata), 0, 0));\n\t}\n\treturn container;\n}\n\n// The call header stays a single line: run rows in the result area take\n// over within the first update and carry richer per-task state, so a\n// task list here would render everything twice.\nexport function renderSubagentCall(args: SubagentParams, theme: Theme): Component {\n\tlet text = theme.fg(\"toolTitle\", theme.bold(\"Subagent \"));\n\tconst modes = [args.prompt != null && \"prompt\", args.tasks != null && \"tasks\", args.chain != null && \"chain\"].filter(\n\t\t(mode): mode is string => Boolean(mode),\n\t);\n\tif (modes.length > 1) {\n\t\ttext += theme.fg(\"error\", `invalid · ${modes.join(\" + \")}`);\n\t} else if (args.tasks) {\n\t\ttext += theme.fg(\"accent\", `parallel · ${args.tasks.length} tasks`);\n\t} else if (args.chain) {\n\t\ttext += theme.fg(\"accent\", `chain · ${args.chain.length} steps`);\n\t} else {\n\t\ttext += theme.fg(\"accent\", singleAgentName(args));\n\t\tif (args.description) text += theme.fg(\"dim\", ` · ${truncate(args.description, 72)}`);\n\t}\n\treturn new Text(text, 0, 0);\n}\n\nexport function renderSubagentResult(\n\tresult: AgentToolResult<SubagentDetails>,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tisError: boolean,\n): Component {\n\tconst details = result.details;\n\tif (!details) {\n\t\tconst text = result.content.find((part) => part.type === \"text\");\n\t\treturn new Text(text?.type === \"text\" ? text.text : \"(no output)\", 0, 0);\n\t}\n\tif (!options.expanded) {\n\t\tconst lines: string[] = [];\n\t\tif (details.mode === \"single\") {\n\t\t\tlines.push(...singleCollapsedLines(details, theme));\n\t\t} else {\n\t\t\tlines.push(theme.fg(isError ? \"error\" : options.isPartial ? \"accent\" : \"muted\", statusSummary(details)));\n\t\t\tconst shown = selectCollapsedRuns(details.runs, Boolean(options.isPartial));\n\t\t\tfor (const run of shown) lines.push(runLine(run, theme, details.mode));\n\t\t\tconst hidden = details.runs.length - shown.length;\n\t\t\tif (hidden > 0) lines.push(theme.fg(\"muted\", `+${hidden} more`));\n\t\t}\n\t\tconst activitySummary = activitySummaryText(details);\n\t\tif (activitySummary) lines.push(theme.fg(\"dim\", activitySummary));\n\t\tconst usage = usageText(details, !activitySummary);\n\t\tif (usage) lines.push(theme.fg(\"dim\", usage));\n\t\tif (!options.isPartial) lines.push(theme.fg(\"muted\", `(${keyHint(\"app.tools.expand\", \"to expand\")})`));\n\t\treturn new Text(lines.join(\"\\n\"), 0, 0);\n\t}\n\tconst container = new Container();\n\tconst single = details.mode === \"single\" && details.runs.length === 1;\n\tif (!single) {\n\t\tcontainer.addChild(new Text(theme.fg(isError ? \"error\" : \"toolTitle\", statusSummary(details)), 0, 0));\n\t}\n\tdetails.runs.forEach((run, index) => {\n\t\tif (!single || index > 0) container.addChild(new Spacer(1));\n\t\tcontainer.addChild(renderRunDetails(run, theme, details.mode, single));\n\t});\n\tif (!single) {\n\t\tconst usage = usageText(details);\n\t\tif (usage) {\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(theme.fg(\"dim\", usage), 0, 0));\n\t\t}\n\t}\n\treturn container;\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { type Api, type Model } from "@earendil-works/pi-ai/compat";
|
|
3
|
+
import type { ModelRegistry } from "../../core/model-registry.ts";
|
|
4
|
+
import type { SubagentTask } from "./schema.ts";
|
|
5
|
+
import { loadSubagentConfig } from "./settings.ts";
|
|
6
|
+
import type { AgentDefinition, ResolvedSubagentTask } from "./types.ts";
|
|
7
|
+
export interface ParentModelContext {
|
|
8
|
+
model: Model<Api> | undefined;
|
|
9
|
+
thinking: ThinkingLevel;
|
|
10
|
+
modelRegistry: Pick<ModelRegistry, "find" | "getAvailable" | "hasConfiguredAuth">;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveTaskCwd(parentCwd: string, requestedCwd: string | undefined): string;
|
|
13
|
+
export declare function resolveSubagentTask(task: SubagentTask, parentCwd: string, agents: readonly AgentDefinition[], parent: ParentModelContext, configAgentDir?: string): Promise<ResolvedSubagentTask>;
|
|
14
|
+
export declare function loadProfileOverrides(agentDir?: string): Promise<Awaited<ReturnType<typeof loadSubagentConfig>>>;
|
|
15
|
+
export declare function modelLabel(model: Model<Api>): string;
|
|
16
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/resolve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,KAAK,GAAG,EAAsB,KAAK,KAAK,EAAE,MAAM,8BAA8B,CAAC;AAExF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAA2B,MAAM,YAAY,CAAC;AAEjG,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,aAAa,CAAC;IACxB,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,GAAG,mBAAmB,CAAC,CAAC;CAClF;AAoBD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAc1F;AA4CD,wBAAsB,mBAAmB,CACxC,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,eAAe,EAAE,EAClC,MAAM,EAAE,kBAAkB,EAC1B,cAAc,SAAgB,GAC5B,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED,wBAAsB,oBAAoB,CACzC,QAAQ,SAAgB,GACtB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAEzD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAEpD","sourcesContent":["import { existsSync, realpathSync, statSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport { type Api, clampThinkingLevel, type Model } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport type { SubagentTask } from \"./schema.ts\";\nimport { loadSubagentConfig } from \"./settings.ts\";\nimport type { AgentDefinition, ResolvedSubagentTask, SubagentProfileOverride } from \"./types.ts\";\n\nexport interface ParentModelContext {\n\tmodel: Model<Api> | undefined;\n\tthinking: ThinkingLevel;\n\tmodelRegistry: Pick<ModelRegistry, \"find\" | \"getAvailable\" | \"hasConfiguredAuth\">;\n}\n\nfunction normalizeCwdInput(value: string | undefined): string | undefined {\n\tif (value === undefined) return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed) throw new Error(\"cwd must not be empty.\");\n\treturn trimmed.startsWith(\"@\") ? trimmed.slice(1) : trimmed;\n}\n\nfunction isWithin(parent: string, child: string): boolean {\n\tconst normalizedParent = process.platform === \"win32\" ? parent.toLowerCase() : parent;\n\tconst normalizedChild = process.platform === \"win32\" ? child.toLowerCase() : child;\n\tconst rest = relative(normalizedParent, normalizedChild);\n\treturn rest === \"\" || (rest !== \"..\" && !rest.startsWith(`..${requirePathSeparator()}`) && !isAbsolute(rest));\n}\n\nfunction requirePathSeparator(): string {\n\treturn process.platform === \"win32\" ? \"\\\\\" : \"/\";\n}\n\nexport function resolveTaskCwd(parentCwd: string, requestedCwd: string | undefined): string {\n\tconst value = normalizeCwdInput(requestedCwd);\n\tif (value !== undefined && isAbsolute(value))\n\t\tthrow new Error(\"cwd must be a relative path inside the parent working directory.\");\n\tconst candidate = resolve(parentCwd, value ?? \".\");\n\tconst realParent = realpathSync(parentCwd);\n\tif (!isWithin(realParent, candidate))\n\t\tthrow new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);\n\tif (!existsSync(candidate) || !statSync(candidate).isDirectory())\n\t\tthrow new Error(`Subagent cwd is not a directory: ${candidate}`);\n\tconst realCandidate = realpathSync(candidate);\n\tif (!isWithin(realParent, realCandidate))\n\t\tthrow new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);\n\treturn realCandidate;\n}\n\nfunction formatModel(model: Model<Api>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction findAvailableModel(spec: string, parent: ParentModelContext): Model<Api> {\n\tconst normalized = spec.trim();\n\tlet model: Model<Api> | undefined;\n\tif (normalized.includes(\"/\")) {\n\t\tconst separator = normalized.indexOf(\"/\");\n\t\tmodel = parent.modelRegistry.find(normalized.slice(0, separator), normalized.slice(separator + 1));\n\t} else {\n\t\tconst matches = parent.modelRegistry.getAvailable().filter((candidate) => candidate.id === normalized);\n\t\tif (matches.length === 1) model = matches[0];\n\t\tif (matches.length > 1) throw new Error(`Model id \"${normalized}\" is ambiguous; use provider/model.`);\n\t}\n\tif (!model) throw new Error(`Model \"${normalized}\" is not available.`);\n\tif (!parent.modelRegistry.hasConfiguredAuth(model))\n\t\tthrow new Error(`Model \"${normalized}\" has no configured authentication.`);\n\treturn model;\n}\n\n// Exactly two layers: a /agents override wins, otherwise the subagent\n// inherits the parent session. Callers and agent files cannot pick models.\nfunction resolveModel(\n\toverride: SubagentProfileOverride | undefined,\n\tparent: ParentModelContext,\n): { model: Model<Api>; source: ResolvedSubagentTask[\"modelSource\"] } {\n\tif (override?.model) return { model: findAvailableModel(override.model, parent), source: \"profile\" };\n\tif (!parent.model) throw new Error(\"The parent session has no active model.\");\n\treturn { model: parent.model, source: \"parent\" };\n}\n\nfunction resolveThinking(\n\toverride: SubagentProfileOverride | undefined,\n\tparent: ParentModelContext,\n\tmodel: Model<Api>,\n): { thinking: ThinkingLevel; source: ResolvedSubagentTask[\"thinkingSource\"] } {\n\tconst requested = override?.thinking ?? parent.thinking;\n\tconst source: ResolvedSubagentTask[\"thinkingSource\"] = override?.thinking ? \"profile\" : \"parent\";\n\treturn { thinking: clampThinkingLevel(model, requested) as ThinkingLevel, source };\n}\n\nexport async function resolveSubagentTask(\n\ttask: SubagentTask,\n\tparentCwd: string,\n\tagents: readonly AgentDefinition[],\n\tparent: ParentModelContext,\n\tconfigAgentDir = getAgentDir(),\n): Promise<ResolvedSubagentTask> {\n\tconst agentName = task.agent ?? \"general\";\n\tconst agent = agents.find((candidate) => candidate.name === agentName);\n\tif (!agent) {\n\t\tconst available = agents.map((candidate) => candidate.name).join(\", \") || \"none\";\n\t\tthrow new Error(`Unknown agent \"${agentName}\". Available agents: ${available}.`);\n\t}\n\tconst config = await loadSubagentConfig(configAgentDir);\n\tconst override = config.profiles[agent.name];\n\tconst resolvedModel = resolveModel(override, parent);\n\tconst resolvedThinking = resolveThinking(override, parent, resolvedModel.model);\n\treturn {\n\t\tagent,\n\t\tdescription: task.description,\n\t\tprompt: task.prompt,\n\t\tcwd: resolveTaskCwd(parentCwd, task.cwd ?? undefined),\n\t\tmodel: resolvedModel.model,\n\t\tthinking: resolvedThinking.thinking,\n\t\tmodelSource: resolvedModel.source,\n\t\tthinkingSource: resolvedThinking.source,\n\t};\n}\n\nexport async function loadProfileOverrides(\n\tagentDir = getAgentDir(),\n): Promise<Awaited<ReturnType<typeof loadSubagentConfig>>> {\n\treturn loadSubagentConfig(agentDir);\n}\n\nexport function modelLabel(model: Model<Api>): string {\n\treturn formatModel(model);\n}\n"]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
|
+
import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
|
|
4
|
+
import { getAgentDir } from "../../config.js";
|
|
5
|
+
import { loadSubagentConfig } from "./settings.js";
|
|
6
|
+
function normalizeCwdInput(value) {
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
if (!trimmed)
|
|
11
|
+
throw new Error("cwd must not be empty.");
|
|
12
|
+
return trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
|
|
13
|
+
}
|
|
14
|
+
function isWithin(parent, child) {
|
|
15
|
+
const normalizedParent = process.platform === "win32" ? parent.toLowerCase() : parent;
|
|
16
|
+
const normalizedChild = process.platform === "win32" ? child.toLowerCase() : child;
|
|
17
|
+
const rest = relative(normalizedParent, normalizedChild);
|
|
18
|
+
return rest === "" || (rest !== ".." && !rest.startsWith(`..${requirePathSeparator()}`) && !isAbsolute(rest));
|
|
19
|
+
}
|
|
20
|
+
function requirePathSeparator() {
|
|
21
|
+
return process.platform === "win32" ? "\\" : "/";
|
|
22
|
+
}
|
|
23
|
+
export function resolveTaskCwd(parentCwd, requestedCwd) {
|
|
24
|
+
const value = normalizeCwdInput(requestedCwd);
|
|
25
|
+
if (value !== undefined && isAbsolute(value))
|
|
26
|
+
throw new Error("cwd must be a relative path inside the parent working directory.");
|
|
27
|
+
const candidate = resolve(parentCwd, value ?? ".");
|
|
28
|
+
const realParent = realpathSync(parentCwd);
|
|
29
|
+
if (!isWithin(realParent, candidate))
|
|
30
|
+
throw new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);
|
|
31
|
+
if (!existsSync(candidate) || !statSync(candidate).isDirectory())
|
|
32
|
+
throw new Error(`Subagent cwd is not a directory: ${candidate}`);
|
|
33
|
+
const realCandidate = realpathSync(candidate);
|
|
34
|
+
if (!isWithin(realParent, realCandidate))
|
|
35
|
+
throw new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);
|
|
36
|
+
return realCandidate;
|
|
37
|
+
}
|
|
38
|
+
function formatModel(model) {
|
|
39
|
+
return `${model.provider}/${model.id}`;
|
|
40
|
+
}
|
|
41
|
+
function findAvailableModel(spec, parent) {
|
|
42
|
+
const normalized = spec.trim();
|
|
43
|
+
let model;
|
|
44
|
+
if (normalized.includes("/")) {
|
|
45
|
+
const separator = normalized.indexOf("/");
|
|
46
|
+
model = parent.modelRegistry.find(normalized.slice(0, separator), normalized.slice(separator + 1));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const matches = parent.modelRegistry.getAvailable().filter((candidate) => candidate.id === normalized);
|
|
50
|
+
if (matches.length === 1)
|
|
51
|
+
model = matches[0];
|
|
52
|
+
if (matches.length > 1)
|
|
53
|
+
throw new Error(`Model id "${normalized}" is ambiguous; use provider/model.`);
|
|
54
|
+
}
|
|
55
|
+
if (!model)
|
|
56
|
+
throw new Error(`Model "${normalized}" is not available.`);
|
|
57
|
+
if (!parent.modelRegistry.hasConfiguredAuth(model))
|
|
58
|
+
throw new Error(`Model "${normalized}" has no configured authentication.`);
|
|
59
|
+
return model;
|
|
60
|
+
}
|
|
61
|
+
// Exactly two layers: a /agents override wins, otherwise the subagent
|
|
62
|
+
// inherits the parent session. Callers and agent files cannot pick models.
|
|
63
|
+
function resolveModel(override, parent) {
|
|
64
|
+
if (override?.model)
|
|
65
|
+
return { model: findAvailableModel(override.model, parent), source: "profile" };
|
|
66
|
+
if (!parent.model)
|
|
67
|
+
throw new Error("The parent session has no active model.");
|
|
68
|
+
return { model: parent.model, source: "parent" };
|
|
69
|
+
}
|
|
70
|
+
function resolveThinking(override, parent, model) {
|
|
71
|
+
const requested = override?.thinking ?? parent.thinking;
|
|
72
|
+
const source = override?.thinking ? "profile" : "parent";
|
|
73
|
+
return { thinking: clampThinkingLevel(model, requested), source };
|
|
74
|
+
}
|
|
75
|
+
export async function resolveSubagentTask(task, parentCwd, agents, parent, configAgentDir = getAgentDir()) {
|
|
76
|
+
const agentName = task.agent ?? "general";
|
|
77
|
+
const agent = agents.find((candidate) => candidate.name === agentName);
|
|
78
|
+
if (!agent) {
|
|
79
|
+
const available = agents.map((candidate) => candidate.name).join(", ") || "none";
|
|
80
|
+
throw new Error(`Unknown agent "${agentName}". Available agents: ${available}.`);
|
|
81
|
+
}
|
|
82
|
+
const config = await loadSubagentConfig(configAgentDir);
|
|
83
|
+
const override = config.profiles[agent.name];
|
|
84
|
+
const resolvedModel = resolveModel(override, parent);
|
|
85
|
+
const resolvedThinking = resolveThinking(override, parent, resolvedModel.model);
|
|
86
|
+
return {
|
|
87
|
+
agent,
|
|
88
|
+
description: task.description,
|
|
89
|
+
prompt: task.prompt,
|
|
90
|
+
cwd: resolveTaskCwd(parentCwd, task.cwd ?? undefined),
|
|
91
|
+
model: resolvedModel.model,
|
|
92
|
+
thinking: resolvedThinking.thinking,
|
|
93
|
+
modelSource: resolvedModel.source,
|
|
94
|
+
thinkingSource: resolvedThinking.source,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export async function loadProfileOverrides(agentDir = getAgentDir()) {
|
|
98
|
+
return loadSubagentConfig(agentDir);
|
|
99
|
+
}
|
|
100
|
+
export function modelLabel(model) {
|
|
101
|
+
return formatModel(model);
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/extensions/subagent/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1D,OAAO,EAAY,kBAAkB,EAAc,MAAM,8BAA8B,CAAC;AACxF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AASnD,SAAS,iBAAiB,CAAC,KAAyB,EAAsB;IACzE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACxD,OAAO,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAAA,CAC5D;AAED,SAAS,QAAQ,CAAC,MAAc,EAAE,KAAa,EAAW;IACzD,MAAM,gBAAgB,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IACtF,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IACnF,MAAM,IAAI,GAAG,QAAQ,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;IACzD,OAAO,IAAI,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,oBAAoB,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AAAA,CAC9G;AAED,SAAS,oBAAoB,GAAW;IACvC,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;AAAA,CACjD;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,YAAgC,EAAU;IAC3F,MAAM,KAAK,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAC9C,IAAI,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACrF,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IAC3C,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,sDAAsD,YAAY,EAAE,CAAC,CAAC;IACvF,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;QAC/D,MAAM,IAAI,KAAK,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;IAClE,MAAM,aAAa,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,sDAAsD,YAAY,EAAE,CAAC,CAAC;IACvF,OAAO,aAAa,CAAC;AAAA,CACrB;AAED,SAAS,WAAW,CAAC,KAAiB,EAAU;IAC/C,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;AAAA,CACvC;AAED,SAAS,kBAAkB,CAAC,IAAY,EAAE,MAA0B,EAAc;IACjF,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,KAA6B,CAAC;IAClC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1C,KAAK,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;IACpG,CAAC;SAAM,CAAC;QACP,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,UAAU,CAAC,CAAC;QACvG,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,UAAU,qCAAqC,CAAC,CAAC;IACvG,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,UAAU,UAAU,qBAAqB,CAAC,CAAC;IACvE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC,KAAK,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,UAAU,UAAU,qCAAqC,CAAC,CAAC;IAC5E,OAAO,KAAK,CAAC;AAAA,CACb;AAED,sEAAsE;AACtE,2EAA2E;AAC3E,SAAS,YAAY,CACpB,QAA6C,EAC7C,MAA0B,EAC2C;IACrE,IAAI,QAAQ,EAAE,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACrG,IAAI,CAAC,MAAM,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC9E,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAAA,CACjD;AAED,SAAS,eAAe,CACvB,QAA6C,EAC7C,MAA0B,EAC1B,KAAiB,EAC6D;IAC9E,MAAM,SAAS,GAAG,QAAQ,EAAE,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC;IACxD,MAAM,MAAM,GAA2C,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;IACjG,OAAO,EAAE,QAAQ,EAAE,kBAAkB,CAAC,KAAK,EAAE,SAAS,CAAkB,EAAE,MAAM,EAAE,CAAC;AAAA,CACnF;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACxC,IAAkB,EAClB,SAAiB,EACjB,MAAkC,EAClC,MAA0B,EAC1B,cAAc,GAAG,WAAW,EAAE,EACE;IAChC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC;IAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IACvE,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,kBAAkB,SAAS,wBAAwB,SAAS,GAAG,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,cAAc,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,gBAAgB,GAAG,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAChF,OAAO;QACN,KAAK;QACL,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,EAAE,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC;QACrD,KAAK,EAAE,aAAa,CAAC,KAAK;QAC1B,QAAQ,EAAE,gBAAgB,CAAC,QAAQ;QACnC,WAAW,EAAE,aAAa,CAAC,MAAM;QACjC,cAAc,EAAE,gBAAgB,CAAC,MAAM;KACvC,CAAC;AAAA,CACF;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,QAAQ,GAAG,WAAW,EAAE,EACkC;IAC1D,OAAO,kBAAkB,CAAC,QAAQ,CAAC,CAAC;AAAA,CACpC;AAED,MAAM,UAAU,UAAU,CAAC,KAAiB,EAAU;IACrD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;AAAA,CAC1B","sourcesContent":["import { existsSync, realpathSync, statSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport { type Api, clampThinkingLevel, type Model } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport type { SubagentTask } from \"./schema.ts\";\nimport { loadSubagentConfig } from \"./settings.ts\";\nimport type { AgentDefinition, ResolvedSubagentTask, SubagentProfileOverride } from \"./types.ts\";\n\nexport interface ParentModelContext {\n\tmodel: Model<Api> | undefined;\n\tthinking: ThinkingLevel;\n\tmodelRegistry: Pick<ModelRegistry, \"find\" | \"getAvailable\" | \"hasConfiguredAuth\">;\n}\n\nfunction normalizeCwdInput(value: string | undefined): string | undefined {\n\tif (value === undefined) return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed) throw new Error(\"cwd must not be empty.\");\n\treturn trimmed.startsWith(\"@\") ? trimmed.slice(1) : trimmed;\n}\n\nfunction isWithin(parent: string, child: string): boolean {\n\tconst normalizedParent = process.platform === \"win32\" ? parent.toLowerCase() : parent;\n\tconst normalizedChild = process.platform === \"win32\" ? child.toLowerCase() : child;\n\tconst rest = relative(normalizedParent, normalizedChild);\n\treturn rest === \"\" || (rest !== \"..\" && !rest.startsWith(`..${requirePathSeparator()}`) && !isAbsolute(rest));\n}\n\nfunction requirePathSeparator(): string {\n\treturn process.platform === \"win32\" ? \"\\\\\" : \"/\";\n}\n\nexport function resolveTaskCwd(parentCwd: string, requestedCwd: string | undefined): string {\n\tconst value = normalizeCwdInput(requestedCwd);\n\tif (value !== undefined && isAbsolute(value))\n\t\tthrow new Error(\"cwd must be a relative path inside the parent working directory.\");\n\tconst candidate = resolve(parentCwd, value ?? \".\");\n\tconst realParent = realpathSync(parentCwd);\n\tif (!isWithin(realParent, candidate))\n\t\tthrow new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);\n\tif (!existsSync(candidate) || !statSync(candidate).isDirectory())\n\t\tthrow new Error(`Subagent cwd is not a directory: ${candidate}`);\n\tconst realCandidate = realpathSync(candidate);\n\tif (!isWithin(realParent, realCandidate))\n\t\tthrow new Error(`Subagent cwd escapes the parent working directory: ${requestedCwd}`);\n\treturn realCandidate;\n}\n\nfunction formatModel(model: Model<Api>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction findAvailableModel(spec: string, parent: ParentModelContext): Model<Api> {\n\tconst normalized = spec.trim();\n\tlet model: Model<Api> | undefined;\n\tif (normalized.includes(\"/\")) {\n\t\tconst separator = normalized.indexOf(\"/\");\n\t\tmodel = parent.modelRegistry.find(normalized.slice(0, separator), normalized.slice(separator + 1));\n\t} else {\n\t\tconst matches = parent.modelRegistry.getAvailable().filter((candidate) => candidate.id === normalized);\n\t\tif (matches.length === 1) model = matches[0];\n\t\tif (matches.length > 1) throw new Error(`Model id \"${normalized}\" is ambiguous; use provider/model.`);\n\t}\n\tif (!model) throw new Error(`Model \"${normalized}\" is not available.`);\n\tif (!parent.modelRegistry.hasConfiguredAuth(model))\n\t\tthrow new Error(`Model \"${normalized}\" has no configured authentication.`);\n\treturn model;\n}\n\n// Exactly two layers: a /agents override wins, otherwise the subagent\n// inherits the parent session. Callers and agent files cannot pick models.\nfunction resolveModel(\n\toverride: SubagentProfileOverride | undefined,\n\tparent: ParentModelContext,\n): { model: Model<Api>; source: ResolvedSubagentTask[\"modelSource\"] } {\n\tif (override?.model) return { model: findAvailableModel(override.model, parent), source: \"profile\" };\n\tif (!parent.model) throw new Error(\"The parent session has no active model.\");\n\treturn { model: parent.model, source: \"parent\" };\n}\n\nfunction resolveThinking(\n\toverride: SubagentProfileOverride | undefined,\n\tparent: ParentModelContext,\n\tmodel: Model<Api>,\n): { thinking: ThinkingLevel; source: ResolvedSubagentTask[\"thinkingSource\"] } {\n\tconst requested = override?.thinking ?? parent.thinking;\n\tconst source: ResolvedSubagentTask[\"thinkingSource\"] = override?.thinking ? \"profile\" : \"parent\";\n\treturn { thinking: clampThinkingLevel(model, requested) as ThinkingLevel, source };\n}\n\nexport async function resolveSubagentTask(\n\ttask: SubagentTask,\n\tparentCwd: string,\n\tagents: readonly AgentDefinition[],\n\tparent: ParentModelContext,\n\tconfigAgentDir = getAgentDir(),\n): Promise<ResolvedSubagentTask> {\n\tconst agentName = task.agent ?? \"general\";\n\tconst agent = agents.find((candidate) => candidate.name === agentName);\n\tif (!agent) {\n\t\tconst available = agents.map((candidate) => candidate.name).join(\", \") || \"none\";\n\t\tthrow new Error(`Unknown agent \"${agentName}\". Available agents: ${available}.`);\n\t}\n\tconst config = await loadSubagentConfig(configAgentDir);\n\tconst override = config.profiles[agent.name];\n\tconst resolvedModel = resolveModel(override, parent);\n\tconst resolvedThinking = resolveThinking(override, parent, resolvedModel.model);\n\treturn {\n\t\tagent,\n\t\tdescription: task.description,\n\t\tprompt: task.prompt,\n\t\tcwd: resolveTaskCwd(parentCwd, task.cwd ?? undefined),\n\t\tmodel: resolvedModel.model,\n\t\tthinking: resolvedThinking.thinking,\n\t\tmodelSource: resolvedModel.source,\n\t\tthinkingSource: resolvedThinking.source,\n\t};\n}\n\nexport async function loadProfileOverrides(\n\tagentDir = getAgentDir(),\n): Promise<Awaited<ReturnType<typeof loadSubagentConfig>>> {\n\treturn loadSubagentConfig(agentDir);\n}\n\nexport function modelLabel(model: Model<Api>): string {\n\treturn formatModel(model);\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ModelRuntime } from "../../core/model-runtime.ts";
|
|
2
|
+
import { type ParentModelContext } from "./resolve.ts";
|
|
3
|
+
import type { SubagentParams } from "./schema.ts";
|
|
4
|
+
import type { AgentDefinition, SubagentDetails, SubagentExecutionResult } from "./types.ts";
|
|
5
|
+
export declare class ConcurrencyGate {
|
|
6
|
+
private active;
|
|
7
|
+
private readonly waiters;
|
|
8
|
+
private readonly limit;
|
|
9
|
+
constructor(limit?: number);
|
|
10
|
+
acquire(signal?: AbortSignal): Promise<() => void>;
|
|
11
|
+
private release;
|
|
12
|
+
}
|
|
13
|
+
export interface SubagentInvocationOptions {
|
|
14
|
+
params: SubagentParams;
|
|
15
|
+
parentCwd: string;
|
|
16
|
+
agents: readonly AgentDefinition[];
|
|
17
|
+
parent: ParentModelContext;
|
|
18
|
+
modelRuntime: ModelRuntime;
|
|
19
|
+
agentDir: string;
|
|
20
|
+
configAgentDir: string;
|
|
21
|
+
projectTrusted: boolean;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
gate: ConcurrencyGate;
|
|
24
|
+
onUpdate?: (details: SubagentDetails) => void;
|
|
25
|
+
registerAbort?: (abort: () => Promise<void>) => () => void;
|
|
26
|
+
}
|
|
27
|
+
export declare function boundSubagentDetails(details: SubagentDetails): SubagentDetails;
|
|
28
|
+
export declare function runSubagentInvocation(options: SubagentInvocationOptions): Promise<SubagentExecutionResult>;
|
|
29
|
+
export declare function statusSummary(details: SubagentDetails): string;
|
|
30
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAahE,OAAO,EAAE,KAAK,kBAAkB,EAAuB,MAAM,cAAc,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,aAAa,CAAC;AAEhE,OAAO,KAAK,EACX,eAAe,EAEf,eAAe,EACf,uBAAuB,EAGvB,MAAM,YAAY,CAAC;AAWpB,qBAAa,eAAe;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B,YAAY,KAAK,SAAkB,EAElC;IAED,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAkBjD;IAED,OAAO,CAAC,OAAO;CAef;AAED,MAAM,WAAW,yBAAyB;IACzC,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC;CAC3D;AAwCD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAyF9E;AA6KD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA2ChH;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAE9D","sourcesContent":["import type { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { boundText, emptyUsage, mergeUsage, toNestedUsage } from \"./activity.ts\";\nimport {\n\tCHAIN_HANDOFF_LIMIT,\n\tDETAILS_ACTIVITY_LIMIT,\n\tDETAILS_OUTPUT_LIMIT,\n\tERROR_TEXT_LIMIT,\n\tMAX_CONCURRENCY,\n\tMAX_TASKS,\n\tPARALLEL_OUTPUT_LIMIT,\n\tPARALLEL_TASK_OUTPUT_LIMIT,\n\tSINGLE_OUTPUT_LIMIT,\n} from \"./constants.ts\";\nimport { type ParentModelContext, resolveSubagentTask } from \"./resolve.ts\";\nimport type { SubagentParams, SubagentTask } from \"./schema.ts\";\nimport { runSdkTask } from \"./sdk-runner.ts\";\nimport type {\n\tAgentDefinition,\n\tResolvedSubagentTask,\n\tSubagentDetails,\n\tSubagentExecutionResult,\n\tSubagentRunDetails,\n\tSubagentUsage,\n} from \"./types.ts\";\n\nconst EMPTY_OUTPUT = \"(Subagent completed but returned no output.)\";\n\ninterface Waiter {\n\tresolve: (release: () => void) => void;\n\treject: (error: Error) => void;\n\tsignal?: AbortSignal;\n\tabortListener?: () => void;\n}\n\nexport class ConcurrencyGate {\n\tprivate active = 0;\n\tprivate readonly waiters: Waiter[] = [];\n\tprivate readonly limit: number;\n\n\tconstructor(limit = MAX_CONCURRENCY) {\n\t\tthis.limit = limit;\n\t}\n\n\tacquire(signal?: AbortSignal): Promise<() => void> {\n\t\tif (signal?.aborted) return Promise.reject(new Error(\"Subagent was aborted while queued.\"));\n\t\tif (this.active < this.limit) {\n\t\t\tthis.active++;\n\t\t\treturn Promise.resolve(() => this.release());\n\t\t}\n\t\treturn new Promise<() => void>((resolve, reject) => {\n\t\t\tconst waiter: Waiter = { resolve, reject, signal };\n\t\t\tif (signal) {\n\t\t\t\twaiter.abortListener = () => {\n\t\t\t\t\tconst index = this.waiters.indexOf(waiter);\n\t\t\t\t\tif (index >= 0) this.waiters.splice(index, 1);\n\t\t\t\t\treject(new Error(\"Subagent was aborted while queued.\"));\n\t\t\t\t};\n\t\t\t\tsignal.addEventListener(\"abort\", waiter.abortListener, { once: true });\n\t\t\t}\n\t\t\tthis.waiters.push(waiter);\n\t\t});\n\t}\n\n\tprivate release(): void {\n\t\tthis.active = Math.max(0, this.active - 1);\n\t\twhile (this.waiters.length > 0) {\n\t\t\tconst waiter = this.waiters.shift();\n\t\t\tif (!waiter) return;\n\t\t\tif (waiter.signal?.aborted) {\n\t\t\t\twaiter.abortListener?.();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twaiter.signal?.removeEventListener(\"abort\", waiter.abortListener!);\n\t\t\tthis.active++;\n\t\t\twaiter.resolve(() => this.release());\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nexport interface SubagentInvocationOptions {\n\tparams: SubagentParams;\n\tparentCwd: string;\n\tagents: readonly AgentDefinition[];\n\tparent: ParentModelContext;\n\tmodelRuntime: ModelRuntime;\n\tagentDir: string;\n\tconfigAgentDir: string;\n\tprojectTrusted: boolean;\n\tsignal?: AbortSignal;\n\tgate: ConcurrencyGate;\n\tonUpdate?: (details: SubagentDetails) => void;\n\tregisterAbort?: (abort: () => Promise<void>) => () => void;\n}\n\nfunction runId(index: number): string {\n\treturn `subagent-${index + 1}`;\n}\n\nfunction createRun(task: ResolvedSubagentTask, index: number, step?: number): SubagentRunDetails {\n\treturn {\n\t\tid: runId(index),\n\t\tagent: task.agent.name,\n\t\tagentSource: task.agent.source,\n\t\tdescription: task.description,\n\t\tprompt: task.prompt,\n\t\tcwd: task.cwd,\n\t\tmodel: `${task.model.provider}/${task.model.id}`,\n\t\tthinking: task.thinking,\n\t\tstatus: \"queued\",\n\t\tactivities: [],\n\t\tliveText: \"\",\n\t\tfinalOutput: \"\",\n\t\tusage: emptyUsage(),\n\t\tstep,\n\t};\n}\n\nfunction aggregateUsage(runs: readonly SubagentRunDetails[]): SubagentUsage {\n\tconst usage = emptyUsage();\n\tfor (const run of runs) mergeUsage(usage, run.usage);\n\treturn usage;\n}\n\ninterface TextField {\n\tget: () => string;\n\tset: (value: string) => void;\n}\n\nfunction detailsSize(details: SubagentDetails): number {\n\treturn Buffer.byteLength(JSON.stringify(details), \"utf8\");\n}\n\nexport function boundSubagentDetails(details: SubagentDetails): SubagentDetails {\n\tconst perRunBudget = Math.max(\n\t\t1_024,\n\t\tMath.floor((DETAILS_OUTPUT_LIMIT - 8 * 1024) / Math.max(1, details.runs.length)),\n\t);\n\tconst outputLimit = Math.min(SINGLE_OUTPUT_LIMIT, Math.max(1_024, perRunBudget - 7 * 1024));\n\tconst bounded: SubagentDetails = {\n\t\t...details,\n\t\truns: details.runs.map((run) => ({\n\t\t\t...run,\n\t\t\tprompt: boundText(run.prompt, 1_024),\n\t\t\tcwd: boundText(run.cwd, 1_024),\n\t\t\tcurrentActivity: run.currentActivity ? boundText(run.currentActivity, 512) : undefined,\n\t\t\tactivities: run.activities.slice(-DETAILS_ACTIVITY_LIMIT).map((activity) => ({\n\t\t\t\t...activity,\n\t\t\t\tsummary: boundText(activity.summary, 256),\n\t\t\t\tresultSummary: activity.resultSummary ? boundText(activity.resultSummary, 256) : undefined,\n\t\t\t})),\n\t\t\tliveText: boundText(run.liveText, 1_024),\n\t\t\tfinalOutput: boundText(run.finalOutput, outputLimit),\n\t\t\terror: run.error ? boundText(run.error, 1_024) : undefined,\n\t\t})),\n\t};\n\tconst fields: TextField[] = [];\n\tfor (const run of bounded.runs) {\n\t\tfields.push(\n\t\t\t{\n\t\t\t\tget: () => run.prompt,\n\t\t\t\tset: (value) => {\n\t\t\t\t\trun.prompt = value;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tget: () => run.cwd,\n\t\t\t\tset: (value) => {\n\t\t\t\t\trun.cwd = value;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tget: () => run.currentActivity ?? \"\",\n\t\t\t\tset: (value) => {\n\t\t\t\t\trun.currentActivity = value || undefined;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tget: () => run.liveText,\n\t\t\t\tset: (value) => {\n\t\t\t\t\trun.liveText = value;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tget: () => run.finalOutput,\n\t\t\t\tset: (value) => {\n\t\t\t\t\trun.finalOutput = value;\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tget: () => run.error ?? \"\",\n\t\t\t\tset: (value) => {\n\t\t\t\t\trun.error = value || undefined;\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t\tfor (const activity of run.activities) {\n\t\t\tfields.push(\n\t\t\t\t{\n\t\t\t\t\tget: () => activity.summary,\n\t\t\t\t\tset: (value) => {\n\t\t\t\t\t\tactivity.summary = value;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tget: () => activity.resultSummary ?? \"\",\n\t\t\t\t\tset: (value) => {\n\t\t\t\t\t\tactivity.resultSummary = value || undefined;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\twhile (detailsSize(bounded) > DETAILS_OUTPUT_LIMIT) {\n\t\tconst largest = fields\n\t\t\t.map((field) => ({ field, size: Buffer.byteLength(field.get(), \"utf8\") }))\n\t\t\t.sort((left, right) => right.size - left.size)[0];\n\t\tif (!largest || largest.size === 0) break;\n\t\tconst overflow = detailsSize(bounded) - DETAILS_OUTPUT_LIMIT;\n\t\tlargest.field.set(boundText(largest.field.get(), Math.max(0, largest.size - overflow)));\n\t}\n\treturn bounded;\n}\n\nfunction statusOf(runs: readonly SubagentRunDetails[]): SubagentDetails[\"status\"] {\n\tif (runs.some((run) => run.status === \"aborted\")) return \"aborted\";\n\tif (runs.some((run) => run.status === \"failed\")) return \"failed\";\n\tif (runs.some((run) => run.status === \"queued\" || run.status === \"running\")) return \"running\";\n\treturn \"completed\";\n}\n\nfunction statusText(details: SubagentDetails): string {\n\tconst running = details.runs.filter((run) => run.status === \"running\").length;\n\tconst queued = details.runs.filter((run) => run.status === \"queued\").length;\n\tconst completed = details.runs.filter((run) => run.status === \"completed\").length;\n\tconst failed = details.runs.filter((run) => run.status === \"failed\").length;\n\tconst aborted = details.runs.filter((run) => run.status === \"aborted\").length;\n\tif (details.mode === \"single\") {\n\t\tconst run = details.runs[0];\n\t\tif (run?.currentActivity) return run.currentActivity;\n\t\tif (\n\t\t\t!run ||\n\t\t\t(run.status === \"running\" && run.usage.turns === 0 && run.usage.toolUses === 0 && run.liveText.length === 0)\n\t\t) {\n\t\t\treturn \"Initializing…\";\n\t\t}\n\t\treturn run.status === \"running\" ? \"Thinking…\" : run.status;\n\t}\n\tif (details.mode === \"chain\") {\n\t\tconst current = details.runs.find((run) => run.status === \"running\" || run.status === \"queued\");\n\t\treturn current\n\t\t\t? `Step ${current.step ?? 1}/${details.runs.length} · ${current.currentActivity ?? current.agent}`\n\t\t\t: `${completed}/${details.runs.length} steps complete`;\n\t}\n\treturn `${completed}/${details.runs.length} complete · ${running} running · ${queued} queued${failed ? ` · ${failed} failed` : \"\"}${aborted ? ` · ${aborted} aborted` : \"\"}`;\n}\n\nfunction emitDetails(\n\tmode: SubagentDetails[\"mode\"],\n\truns: SubagentRunDetails[],\n\tstartedAt: number,\n\tonUpdate: ((details: SubagentDetails) => void) | undefined,\n): SubagentDetails {\n\tconst details: SubagentDetails = {\n\t\tmode,\n\t\tstatus: statusOf(runs),\n\t\truns: [...runs],\n\t\tstartedAt,\n\t\tusage: aggregateUsage(runs),\n\t};\n\tonUpdate?.(boundSubagentDetails(details));\n\treturn details;\n}\n\nfunction replacePrevious(prompt: string, previous: string): string {\n\treturn prompt.replace(/\\{previous\\}/gu, boundText(previous, CHAIN_HANDOFF_LIMIT));\n}\n\nfunction validateTaskCount(tasks: readonly SubagentTask[]): void {\n\tif (tasks.length === 0) throw new Error(\"Subagent task list must not be empty.\");\n\tif (tasks.length > MAX_TASKS) throw new Error(`Subagent task list is limited to ${MAX_TASKS} tasks.`);\n}\n\nasync function resolveTasks(\n\ttasks: readonly SubagentTask[],\n\toptions: SubagentInvocationOptions,\n): Promise<ResolvedSubagentTask[]> {\n\tvalidateTaskCount(tasks);\n\treturn Promise.all(\n\t\ttasks.map((task) =>\n\t\t\tresolveSubagentTask(task, options.parentCwd, options.agents, options.parent, options.configAgentDir),\n\t\t),\n\t);\n}\n\nasync function runWithGate(\n\ttask: ResolvedSubagentTask,\n\trun: SubagentRunDetails,\n\toptions: SubagentInvocationOptions,\n\tonProgress: () => void,\n): Promise<SubagentRunDetails> {\n\tonProgress();\n\tlet release: (() => void) | undefined;\n\ttry {\n\t\trelease = await options.gate.acquire(options.signal);\n\t\tif (options.signal?.aborted) {\n\t\t\trun.status = \"aborted\";\n\t\t\trun.error = \"Subagent was aborted while queued.\";\n\t\t\treturn run;\n\t\t}\n\t\treturn await runSdkTask({\n\t\t\ttask,\n\t\t\trun,\n\t\t\tmodelRuntime: options.modelRuntime,\n\t\t\tagentDir: options.agentDir,\n\t\t\tprojectTrusted: options.projectTrusted,\n\t\t\tsignal: options.signal,\n\t\t\tonProgress,\n\t\t\tregisterAbort: options.registerAbort,\n\t\t});\n\t} catch (error) {\n\t\trun.status = options.signal?.aborted ? \"aborted\" : \"failed\";\n\t\trun.error = boundText(error instanceof Error ? error.message : String(error), ERROR_TEXT_LIMIT);\n\t\trun.endedAt = Date.now();\n\t\tonProgress();\n\t\treturn run;\n\t} finally {\n\t\trelease?.();\n\t}\n}\n\nfunction resultContent(details: SubagentDetails): string {\n\tif (details.mode === \"single\") {\n\t\tconst run = details.runs[0];\n\t\tif (!run) return \"Subagent produced no run.\";\n\t\tif (run.status === \"failed\" || run.status === \"aborted\") {\n\t\t\treturn boundText(\n\t\t\t\t`${run.status === \"aborted\" ? \"Subagent aborted\" : \"Subagent failed\"}: ${run.error ?? \"unknown error\"}${run.finalOutput ? `\\n\\nPartial response:\\n${run.finalOutput}` : \"\"}`,\n\t\t\t\tSINGLE_OUTPUT_LIMIT,\n\t\t\t);\n\t\t}\n\t\treturn run.finalOutput || EMPTY_OUTPUT;\n\t}\n\tif (details.mode === \"chain\") {\n\t\tconst failed = details.runs.find((run) => run.status === \"failed\" || run.status === \"aborted\");\n\t\tif (failed)\n\t\t\treturn boundText(\n\t\t\t\t`Chain stopped at step ${failed.step ?? \"?\"} (${failed.agent}): ${failed.error ?? \"unknown error\"}`,\n\t\t\t\tERROR_TEXT_LIMIT,\n\t\t\t);\n\t\tconst last = details.runs[details.runs.length - 1];\n\t\treturn last?.finalOutput || EMPTY_OUTPUT;\n\t}\n\tconst sections = details.runs.map((run) => {\n\t\tconst output = run.finalOutput || run.error || EMPTY_OUTPUT;\n\t\treturn `### ${run.agent} · ${run.status}\\n\\n${boundText(output, PARALLEL_TASK_OUTPUT_LIMIT)}`;\n\t});\n\treturn boundText(`Parallel results\\n\\n${sections.join(\"\\n\\n---\\n\\n\")}`, PARALLEL_OUTPUT_LIMIT);\n}\n\nfunction invocationMode(params: SubagentParams): { mode: SubagentDetails[\"mode\"]; tasks: SubagentTask[] } {\n\tconst provided: string[] = [];\n\tif (params.prompt != null) provided.push(\"prompt\");\n\tif (params.tasks != null) provided.push(\"tasks\");\n\tif (params.chain != null) provided.push(\"chain\");\n\tif (provided.length !== 1) {\n\t\tthrow new Error(\n\t\t\tprovided.length === 0\n\t\t\t\t? \"Provide exactly one subagent mode: prompt (single), tasks (parallel), or chain (sequential); none was provided.\"\n\t\t\t\t: `Provide exactly one subagent mode: received ${provided.join(\", \")}. Keep one and set the unused mode fields to null or omit them.`,\n\t\t);\n\t}\n\tif (params.tasks != null) {\n\t\treturn { mode: \"parallel\", tasks: params.tasks };\n\t}\n\tif (params.chain != null) {\n\t\treturn { mode: \"chain\", tasks: params.chain };\n\t}\n\tif (params.prompt == null) {\n\t\tthrow new Error(\"Provide exactly one subagent mode: prompt (single), tasks (parallel), or chain (sequential).\");\n\t}\n\tif (!params.description) throw new Error(\"description is required for single mode.\");\n\treturn {\n\t\tmode: \"single\",\n\t\ttasks: [\n\t\t\t{\n\t\t\t\tagent: params.agent,\n\t\t\t\tdescription: params.description,\n\t\t\t\tprompt: params.prompt,\n\t\t\t\tcwd: params.cwd,\n\t\t\t},\n\t\t],\n\t};\n}\n\nexport async function runSubagentInvocation(options: SubagentInvocationOptions): Promise<SubagentExecutionResult> {\n\tconst { mode, tasks } = invocationMode(options.params);\n\tconst resolved = await resolveTasks(tasks, options);\n\tconst runs = resolved.map((task, index) => createRun(task, index, mode === \"chain\" ? index + 1 : undefined));\n\tconst startedAt = Date.now();\n\tlet latestDetails = emitDetails(mode, runs, startedAt, options.onUpdate);\n\tconst progress = () => {\n\t\tlatestDetails = emitDetails(mode, runs, startedAt, options.onUpdate);\n\t};\n\n\tif (mode === \"single\") {\n\t\tawait runWithGate(resolved[0]!, runs[0]!, options, progress);\n\t} else if (mode === \"parallel\") {\n\t\tawait Promise.all(resolved.map((task, index) => runWithGate(task, runs[index]!, options, progress)));\n\t} else {\n\t\tlet previous = \"\";\n\t\tfor (let index = 0; index < resolved.length; index++) {\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\truns[index]!.status = \"aborted\";\n\t\t\t\truns[index]!.error = \"Skipped because the parent subagent call was aborted.\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst task = { ...resolved[index]!, prompt: replacePrevious(resolved[index]!.prompt, previous) };\n\t\t\tawait runWithGate(task, runs[index]!, options, progress);\n\t\t\tif (runs[index]!.status !== \"completed\") {\n\t\t\t\tfor (let skipped = index + 1; skipped < runs.length; skipped++) {\n\t\t\t\t\truns[skipped]!.status = options.signal?.aborted ? \"aborted\" : \"failed\";\n\t\t\t\t\truns[skipped]!.error = `Skipped because chain step ${index + 1} did not complete.`;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tprevious = runs[index]!.finalOutput;\n\t\t}\n\t}\n\tlatestDetails = emitDetails(mode, runs, startedAt, undefined);\n\tlatestDetails.endedAt = Date.now();\n\tconst isError = latestDetails.status === \"failed\" || latestDetails.status === \"aborted\";\n\treturn {\n\t\tcontent: resultContent(latestDetails),\n\t\tdetails: boundSubagentDetails(latestDetails),\n\t\tusage: toNestedUsage(latestDetails.usage),\n\t\tisError,\n\t};\n}\n\nexport function statusSummary(details: SubagentDetails): string {\n\treturn statusText(details);\n}\n"]}
|