@tt-a1i/openpi 0.4.0 → 0.6.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/README.md +116 -46
- package/SETUP.md +29 -7
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/assets/openpi-launch-card-v1.webp +0 -0
- package/bin/openpi.js +155 -0
- package/extensions/ai-providers/LICENSE.upstream +23 -0
- package/extensions/ai-providers/README.md +59 -0
- package/extensions/ai-providers/antigravity/credentials.ts +52 -0
- package/extensions/ai-providers/antigravity/discovery.ts +130 -0
- package/extensions/ai-providers/antigravity/google-conversion.ts +455 -0
- package/extensions/ai-providers/antigravity/models.ts +84 -0
- package/extensions/ai-providers/antigravity/oauth.ts +700 -0
- package/extensions/ai-providers/antigravity/provider.ts +1116 -0
- package/extensions/ai-providers/antigravity/routing.ts +340 -0
- package/extensions/ai-providers/antigravity/with-resolvers.d.ts +19 -0
- package/extensions/ai-providers/cursor/constants.ts +5 -0
- package/extensions/ai-providers/cursor/credentials.ts +14 -0
- package/extensions/ai-providers/cursor/discovery.ts +291 -0
- package/extensions/ai-providers/cursor/input-images.ts +106 -0
- package/extensions/ai-providers/cursor/models.ts +45 -0
- package/extensions/ai-providers/cursor/oauth.ts +263 -0
- package/extensions/ai-providers/cursor/proto.ts +1064 -0
- package/extensions/ai-providers/cursor/protobuf.ts +1171 -0
- package/extensions/ai-providers/cursor/provider.ts +1175 -0
- package/extensions/ai-providers/cursor/proxy.ts +213 -0
- package/extensions/ai-providers/cursor/with-resolvers.d.ts +12 -0
- package/extensions/ai-providers/index.ts +86 -0
- package/extensions/ai-providers/oauth-adapter.ts +81 -0
- package/extensions/ai-providers/usage.ts +10 -0
- package/extensions/background-terminals/index.ts +38 -3
- package/extensions/background-terminals/src/domain.ts +2 -0
- package/extensions/background-terminals/src/manager.ts +484 -106
- package/extensions/background-terminals/src/output.ts +33 -0
- package/extensions/background-terminals/src/prompt.ts +13 -5
- package/extensions/background-terminals/src/result-delivery.ts +47 -24
- package/extensions/clear-context/index.ts +83 -0
- package/extensions/context-pivot/index.ts +16 -6
- package/extensions/cron/index.ts +68 -27
- package/extensions/cron/schedule.ts +12 -2
- package/extensions/file-mutation-display/render.ts +17 -257
- package/extensions/file-search/src/binaries.ts +57 -41
- package/extensions/git-read/index.ts +1 -3
- package/extensions/model-info/cache-diagnostics.ts +220 -0
- package/extensions/model-info/index.ts +65 -33
- package/extensions/model-info/session-metrics.ts +96 -0
- package/extensions/plan-mode/bash-policy.ts +54 -9
- package/extensions/plan-mode/index.ts +82 -6
- package/extensions/post-edit/index.ts +16 -6
- package/extensions/sessions/git-stats.ts +258 -72
- package/extensions/sessions/index.ts +153 -86
- package/extensions/sessions/preview-cache.ts +104 -0
- package/extensions/sessions/preview-loader.ts +856 -0
- package/extensions/sessions/sessions.ts +43 -4
- package/extensions/setup/index.ts +138 -130
- package/extensions/shared/activity-status.ts +30 -0
- package/extensions/shared/agent-session-page.ts +319 -0
- package/extensions/shared/agent-tool-renderer.ts +218 -0
- package/extensions/shared/agent-transcript.ts +524 -0
- package/extensions/shared/capability-intent.ts +1 -1
- package/extensions/shared/child-session.ts +457 -21
- package/extensions/shared/completion-inbox.ts +193 -0
- package/extensions/shared/result-delivery.ts +34 -0
- package/extensions/shared/setup-config.ts +83 -34
- package/extensions/shared/setup-episode-state.ts +1 -1
- package/extensions/shared/structured-output.ts +154 -0
- package/extensions/shared/terminal-text.ts +110 -23
- package/extensions/shared/text-projection.ts +72 -15
- package/extensions/shared/tool-activity.ts +382 -0
- package/extensions/shared/tool-surface.ts +29 -2
- package/extensions/shared/transcript-viewport.ts +46 -0
- package/extensions/shared/web-observer-registry.ts +390 -0
- package/extensions/shared/worktree.ts +11 -0
- package/extensions/subagents/index.ts +313 -62
- package/extensions/subagents/navigation.ts +34 -5
- package/extensions/subagents/src/backend.ts +12 -1
- package/extensions/subagents/src/backends/pi.ts +450 -70
- package/extensions/subagents/src/domain.ts +21 -1
- package/extensions/subagents/src/manager.ts +39 -2
- package/extensions/subagents/src/prompt.ts +49 -7
- package/extensions/subagents/src/result-artifact.ts +36 -0
- package/extensions/subagents/src/result-delivery.ts +39 -14
- package/extensions/subagents/src/runtime.ts +15 -1
- package/extensions/subagents/src/ui/takeover.ts +73 -257
- package/extensions/subagents/src/ui/transcript.ts +38 -535
- package/extensions/subagents/src/ui/wait-result.ts +103 -15
- package/extensions/suggestions/src/ui.ts +10 -4
- package/extensions/tasks/index.ts +0 -3
- package/extensions/ui-customization/footer.ts +16 -45
- package/extensions/ui-customization/index.ts +0 -4
- package/extensions/user-input-fold/index.ts +42 -6
- package/extensions/web/index.ts +257 -0
- package/extensions/workflows/acceptance.ts +43 -19
- package/extensions/workflows/artifacts.ts +137 -47
- package/extensions/workflows/completion-projection.ts +459 -0
- package/extensions/workflows/coordinator.ts +8 -10
- package/extensions/workflows/dashboard.ts +175 -228
- package/extensions/workflows/handoff.ts +70 -16
- package/extensions/workflows/index.ts +501 -198
- package/extensions/workflows/journal.ts +148 -13
- package/extensions/workflows/model.ts +79 -5
- package/extensions/workflows/navigation.ts +32 -8
- package/extensions/workflows/progress-projection.ts +306 -0
- package/extensions/workflows/prompt.ts +70 -16
- package/extensions/workflows/replay-safety.ts +42 -21
- package/extensions/workflows/result-delivery.ts +214 -76
- package/extensions/workflows/retention.ts +599 -0
- package/extensions/workflows/runner.ts +389 -345
- package/extensions/workflows/sandbox-child.cjs +25 -3
- package/extensions/workflows/sandbox.ts +62 -8
- package/extensions/workflows/serialization.ts +325 -17
- package/extensions/workflows/tool-renderer.ts +22 -0
- package/extensions/workflows/transcript.ts +149 -0
- package/extensions/workspace-cleanup-guard/index.ts +54 -0
- package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
- package/package.json +34 -14
- package/skills/subagents/REFERENCE.md +190 -0
- package/skills/subagents/SKILL.md +2 -1
- package/skills/workflows/REFERENCE.md +6 -4
- package/skills/workflows/SKILL.md +1 -1
- package/web/adapter/pi-adapter.ts +664 -0
- package/web/host/browser-launcher.ts +20 -0
- package/web/host/pi-coding-agent-entry.ts +162 -0
- package/web/host/static-assets.ts +4 -0
- package/web/host/terminal-status.ts +38 -0
- package/web/host/web-host.ts +1069 -0
- package/web/http-dispatcher.ts +125 -0
- package/web/protocol/types.ts +467 -0
- package/web/runtime/pi-runtime.ts +1206 -0
- package/web/runtime/types.ts +102 -0
- package/web/runtime/web-host-lease.ts +497 -0
- package/web/trace.ts +18 -0
- package/web/ui/app.js +1700 -0
- package/web/ui/index.html +142 -0
- package/web/ui/styles.css +680 -0
- package/web/vite.config.mjs +34 -0
- package/extensions/execution-convergence/active-evidence.ts +0 -129
- package/extensions/execution-convergence/index.ts +0 -442
- package/extensions/execution-convergence/workspace-provenance.ts +0 -338
- package/extensions/setup/intercom-fs-helper.cjs +0 -130
- package/extensions/setup/intercom.ts +0 -603
- package/extensions/subagents/src/backends/stub.ts +0 -303
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* settle. `/subagents` opens a picker + full interactive takeover view.
|
|
17
17
|
*
|
|
18
18
|
* Agent types (`src/agent-types.ts`) are optional named presets that fix a
|
|
19
|
-
* child's system prompt, model, and tool allowlist; see
|
|
19
|
+
* child's system prompt, model, and tool allowlist; see
|
|
20
|
+
* `skills/subagents/REFERENCE.md`.
|
|
20
21
|
*
|
|
21
22
|
* Architecture: Effect v4 generators throughout (backend -> manager ->
|
|
22
23
|
* runtime); this file is the async boundary where tool handlers run effects
|
|
@@ -44,10 +45,12 @@ import {
|
|
|
44
45
|
import { Markdown, Text } from "@earendil-works/pi-tui";
|
|
45
46
|
import { Type } from "typebox";
|
|
46
47
|
import {
|
|
48
|
+
createStatusWriter,
|
|
47
49
|
formatActivityStatus,
|
|
48
50
|
hasActivity,
|
|
49
51
|
unreadActivityCounts,
|
|
50
52
|
} from "../shared/activity-status.ts";
|
|
53
|
+
import { sanitizeText } from "../shared/agent-transcript.ts";
|
|
51
54
|
import {
|
|
52
55
|
BelowEditorNavigationEditor,
|
|
53
56
|
BelowEditorStripState,
|
|
@@ -57,6 +60,7 @@ import {
|
|
|
57
60
|
resolveStandaloneChildProjectTrust,
|
|
58
61
|
} from "../shared/child-session.ts";
|
|
59
62
|
import { formatContextUtilization } from "../shared/context-utilization.ts";
|
|
63
|
+
import { completionOwnerFor } from "../shared/completion-inbox.ts";
|
|
60
64
|
import {
|
|
61
65
|
registerEditorLayer,
|
|
62
66
|
removeEditorLayer,
|
|
@@ -67,13 +71,22 @@ import {
|
|
|
67
71
|
planModeAllowsDeclaredTools,
|
|
68
72
|
planModeChildTools,
|
|
69
73
|
} from "../shared/plan-mode-state.ts";
|
|
70
|
-
import {
|
|
74
|
+
import {
|
|
75
|
+
allocateResultBudgets,
|
|
76
|
+
type ParentContextUsage,
|
|
77
|
+
} from "../shared/result-budget.ts";
|
|
78
|
+
import { type DetailDisplay, loadSetupConfig } from "../shared/setup-config.ts";
|
|
71
79
|
import {
|
|
72
80
|
OPENPI_TOOL_SURFACE,
|
|
73
81
|
patchOwnedTools,
|
|
74
82
|
} from "../shared/tool-surface.ts";
|
|
83
|
+
import {
|
|
84
|
+
projectSubagentCapability,
|
|
85
|
+
registerWebCapability,
|
|
86
|
+
} from "../shared/web-observer-registry.ts";
|
|
75
87
|
import {
|
|
76
88
|
createWorktree,
|
|
89
|
+
formatWorktreeCleanupWarning,
|
|
77
90
|
reclaimWorktree,
|
|
78
91
|
type Worktree,
|
|
79
92
|
} from "../shared/worktree.ts";
|
|
@@ -81,9 +94,9 @@ import {
|
|
|
81
94
|
normalizeSubagentTitle,
|
|
82
95
|
SubagentStripWidget,
|
|
83
96
|
selectSubagentStripEntry,
|
|
97
|
+
subagentStripEntryKey,
|
|
84
98
|
} from "./navigation.ts";
|
|
85
99
|
import {
|
|
86
|
-
type AgentType,
|
|
87
100
|
formatAgentTypeDiagnostics,
|
|
88
101
|
loadAgentTypes,
|
|
89
102
|
roleModelForAgentType,
|
|
@@ -104,6 +117,7 @@ import {
|
|
|
104
117
|
} from "./src/id-sequence.ts";
|
|
105
118
|
import { SubagentManager, type SubagentManagerShape } from "./src/manager.ts";
|
|
106
119
|
import {
|
|
120
|
+
buildSubagentResultDisplayMessage,
|
|
107
121
|
buildSubagentResultMessage,
|
|
108
122
|
buildSubagentSendResult,
|
|
109
123
|
buildSubagentSpawnResult,
|
|
@@ -119,12 +133,13 @@ import {
|
|
|
119
133
|
SUBAGENT_SPAWN_PROMPT_SNIPPET,
|
|
120
134
|
SUBAGENT_WAIT_PARAMETER_DESCRIPTIONS,
|
|
121
135
|
SUBAGENT_WAIT_TOOL_DESCRIPTION,
|
|
136
|
+
stripSubagentResultTransportInstruction,
|
|
122
137
|
} from "./src/prompt.ts";
|
|
123
|
-
import { persistResultArtifact, projectResult } from "./src/result-artifact.ts";
|
|
124
138
|
import {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
persistResultArtifact,
|
|
140
|
+
projectResult,
|
|
141
|
+
type ResultProjection,
|
|
142
|
+
} from "./src/result-artifact.ts";
|
|
128
143
|
import { createSubagentResultDelivery } from "./src/result-delivery.ts";
|
|
129
144
|
import {
|
|
130
145
|
createSubagentRuntime,
|
|
@@ -133,8 +148,8 @@ import {
|
|
|
133
148
|
} from "./src/runtime.ts";
|
|
134
149
|
import { openSubagentPicker, openSubagentTakeover } from "./src/ui/takeover.ts";
|
|
135
150
|
import {
|
|
136
|
-
buildWaitResultPreview,
|
|
137
151
|
renderWaitResult,
|
|
152
|
+
renderWaitResultPreview,
|
|
138
153
|
type WaitResultDetails,
|
|
139
154
|
} from "./src/ui/wait-result.ts";
|
|
140
155
|
|
|
@@ -146,6 +161,8 @@ const WAIT_PER_AGENT_MAX_BYTES = 16 * 1024;
|
|
|
146
161
|
const WAIT_MIN_RESULT_BYTES = 512;
|
|
147
162
|
const RESULT_HEADROOM_SHARE = 0.5;
|
|
148
163
|
const ESTIMATED_BYTES_PER_TOKEN = 4;
|
|
164
|
+
const AUTOMATIC_BATCH_TRUNCATION_NOTICE =
|
|
165
|
+
"\n\n[Automatic subagent result batch truncated at the 48 KiB total limit.]";
|
|
149
166
|
|
|
150
167
|
interface SpawnResultDetails {
|
|
151
168
|
readonly id?: string;
|
|
@@ -153,6 +170,7 @@ interface SpawnResultDetails {
|
|
|
153
170
|
readonly harness?: string;
|
|
154
171
|
readonly model?: string;
|
|
155
172
|
readonly agentType?: string;
|
|
173
|
+
readonly structured?: boolean;
|
|
156
174
|
}
|
|
157
175
|
|
|
158
176
|
interface SubagentFinishedData {
|
|
@@ -166,12 +184,28 @@ interface SubagentResultDetails {
|
|
|
166
184
|
readonly id?: string;
|
|
167
185
|
readonly title?: string;
|
|
168
186
|
readonly status?: SubagentSnapshot["status"];
|
|
187
|
+
readonly outcome?: SubagentSnapshot["outcome"];
|
|
188
|
+
readonly worktreeBranch?: string;
|
|
189
|
+
readonly elapsed?: string;
|
|
190
|
+
readonly artifactSaveFailed?: boolean;
|
|
191
|
+
readonly fullResultSaved?: boolean;
|
|
192
|
+
readonly structured?: unknown;
|
|
193
|
+
readonly structuredArtifactPath?: string;
|
|
169
194
|
readonly count?: number;
|
|
170
195
|
readonly results?: ReadonlyArray<{
|
|
171
196
|
readonly id: string;
|
|
172
197
|
readonly title: string;
|
|
173
198
|
readonly status: SubagentSnapshot["status"];
|
|
199
|
+
readonly outcome?: SubagentSnapshot["outcome"];
|
|
200
|
+
readonly worktreeBranch?: string;
|
|
201
|
+
readonly elapsed?: string;
|
|
202
|
+
readonly artifactSaveFailed?: boolean;
|
|
203
|
+
readonly fullResultSaved?: boolean;
|
|
204
|
+
readonly structured?: unknown;
|
|
205
|
+
readonly structuredArtifactPath?: string;
|
|
174
206
|
}>;
|
|
207
|
+
/** Display-only projection for the custom message renderer. */
|
|
208
|
+
readonly displayContent?: string;
|
|
175
209
|
}
|
|
176
210
|
|
|
177
211
|
interface SubagentResultEntryData {
|
|
@@ -199,13 +233,17 @@ function describeSubagent(snap: SubagentSnapshot) {
|
|
|
199
233
|
return `${snap.id} [${snap.status}] "${snap.title}" (${details.join(", ")})`;
|
|
200
234
|
}
|
|
201
235
|
|
|
236
|
+
function subagentResultContent(snap: SubagentSnapshot) {
|
|
237
|
+
return snap.structuredResult?.json ?? (snap.finalText || "(no output)");
|
|
238
|
+
}
|
|
239
|
+
|
|
202
240
|
export function truncatedOutput(
|
|
203
241
|
snap: SubagentSnapshot,
|
|
204
242
|
maxBytes = SUBAGENT_OUTPUT_MAX_BYTES,
|
|
205
243
|
writeArtifact: (content: string) => string = (content) =>
|
|
206
244
|
persistResultArtifact(getAgentDir(), content),
|
|
207
245
|
): string {
|
|
208
|
-
const output = snap
|
|
246
|
+
const output = subagentResultContent(snap);
|
|
209
247
|
return projectResult(output, {
|
|
210
248
|
maxBytes: Math.min(maxBytes, DEFAULT_MAX_BYTES),
|
|
211
249
|
maxLines: Math.min(600, DEFAULT_MAX_LINES),
|
|
@@ -213,12 +251,53 @@ export function truncatedOutput(
|
|
|
213
251
|
}).text;
|
|
214
252
|
}
|
|
215
253
|
|
|
254
|
+
function projectSubagentOutput(
|
|
255
|
+
snap: SubagentSnapshot,
|
|
256
|
+
maxBytes: number,
|
|
257
|
+
): ResultProjection {
|
|
258
|
+
const output = subagentResultContent(snap);
|
|
259
|
+
return projectResult(output, {
|
|
260
|
+
maxBytes: Math.min(maxBytes, DEFAULT_MAX_BYTES),
|
|
261
|
+
maxLines: Math.min(600, DEFAULT_MAX_LINES),
|
|
262
|
+
writeArtifact: (content) => persistResultArtifact(getAgentDir(), content),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
type OutputProjection = Pick<
|
|
267
|
+
ResultProjection,
|
|
268
|
+
"text" | "artifactPath" | "artifactSaveFailed"
|
|
269
|
+
>;
|
|
270
|
+
|
|
271
|
+
function normalizeProjection(
|
|
272
|
+
output: string | OutputProjection,
|
|
273
|
+
): OutputProjection {
|
|
274
|
+
return typeof output === "string" ? { text: output } : output;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function boundAutomaticResultBatch(content: string) {
|
|
278
|
+
const probe = truncateHead(content, {
|
|
279
|
+
maxBytes: AUTOMATIC_OUTPUT_MAX_BYTES,
|
|
280
|
+
maxLines: Number.MAX_SAFE_INTEGER,
|
|
281
|
+
});
|
|
282
|
+
if (!probe.truncated) return content;
|
|
283
|
+
|
|
284
|
+
const noticeBytes = Buffer.byteLength(
|
|
285
|
+
AUTOMATIC_BATCH_TRUNCATION_NOTICE,
|
|
286
|
+
"utf8",
|
|
287
|
+
);
|
|
288
|
+
const bounded = truncateHead(content, {
|
|
289
|
+
maxBytes: Math.max(0, AUTOMATIC_OUTPUT_MAX_BYTES - noticeBytes),
|
|
290
|
+
maxLines: Number.MAX_SAFE_INTEGER,
|
|
291
|
+
});
|
|
292
|
+
return `${bounded.content}${AUTOMATIC_BATCH_TRUNCATION_NOTICE}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
216
295
|
export function createSubagentResultDispatcher(
|
|
217
296
|
pi: ExtensionAPI,
|
|
218
297
|
outputFor: (
|
|
219
298
|
snap: SubagentSnapshot,
|
|
220
299
|
maxBytes: number,
|
|
221
|
-
) => string =
|
|
300
|
+
) => string | OutputProjection = projectSubagentOutput,
|
|
222
301
|
getContextUsage: () => ParentContextUsage | undefined = () => undefined,
|
|
223
302
|
) {
|
|
224
303
|
return (snaps: readonly SubagentSnapshot[]) => {
|
|
@@ -239,12 +318,12 @@ export function createSubagentResultDispatcher(
|
|
|
239
318
|
) +
|
|
240
319
|
Math.max(0, snaps.length - 1) * 2;
|
|
241
320
|
const projectionBatchBytes = Math.max(
|
|
242
|
-
|
|
321
|
+
0,
|
|
243
322
|
AUTOMATIC_OUTPUT_MAX_BYTES - wrapperBytes,
|
|
244
323
|
);
|
|
245
324
|
const allocation = allocateResultBudgets(
|
|
246
325
|
snaps.map((snap) =>
|
|
247
|
-
Buffer.byteLength(snap
|
|
326
|
+
Buffer.byteLength(subagentResultContent(snap), "utf8"),
|
|
248
327
|
),
|
|
249
328
|
getContextUsage(),
|
|
250
329
|
{
|
|
@@ -256,34 +335,86 @@ export function createSubagentResultDispatcher(
|
|
|
256
335
|
fixedBytes: wrapperBytes,
|
|
257
336
|
},
|
|
258
337
|
);
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
338
|
+
const projections = snaps.map((snap, index) =>
|
|
339
|
+
normalizeProjection(outputFor(snap, allocation.budgets[index]!)),
|
|
340
|
+
);
|
|
341
|
+
const outputs = projections.map((projection) => projection.text);
|
|
342
|
+
const displayContent = boundAutomaticResultBatch(
|
|
343
|
+
snaps
|
|
344
|
+
.map((snap, index) =>
|
|
345
|
+
buildSubagentResultDisplayMessage({
|
|
346
|
+
id: snap.id,
|
|
347
|
+
title: snap.title,
|
|
348
|
+
status: snap.status,
|
|
349
|
+
errorText: snap.errorText,
|
|
350
|
+
output: outputs[index]!,
|
|
351
|
+
}),
|
|
352
|
+
)
|
|
353
|
+
.join("\n\n"),
|
|
354
|
+
);
|
|
355
|
+
const content = boundAutomaticResultBatch(
|
|
356
|
+
snaps
|
|
357
|
+
.map((snap, index) =>
|
|
358
|
+
buildSubagentResultMessage({
|
|
359
|
+
id: snap.id,
|
|
360
|
+
title: snap.title,
|
|
361
|
+
status: snap.status,
|
|
362
|
+
errorText: snap.errorText,
|
|
363
|
+
output: outputs[index]!,
|
|
364
|
+
}),
|
|
365
|
+
)
|
|
366
|
+
.join("\n\n"),
|
|
367
|
+
);
|
|
270
368
|
const details: SubagentResultDetails =
|
|
271
369
|
snaps.length === 1
|
|
272
370
|
? {
|
|
273
371
|
id: snaps[0]!.id,
|
|
274
372
|
title: snaps[0]!.title,
|
|
275
373
|
status: snaps[0]!.status,
|
|
374
|
+
...(snaps[0]!.outcome ? { outcome: snaps[0]!.outcome } : {}),
|
|
375
|
+
...(snaps[0]!.worktreeBranch
|
|
376
|
+
? { worktreeBranch: snaps[0]!.worktreeBranch }
|
|
377
|
+
: {}),
|
|
378
|
+
elapsed: formatElapsed(snaps[0]!),
|
|
379
|
+
...(projections[0]!.artifactPath ? { fullResultSaved: true } : {}),
|
|
380
|
+
...(projections[0]!.artifactSaveFailed
|
|
381
|
+
? { artifactSaveFailed: true }
|
|
382
|
+
: {}),
|
|
383
|
+
...(snaps[0]!.structuredResult
|
|
384
|
+
? {
|
|
385
|
+
structured: snaps[0]!.structuredResult.value,
|
|
386
|
+
structuredArtifactPath:
|
|
387
|
+
snaps[0]!.structuredResult.artifactPath,
|
|
388
|
+
}
|
|
389
|
+
: {}),
|
|
276
390
|
}
|
|
277
391
|
: {
|
|
278
392
|
count: snaps.length,
|
|
279
|
-
results: snaps.map((snap) => ({
|
|
393
|
+
results: snaps.map((snap, index) => ({
|
|
280
394
|
id: snap.id,
|
|
281
395
|
title: snap.title,
|
|
282
396
|
status: snap.status,
|
|
397
|
+
...(snap.outcome ? { outcome: snap.outcome } : {}),
|
|
398
|
+
...(snap.worktreeBranch
|
|
399
|
+
? { worktreeBranch: snap.worktreeBranch }
|
|
400
|
+
: {}),
|
|
401
|
+
elapsed: formatElapsed(snap),
|
|
402
|
+
...(projections[index]!.artifactPath
|
|
403
|
+
? { fullResultSaved: true }
|
|
404
|
+
: {}),
|
|
405
|
+
...(projections[index]!.artifactSaveFailed
|
|
406
|
+
? { artifactSaveFailed: true }
|
|
407
|
+
: {}),
|
|
408
|
+
...(snap.structuredResult
|
|
409
|
+
? {
|
|
410
|
+
structured: snap.structuredResult.value,
|
|
411
|
+
structuredArtifactPath: snap.structuredResult.artifactPath,
|
|
412
|
+
}
|
|
413
|
+
: {}),
|
|
283
414
|
})),
|
|
284
415
|
};
|
|
285
416
|
pi.appendEntry<SubagentResultEntryData>("subagent-result", {
|
|
286
|
-
content,
|
|
417
|
+
content: displayContent,
|
|
287
418
|
details,
|
|
288
419
|
});
|
|
289
420
|
pi.sendMessage(
|
|
@@ -291,7 +422,7 @@ export function createSubagentResultDispatcher(
|
|
|
291
422
|
customType: "subagent-result",
|
|
292
423
|
content,
|
|
293
424
|
display: false,
|
|
294
|
-
details,
|
|
425
|
+
details: { ...details, displayContent },
|
|
295
426
|
},
|
|
296
427
|
{ deliverAs: "followUp", triggerTurn: true },
|
|
297
428
|
);
|
|
@@ -304,36 +435,52 @@ function renderSubagentResult(
|
|
|
304
435
|
content: string,
|
|
305
436
|
details: SubagentResultDetails,
|
|
306
437
|
expanded: boolean,
|
|
438
|
+
resultDisplay: DetailDisplay,
|
|
307
439
|
theme: SubagentResultTheme,
|
|
308
440
|
) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
441
|
+
const displayContent = sanitizeText(
|
|
442
|
+
details.displayContent ?? stripSubagentResultTransportInstruction(content),
|
|
443
|
+
);
|
|
444
|
+
const results = details.results?.length
|
|
445
|
+
? details.results
|
|
446
|
+
: details.id
|
|
447
|
+
? [
|
|
448
|
+
{
|
|
449
|
+
id: details.id,
|
|
450
|
+
title: details.title,
|
|
451
|
+
status: details.status,
|
|
452
|
+
outcome: details.outcome,
|
|
453
|
+
worktreeBranch: details.worktreeBranch,
|
|
454
|
+
elapsed: details.elapsed,
|
|
455
|
+
artifactSaveFailed: details.artifactSaveFailed,
|
|
456
|
+
fullResultSaved: details.fullResultSaved,
|
|
457
|
+
},
|
|
458
|
+
]
|
|
459
|
+
: [];
|
|
460
|
+
if (!expanded && resultDisplay === "compact") {
|
|
461
|
+
return renderWaitResultPreview(displayContent, { results }, theme);
|
|
322
462
|
}
|
|
323
463
|
|
|
324
|
-
const failed =
|
|
464
|
+
const failed = results.some((result) => result.status === "error");
|
|
465
|
+
const batched = results.length > 1;
|
|
325
466
|
const icon = failed ? theme.fg("error", "x") : theme.fg("success", "✓");
|
|
326
|
-
const header =
|
|
327
|
-
`${icon} `
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
467
|
+
const header = batched
|
|
468
|
+
? `${icon} ${theme.fg("accent", theme.bold(`${results.length} subagents`))}${theme.fg("muted", ` · ${failed ? `${results.filter((result) => result.status === "error").length} failed` : "finished"}`)}`
|
|
469
|
+
: `${icon} ` +
|
|
470
|
+
theme.fg(
|
|
471
|
+
"accent",
|
|
472
|
+
theme.bold(`subagent ${sanitizeText(details.id ?? "?")}`),
|
|
473
|
+
) +
|
|
474
|
+
theme.fg(
|
|
475
|
+
"muted",
|
|
476
|
+
` · ${sanitizeText(details.title ?? "")} · ${failed ? "failed" : "finished"}${details.elapsed ? ` · ${sanitizeText(details.elapsed)}` : ""}`,
|
|
477
|
+
);
|
|
333
478
|
|
|
334
|
-
// Remove only the summary line.
|
|
335
|
-
//
|
|
336
|
-
const body =
|
|
479
|
+
// Remove only the single-result summary line. Error lines and batched result
|
|
480
|
+
// summaries are part of the display projection and must remain visible.
|
|
481
|
+
const body = batched
|
|
482
|
+
? displayContent.trim()
|
|
483
|
+
: displayContent.split("\n").slice(1).join("\n").trim();
|
|
337
484
|
const md = new Markdown(body, 0, 0, getMarkdownTheme());
|
|
338
485
|
const container = new Text(header, 0, 0);
|
|
339
486
|
return {
|
|
@@ -348,7 +495,17 @@ function renderSubagentResult(
|
|
|
348
495
|
};
|
|
349
496
|
}
|
|
350
497
|
|
|
351
|
-
|
|
498
|
+
interface SubagentExtensionOptions {
|
|
499
|
+
readonly getResultDisplay?: () => DetailDisplay;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export default function (
|
|
503
|
+
pi: ExtensionAPI,
|
|
504
|
+
options: SubagentExtensionOptions = {},
|
|
505
|
+
) {
|
|
506
|
+
const getResultDisplay =
|
|
507
|
+
options.getResultDisplay ??
|
|
508
|
+
(() => loadSetupConfig().ui.subagentResultDisplay);
|
|
352
509
|
let runtime: SubagentRuntime | undefined;
|
|
353
510
|
let managerPromise: Promise<SubagentManagerShape> | undefined;
|
|
354
511
|
let restoredIdCounters: SubagentIdCounters = {
|
|
@@ -364,19 +521,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
364
521
|
*/
|
|
365
522
|
let settledAcknowledgedAt = 0;
|
|
366
523
|
const stripState = new BelowEditorStripState();
|
|
524
|
+
const statusWriter = createStatusWriter("subagents");
|
|
367
525
|
const widgetKey = "subagent-navigation";
|
|
368
526
|
let navigationManager: SubagentManagerShape | undefined;
|
|
527
|
+
let unregisterWebCapability: (() => void) | undefined;
|
|
369
528
|
let widgetVisible = false;
|
|
529
|
+
let widgetEntryKey: string | undefined;
|
|
370
530
|
let requestWidgetRender: (() => void) | undefined;
|
|
371
531
|
let navigationLayerRegistered = false;
|
|
372
532
|
let dashboardOpen = false;
|
|
373
533
|
const dispatchResults = createSubagentResultDispatcher(
|
|
374
534
|
pi,
|
|
375
|
-
|
|
535
|
+
projectSubagentOutput,
|
|
376
536
|
() => sessionContext?.getContextUsage(),
|
|
377
537
|
);
|
|
378
538
|
const resultDelivery = createSubagentResultDelivery<SubagentSnapshot>({
|
|
379
539
|
isIdle: () => sessionContext?.isIdle() === true,
|
|
540
|
+
owner: () =>
|
|
541
|
+
sessionContext
|
|
542
|
+
? completionOwnerFor(sessionContext.sessionManager)
|
|
543
|
+
: undefined,
|
|
380
544
|
// Every unconsumed fire-and-forget result must reach the parent. The
|
|
381
545
|
// delivery coordinator batches results that settled while it was busy.
|
|
382
546
|
deliver: dispatchResults,
|
|
@@ -398,10 +562,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
398
562
|
|
|
399
563
|
/** Resolve the manager service once per runtime and wire the extension hooks. */
|
|
400
564
|
const getManager = () => {
|
|
565
|
+
const scope = sessionContext?.sessionManager;
|
|
401
566
|
managerPromise ??= getRuntime()
|
|
402
567
|
.runPromise(SubagentManager)
|
|
403
568
|
.then((manager) => {
|
|
404
569
|
navigationManager = manager;
|
|
570
|
+
unregisterWebCapability?.();
|
|
571
|
+
unregisterWebCapability =
|
|
572
|
+
scope && sessionContext?.sessionManager === scope
|
|
573
|
+
? registerWebCapability(scope, {
|
|
574
|
+
kind: "subagents",
|
|
575
|
+
snapshot: () => projectSubagentCapability(manager.view.list()),
|
|
576
|
+
subscribe: (listener) => manager.view.subscribe(listener),
|
|
577
|
+
})
|
|
578
|
+
: undefined;
|
|
405
579
|
manager.view.setOnSettled(onSettled);
|
|
406
580
|
unsubStatus?.();
|
|
407
581
|
unsubStatus = manager.view.subscribe(() => updateStatus(manager));
|
|
@@ -422,10 +596,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
422
596
|
const updateSubagentWidget = () => {
|
|
423
597
|
const ctx = sessionContext;
|
|
424
598
|
if (!ctx || ctx.mode !== "tui") return;
|
|
425
|
-
const
|
|
426
|
-
|
|
599
|
+
const entry = stripEntry();
|
|
600
|
+
const visible = Boolean(entry);
|
|
601
|
+
const entryKey = subagentStripEntryKey(entry);
|
|
602
|
+
if (visible === widgetVisible) {
|
|
603
|
+
if (visible && entryKey !== widgetEntryKey) {
|
|
604
|
+
widgetEntryKey = entryKey;
|
|
605
|
+
requestWidgetRender?.();
|
|
606
|
+
}
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
427
609
|
if (!visible) {
|
|
428
610
|
stripState.focused = false;
|
|
611
|
+
widgetEntryKey = undefined;
|
|
429
612
|
requestWidgetRender = undefined;
|
|
430
613
|
ctx.ui.setWidget(widgetKey, undefined);
|
|
431
614
|
widgetVisible = false;
|
|
@@ -440,6 +623,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
440
623
|
{ placement: "belowEditor" },
|
|
441
624
|
);
|
|
442
625
|
widgetVisible = true;
|
|
626
|
+
widgetEntryKey = entryKey;
|
|
443
627
|
};
|
|
444
628
|
|
|
445
629
|
const updateStatus = (manager: SubagentManagerShape) => {
|
|
@@ -451,8 +635,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
451
635
|
// In the TUI the below-editor strip already reports the same activity and
|
|
452
636
|
// carries the manage affordance, so a footer status line would repeat it.
|
|
453
637
|
const tui = sessionContext?.mode === "tui";
|
|
454
|
-
|
|
455
|
-
|
|
638
|
+
statusWriter.write(
|
|
639
|
+
ui,
|
|
456
640
|
!tui && hasActivity(counts)
|
|
457
641
|
? formatActivityStatus(ui.theme, "subagents", counts)
|
|
458
642
|
: undefined,
|
|
@@ -586,16 +770,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
586
770
|
resultDelivery.clear();
|
|
587
771
|
unsubStatus?.();
|
|
588
772
|
unsubStatus = undefined;
|
|
773
|
+
unregisterWebCapability?.();
|
|
774
|
+
unregisterWebCapability = undefined;
|
|
589
775
|
try {
|
|
590
776
|
ui?.setStatus("subagents", undefined);
|
|
591
777
|
sessionContext?.ui.setWidget(widgetKey, undefined);
|
|
592
778
|
} catch {
|
|
593
779
|
// UI may already be disposed.
|
|
594
780
|
}
|
|
781
|
+
statusWriter.reset();
|
|
595
782
|
sessionContext = undefined;
|
|
596
783
|
ui = undefined;
|
|
597
784
|
navigationManager = undefined;
|
|
598
785
|
widgetVisible = false;
|
|
786
|
+
widgetEntryKey = undefined;
|
|
599
787
|
requestWidgetRender = undefined;
|
|
600
788
|
stripState.focused = false;
|
|
601
789
|
dashboardOpen = false;
|
|
@@ -770,6 +958,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
770
958
|
...(agentType?.body ? { appendSystemPrompt: [agentType.body] } : {}),
|
|
771
959
|
...(childTools ? { tools: childTools } : {}),
|
|
772
960
|
...(agentType ? { agentTypeName: agentType.name } : {}),
|
|
961
|
+
...(params.output_schema !== undefined
|
|
962
|
+
? { outputSchema: params.output_schema }
|
|
963
|
+
: {}),
|
|
773
964
|
...(worktree ? { worktree: { ...worktree, repoCwd: cwd } } : {}),
|
|
774
965
|
parent: {
|
|
775
966
|
parentCwd: ctx.cwd,
|
|
@@ -791,7 +982,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
791
982
|
} catch (error) {
|
|
792
983
|
// The session scope owns reclamation, but it never opened, so this
|
|
793
984
|
// worktree would otherwise be orphaned on disk.
|
|
794
|
-
if (worktree)
|
|
985
|
+
if (worktree) {
|
|
986
|
+
const spawnError =
|
|
987
|
+
error instanceof Error ? error.message : String(error);
|
|
988
|
+
let cleanupWarning: string | undefined;
|
|
989
|
+
let cleanupError: unknown;
|
|
990
|
+
try {
|
|
991
|
+
const cleanup = await reclaimWorktree(cwd, worktree);
|
|
992
|
+
cleanupWarning = formatWorktreeCleanupWarning(
|
|
993
|
+
cleanup,
|
|
994
|
+
worktree.path,
|
|
995
|
+
);
|
|
996
|
+
} catch (failure) {
|
|
997
|
+
cleanupError = failure;
|
|
998
|
+
}
|
|
999
|
+
if (cleanupError) {
|
|
1000
|
+
const reason =
|
|
1001
|
+
cleanupError instanceof Error
|
|
1002
|
+
? cleanupError.message
|
|
1003
|
+
: String(cleanupError);
|
|
1004
|
+
throw new Error(
|
|
1005
|
+
`${spawnError}; worktree cleanup failed: ${reason}; checkout preserved at ${worktree.path}`,
|
|
1006
|
+
{ cause: error },
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
if (cleanupWarning) {
|
|
1010
|
+
throw new Error(
|
|
1011
|
+
`${spawnError}; worktree cleanup warning: ${cleanupWarning}`,
|
|
1012
|
+
{ cause: error },
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
795
1016
|
throw error;
|
|
796
1017
|
}
|
|
797
1018
|
persistId(snap.id);
|
|
@@ -809,6 +1030,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
809
1030
|
...(worktree ? { worktreeBranch: worktree.branch } : {}),
|
|
810
1031
|
...(agentType ? { agentTypeName: agentType.name } : {}),
|
|
811
1032
|
...(childTools ? { tools: childTools } : {}),
|
|
1033
|
+
...(params.output_schema !== undefined
|
|
1034
|
+
? { structured: true }
|
|
1035
|
+
: {}),
|
|
812
1036
|
}),
|
|
813
1037
|
},
|
|
814
1038
|
],
|
|
@@ -819,6 +1043,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
819
1043
|
harness,
|
|
820
1044
|
model: snap.meta.modelLabel,
|
|
821
1045
|
...(agentType ? { agentType: agentType.name } : {}),
|
|
1046
|
+
...(params.output_schema !== undefined ? { structured: true } : {}),
|
|
822
1047
|
},
|
|
823
1048
|
};
|
|
824
1049
|
},
|
|
@@ -941,7 +1166,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
941
1166
|
);
|
|
942
1167
|
const allocation = allocateResultBudgets(
|
|
943
1168
|
resultEntries.map(({ snap }) =>
|
|
944
|
-
Buffer.byteLength(snap
|
|
1169
|
+
Buffer.byteLength(subagentResultContent(snap), "utf8"),
|
|
945
1170
|
),
|
|
946
1171
|
ctx.getContextUsage(),
|
|
947
1172
|
{
|
|
@@ -954,10 +1179,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
954
1179
|
},
|
|
955
1180
|
);
|
|
956
1181
|
let resultIndex = 0;
|
|
1182
|
+
const artifactSaveFailures = new Set<string>();
|
|
1183
|
+
const fullResultsSaved = new Set<string>();
|
|
957
1184
|
const sections = entries.map((entry) => {
|
|
958
1185
|
if ("section" in entry) return entry.section;
|
|
959
1186
|
const outputBudget = allocation.budgets[resultIndex++]!;
|
|
960
|
-
|
|
1187
|
+
const projection = projectSubagentOutput(entry.snap, outputBudget);
|
|
1188
|
+
if (projection.artifactSaveFailed) artifactSaveFailures.add(entry.id);
|
|
1189
|
+
if (projection.artifactPath) fullResultsSaved.add(entry.id);
|
|
1190
|
+
return `${entry.header}\n\n${projection.text}`;
|
|
961
1191
|
});
|
|
962
1192
|
|
|
963
1193
|
const combined = sections.join("\n\n---\n\n");
|
|
@@ -973,7 +1203,26 @@ export default function (pi: ExtensionAPI) {
|
|
|
973
1203
|
details: {
|
|
974
1204
|
results: ids.map((id) => {
|
|
975
1205
|
const snap = manager.view.get(id);
|
|
976
|
-
return {
|
|
1206
|
+
return {
|
|
1207
|
+
id,
|
|
1208
|
+
title: snap?.title,
|
|
1209
|
+
status: snap?.status,
|
|
1210
|
+
...(snap?.outcome ? { outcome: snap.outcome } : {}),
|
|
1211
|
+
...(snap?.worktreeBranch
|
|
1212
|
+
? { worktreeBranch: snap.worktreeBranch }
|
|
1213
|
+
: {}),
|
|
1214
|
+
...(snap ? { elapsed: formatElapsed(snap) } : {}),
|
|
1215
|
+
...(fullResultsSaved.has(id) ? { fullResultSaved: true } : {}),
|
|
1216
|
+
...(artifactSaveFailures.has(id)
|
|
1217
|
+
? { artifactSaveFailed: true }
|
|
1218
|
+
: {}),
|
|
1219
|
+
...(snap?.structuredResult
|
|
1220
|
+
? {
|
|
1221
|
+
structured: snap.structuredResult.value,
|
|
1222
|
+
structuredArtifactPath: snap.structuredResult.artifactPath,
|
|
1223
|
+
}
|
|
1224
|
+
: {}),
|
|
1225
|
+
};
|
|
977
1226
|
}),
|
|
978
1227
|
},
|
|
979
1228
|
};
|
|
@@ -998,7 +1247,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
998
1247
|
return renderWaitResult(
|
|
999
1248
|
content,
|
|
1000
1249
|
result.details as WaitResultDetails | undefined,
|
|
1001
|
-
expanded ||
|
|
1250
|
+
expanded || getResultDisplay() === "full",
|
|
1002
1251
|
theme,
|
|
1003
1252
|
);
|
|
1004
1253
|
},
|
|
@@ -1193,6 +1442,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1193
1442
|
content,
|
|
1194
1443
|
(message.details ?? {}) as SubagentResultDetails,
|
|
1195
1444
|
expanded,
|
|
1445
|
+
getResultDisplay(),
|
|
1196
1446
|
theme,
|
|
1197
1447
|
);
|
|
1198
1448
|
},
|
|
@@ -1205,6 +1455,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1205
1455
|
entry.data?.content ?? "",
|
|
1206
1456
|
entry.data?.details ?? {},
|
|
1207
1457
|
expanded,
|
|
1458
|
+
getResultDisplay(),
|
|
1208
1459
|
theme,
|
|
1209
1460
|
),
|
|
1210
1461
|
);
|
|
@@ -1247,7 +1498,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1247
1498
|
.filter(Boolean)
|
|
1248
1499
|
.join("\n\n");
|
|
1249
1500
|
|
|
1250
|
-
if (expanded ||
|
|
1501
|
+
if (expanded || getResultDisplay() === "full") {
|
|
1251
1502
|
const md = new Markdown(body, 0, 0, getMarkdownTheme());
|
|
1252
1503
|
const container = new Text(header, 0, 0);
|
|
1253
1504
|
return {
|