@oh-my-pi/pi-coding-agent 15.10.12 → 15.11.1
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 +90 -4
- package/dist/cli.js +869 -825
- package/dist/types/async/index.d.ts +0 -1
- package/dist/types/capability/mcp.d.ts +1 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +5 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +66 -34
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/history-protocol.d.ts +14 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/irc/bus.d.ts +66 -0
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/oauth-flow.d.ts +6 -1
- package/dist/types/mcp/transports/stdio.d.ts +1 -0
- package/dist/types/mcp/types.d.ts +2 -0
- package/dist/types/modes/components/agent-hub.d.ts +30 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +10 -4
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +3 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/transcript-container.d.ts +3 -2
- package/dist/types/modes/components/ttsr-notification.d.ts +5 -1
- package/dist/types/modes/components/welcome.d.ts +3 -9
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/tool-args-reveal.d.ts +43 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -2
- package/dist/types/modes/theme/theme.d.ts +3 -1
- package/dist/types/modes/types.d.ts +3 -2
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
- package/dist/types/registry/agent-lifecycle.d.ts +51 -0
- package/dist/types/registry/agent-registry.d.ts +16 -5
- package/dist/types/session/agent-session.d.ts +35 -30
- package/dist/types/session/messages.d.ts +2 -4
- package/dist/types/session/session-history-format.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +21 -3
- package/dist/types/session/streaming-output.d.ts +23 -0
- package/dist/types/task/executor.d.ts +11 -2
- package/dist/types/task/index.d.ts +11 -4
- package/dist/types/task/output-manager.d.ts +0 -7
- package/dist/types/task/repair-args.d.ts +8 -7
- package/dist/types/task/types.d.ts +55 -51
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/find.d.ts +0 -11
- package/dist/types/tools/grouped-file-output.d.ts +0 -49
- package/dist/types/tools/index.d.ts +1 -3
- package/dist/types/tools/irc.d.ts +76 -38
- package/dist/types/tools/job.d.ts +7 -1
- package/dist/types/tools/render-utils.d.ts +22 -0
- package/examples/extensions/with-deps/package.json +1 -0
- package/package.json +11 -10
- package/scripts/bundle-dist.ts +28 -19
- package/src/async/index.ts +0 -1
- package/src/capability/mcp.ts +1 -0
- package/src/cli/gallery-cli.ts +6 -5
- package/src/cli/gallery-fixtures/agentic.ts +230 -115
- package/src/cli/gallery-fixtures/types.ts +5 -0
- package/src/cli.ts +20 -6
- package/src/commit/agentic/tools/analyze-file.ts +38 -19
- package/src/config/keybindings.ts +6 -1
- package/src/config/mcp-schema.json +4 -0
- package/src/config/settings-schema.ts +68 -41
- package/src/config/settings.ts +7 -0
- package/src/edit/renderer.ts +96 -46
- package/src/eval/__tests__/agent-bridge.test.ts +5 -3
- package/src/eval/agent-bridge.ts +3 -16
- package/src/eval/js/shared/prelude.txt +1 -1
- package/src/eval/py/prelude.py +5 -6
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +44 -14
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +9 -9
- package/src/internal-urls/history-protocol.ts +113 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/router.ts +3 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/irc/bus.ts +292 -0
- package/src/main.ts +8 -60
- package/src/mcp/manager.ts +3 -0
- package/src/mcp/oauth-discovery.ts +27 -2
- package/src/mcp/oauth-flow.ts +47 -1
- package/src/mcp/transports/stdio.ts +3 -0
- package/src/mcp/types.ts +2 -0
- package/src/modes/components/{session-observer-overlay.ts → agent-hub.ts} +586 -367
- package/src/modes/components/assistant-message.ts +15 -0
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/compaction-summary-message.ts +68 -32
- package/src/modes/components/custom-editor.ts +10 -0
- package/src/modes/components/mcp-add-wizard.ts +13 -0
- package/src/modes/components/settings-selector.ts +2 -0
- package/src/modes/components/status-line/component.ts +22 -12
- package/src/modes/components/status-line/types.ts +3 -0
- package/src/modes/components/tool-execution.ts +31 -1
- package/src/modes/components/transcript-container.ts +99 -18
- package/src/modes/components/tree-selector.ts +6 -1
- package/src/modes/components/ttsr-notification.ts +72 -30
- package/src/modes/components/welcome.ts +9 -33
- package/src/modes/controllers/event-controller.ts +93 -4
- package/src/modes/controllers/extension-ui-controller.ts +8 -8
- package/src/modes/controllers/input-controller.ts +18 -2
- package/src/modes/controllers/mcp-command-controller.ts +34 -2
- package/src/modes/controllers/selector-controller.ts +25 -17
- package/src/modes/controllers/tool-args-reveal.ts +174 -0
- package/src/modes/interactive-mode.ts +17 -15
- package/src/modes/theme/theme.ts +24 -5
- package/src/modes/types.ts +3 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +51 -49
- package/src/prompts/system/irc-incoming.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +0 -5
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/system/workflow-notice.md +2 -2
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/irc.md +29 -19
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/task-summary.md +5 -16
- package/src/prompts/tools/task.md +43 -29
- package/src/registry/agent-lifecycle.ts +218 -0
- package/src/registry/agent-registry.ts +16 -5
- package/src/sdk.ts +29 -9
- package/src/session/agent-session.ts +268 -241
- package/src/session/messages.ts +11 -78
- package/src/session/session-history-format.ts +246 -0
- package/src/session/session-manager.ts +59 -5
- package/src/session/streaming-output.ts +60 -0
- package/src/task/executor.ts +855 -466
- package/src/task/index.ts +723 -794
- package/src/task/output-manager.ts +0 -11
- package/src/task/render.ts +142 -66
- package/src/task/repair-args.ts +21 -9
- package/src/task/types.ts +73 -66
- package/src/tools/ask.ts +4 -2
- package/src/tools/bash.ts +15 -5
- package/src/tools/browser/tab-worker.ts +26 -7
- package/src/tools/browser.ts +28 -1
- package/src/tools/find.ts +2 -27
- package/src/tools/grouped-file-output.ts +1 -118
- package/src/tools/index.ts +4 -12
- package/src/tools/irc.ts +596 -171
- package/src/tools/job.ts +41 -7
- package/src/tools/read.ts +57 -1
- package/src/tools/render-utils.ts +56 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +4 -1
- package/src/tools/write.ts +65 -47
- package/src/web/search/providers/anthropic.ts +29 -4
- package/dist/types/async/support.d.ts +0 -2
- package/dist/types/modes/components/session-observer-overlay.d.ts +0 -11
- package/dist/types/task/simple-mode.d.ts +0 -8
- package/src/async/support.ts +0 -5
- package/src/task/simple-mode.ts +0 -27
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { THINKING_EFFORTS } from "@oh-my-pi/pi-ai";
|
|
2
|
-
import { TASK_SIMPLE_MODES } from "../task/simple-mode";
|
|
3
2
|
import { AUTO_THINKING, getConfiguredThinkingLevelMetadata, getThinkingLevelMetadata } from "../thinking";
|
|
4
3
|
import {
|
|
5
4
|
TINY_MODEL_DEVICE_DEFAULT,
|
|
@@ -453,6 +452,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
453
452
|
description: "Use the session name color for the editor border and status line gap",
|
|
454
453
|
},
|
|
455
454
|
},
|
|
455
|
+
|
|
456
|
+
"statusLine.transparent": {
|
|
457
|
+
type: "boolean",
|
|
458
|
+
default: false,
|
|
459
|
+
ui: {
|
|
460
|
+
tab: "appearance",
|
|
461
|
+
label: "Transparent Status Line",
|
|
462
|
+
description:
|
|
463
|
+
"Use the terminal's default background for the status line instead of the theme's `statusLineBg`. Powerline end caps are dropped because they need a contrasting fill to bridge into the surrounding terminal.",
|
|
464
|
+
},
|
|
465
|
+
},
|
|
456
466
|
"tools.artifactSpillThreshold": {
|
|
457
467
|
type: "number",
|
|
458
468
|
default: 50,
|
|
@@ -669,7 +679,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
669
679
|
ui: {
|
|
670
680
|
tab: "appearance",
|
|
671
681
|
label: "Smooth Streaming",
|
|
672
|
-
description: "Reveal assistant text smoothly while
|
|
682
|
+
description: "Reveal assistant text and streamed tool input smoothly while chunks arrive",
|
|
673
683
|
},
|
|
674
684
|
},
|
|
675
685
|
|
|
@@ -1177,13 +1187,13 @@ export const SETTINGS_SCHEMA = {
|
|
|
1177
1187
|
|
|
1178
1188
|
"compaction.strategy": {
|
|
1179
1189
|
type: "enum",
|
|
1180
|
-
values: ["context-full", "handoff", "shake", "off"] as const,
|
|
1190
|
+
values: ["context-full", "handoff", "shake", "snapcompact", "off"] as const,
|
|
1181
1191
|
default: "context-full",
|
|
1182
1192
|
ui: {
|
|
1183
1193
|
tab: "context",
|
|
1184
1194
|
label: "Compaction Strategy",
|
|
1185
1195
|
description:
|
|
1186
|
-
"Choose in-place context-full maintenance, auto-handoff, surgical shake (drop heavy content), or disable auto maintenance (off)",
|
|
1196
|
+
"Choose in-place context-full maintenance, auto-handoff, surgical shake (drop heavy content), snapcompact (archive history as dense images), or disable auto maintenance (off)",
|
|
1187
1197
|
options: [
|
|
1188
1198
|
{
|
|
1189
1199
|
value: "context-full",
|
|
@@ -1196,6 +1206,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
1196
1206
|
label: "Shake",
|
|
1197
1207
|
description: "Drop heavy content (tool results + large blocks) in place; recover via artifact",
|
|
1198
1208
|
},
|
|
1209
|
+
{
|
|
1210
|
+
value: "snapcompact",
|
|
1211
|
+
label: "Snapcompact",
|
|
1212
|
+
description: "Archive history onto dense bitmap images the model reads back; no LLM call",
|
|
1213
|
+
},
|
|
1199
1214
|
{
|
|
1200
1215
|
value: "off",
|
|
1201
1216
|
label: "Off",
|
|
@@ -1326,6 +1341,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
1326
1341
|
],
|
|
1327
1342
|
},
|
|
1328
1343
|
},
|
|
1344
|
+
|
|
1345
|
+
"compaction.supersedeReads": {
|
|
1346
|
+
type: "boolean",
|
|
1347
|
+
default: true,
|
|
1348
|
+
ui: {
|
|
1349
|
+
tab: "context",
|
|
1350
|
+
label: "Supersede Stale Reads",
|
|
1351
|
+
description: "Prune older read results when the same file is read again (cache-aware, runs every turn)",
|
|
1352
|
+
},
|
|
1353
|
+
},
|
|
1354
|
+
|
|
1329
1355
|
// Branch summaries
|
|
1330
1356
|
"branchSummary.enabled": {
|
|
1331
1357
|
type: "boolean",
|
|
@@ -2289,24 +2315,13 @@ export const SETTINGS_SCHEMA = {
|
|
|
2289
2315
|
},
|
|
2290
2316
|
},
|
|
2291
2317
|
|
|
2292
|
-
"irc.enabled": {
|
|
2293
|
-
type: "boolean",
|
|
2294
|
-
default: true,
|
|
2295
|
-
ui: {
|
|
2296
|
-
tab: "tools",
|
|
2297
|
-
label: "IRC",
|
|
2298
|
-
description: "Enable agent-to-agent IRC messaging via the irc tool",
|
|
2299
|
-
},
|
|
2300
|
-
},
|
|
2301
|
-
|
|
2302
2318
|
"irc.timeoutMs": {
|
|
2303
2319
|
type: "number",
|
|
2304
2320
|
default: 120_000,
|
|
2305
2321
|
ui: {
|
|
2306
2322
|
tab: "tools",
|
|
2307
2323
|
label: "IRC Timeout",
|
|
2308
|
-
description:
|
|
2309
|
-
"Drop IRC messages whose recipient does not respond within this many milliseconds (0 disables the timeout)",
|
|
2324
|
+
description: "Default timeout for irc wait (and send await:true) in milliseconds; 0 disables the timeout",
|
|
2310
2325
|
options: [
|
|
2311
2326
|
{ value: "0", label: "Disabled" },
|
|
2312
2327
|
{ value: "30000", label: "30 seconds" },
|
|
@@ -2497,7 +2512,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2497
2512
|
// Async jobs
|
|
2498
2513
|
"async.enabled": {
|
|
2499
2514
|
type: "boolean",
|
|
2500
|
-
default:
|
|
2515
|
+
default: true,
|
|
2501
2516
|
ui: {
|
|
2502
2517
|
tab: "tools",
|
|
2503
2518
|
label: "Async Execution",
|
|
@@ -2747,31 +2762,14 @@ export const SETTINGS_SCHEMA = {
|
|
|
2747
2762
|
},
|
|
2748
2763
|
},
|
|
2749
2764
|
|
|
2750
|
-
"task.
|
|
2751
|
-
type: "
|
|
2752
|
-
|
|
2753
|
-
default: "schema-free",
|
|
2765
|
+
"task.batch": {
|
|
2766
|
+
type: "boolean",
|
|
2767
|
+
default: true,
|
|
2754
2768
|
ui: {
|
|
2755
2769
|
tab: "tasks",
|
|
2756
|
-
label: "Task
|
|
2757
|
-
description:
|
|
2758
|
-
|
|
2759
|
-
{
|
|
2760
|
-
value: "default",
|
|
2761
|
-
label: "Default",
|
|
2762
|
-
description: "Shared context and custom task schema are available",
|
|
2763
|
-
},
|
|
2764
|
-
{
|
|
2765
|
-
value: "schema-free",
|
|
2766
|
-
label: "Schema-free",
|
|
2767
|
-
description: "Shared context stays available, but custom task schema is disabled",
|
|
2768
|
-
},
|
|
2769
|
-
{
|
|
2770
|
-
value: "independent",
|
|
2771
|
-
label: "Independent",
|
|
2772
|
-
description: "No shared context or custom task schema; each task must stand alone",
|
|
2773
|
-
},
|
|
2774
|
-
],
|
|
2770
|
+
label: "Batch Task Calls",
|
|
2771
|
+
description:
|
|
2772
|
+
"Switch the task tool to its batch shape: one call carries { agent, context, tasks[] } — one subagent per item (with per-item isolation) and a required shared context prepended to every assignment. With async.enabled=true, each spawn runs as an independent background agent with the normal idle/parked lifecycle; otherwise the call blocks for merged results. Disable to restore the flat single-spawn schema.",
|
|
2775
2773
|
},
|
|
2776
2774
|
},
|
|
2777
2775
|
|
|
@@ -2841,6 +2839,34 @@ export const SETTINGS_SCHEMA = {
|
|
|
2841
2839
|
},
|
|
2842
2840
|
},
|
|
2843
2841
|
|
|
2842
|
+
"task.agentIdleTtlMs": {
|
|
2843
|
+
type: "number",
|
|
2844
|
+
default: 420_000,
|
|
2845
|
+
ui: {
|
|
2846
|
+
tab: "tasks",
|
|
2847
|
+
label: "Agent Idle TTL",
|
|
2848
|
+
description:
|
|
2849
|
+
"How long an idle subagent stays live in memory before being parked to disk (ms). Parked agents are revived automatically when messaged or resumed. 0 keeps idle agents live until exit.",
|
|
2850
|
+
},
|
|
2851
|
+
},
|
|
2852
|
+
|
|
2853
|
+
"task.softRequestBudget": {
|
|
2854
|
+
type: "number",
|
|
2855
|
+
default: 90,
|
|
2856
|
+
ui: {
|
|
2857
|
+
tab: "tasks",
|
|
2858
|
+
label: "Soft Subagent Request Budget",
|
|
2859
|
+
description:
|
|
2860
|
+
"Soft per-subagent request budget (assistant requests per run). Crossing it injects one steering notice asking the subagent to wrap up; at 1.5x the budget the run is aborted gracefully, salvaging partial output. 0 disables the guard. Bundled explore/quick_task agents use a lower built-in budget.",
|
|
2861
|
+
options: [
|
|
2862
|
+
{ value: "0", label: "Disabled" },
|
|
2863
|
+
{ value: "40", label: "40 requests" },
|
|
2864
|
+
{ value: "90", label: "90 requests", description: "Default" },
|
|
2865
|
+
{ value: "150", label: "150 requests" },
|
|
2866
|
+
],
|
|
2867
|
+
},
|
|
2868
|
+
},
|
|
2869
|
+
|
|
2844
2870
|
"task.disabledAgents": {
|
|
2845
2871
|
type: "array",
|
|
2846
2872
|
default: [] as string[],
|
|
@@ -3353,7 +3379,7 @@ export type TreeFilterMode = SettingValue<"treeFilterMode">;
|
|
|
3353
3379
|
|
|
3354
3380
|
export interface CompactionSettings {
|
|
3355
3381
|
enabled: boolean;
|
|
3356
|
-
strategy: "context-full" | "handoff" | "shake" | "off";
|
|
3382
|
+
strategy: "context-full" | "handoff" | "shake" | "snapcompact" | "off";
|
|
3357
3383
|
thresholdPercent: number;
|
|
3358
3384
|
thresholdTokens: number;
|
|
3359
3385
|
reserveTokens: number;
|
|
@@ -3365,6 +3391,7 @@ export interface CompactionSettings {
|
|
|
3365
3391
|
idleEnabled: boolean;
|
|
3366
3392
|
idleThresholdTokens: number;
|
|
3367
3393
|
idleTimeoutSeconds: number;
|
|
3394
|
+
supersedeReads: boolean;
|
|
3368
3395
|
}
|
|
3369
3396
|
|
|
3370
3397
|
export interface ContextPromotionSettings {
|
package/src/config/settings.ts
CHANGED
|
@@ -745,6 +745,13 @@ export class Settings {
|
|
|
745
745
|
delete isolationObj.enabled;
|
|
746
746
|
}
|
|
747
747
|
|
|
748
|
+
// task.simple: removed — the task tool no longer accepts a per-call
|
|
749
|
+
// schema (workflows drive structured output via eval agent()) and the
|
|
750
|
+
// batch/context shape is gated by task.batch instead.
|
|
751
|
+
if (taskObj && "simple" in taskObj) {
|
|
752
|
+
delete taskObj.simple;
|
|
753
|
+
}
|
|
754
|
+
|
|
748
755
|
// task.isolation.mode: legacy values from before the pi-iso PAL refactor.
|
|
749
756
|
// `worktree` was git worktree → now lives under `rcopy`. `fuse-overlay`
|
|
750
757
|
// and `fuse-projfs` are now the platform-named `overlayfs` / `projfs`
|
package/src/edit/renderer.ts
CHANGED
|
@@ -12,13 +12,17 @@ import { renderDiff as renderDiffColored } from "../modes/components/diff";
|
|
|
12
12
|
import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
|
|
13
13
|
import type { OutputMeta } from "../tools/output-meta";
|
|
14
14
|
import {
|
|
15
|
+
cachedRenderedString,
|
|
16
|
+
createRenderedStringCache,
|
|
15
17
|
formatDiagnostics,
|
|
16
18
|
formatExpandHint,
|
|
17
19
|
formatStatusIcon,
|
|
18
20
|
getDiffStats,
|
|
19
21
|
getLspBatchRequest,
|
|
22
|
+
invalidateRenderedStringCache,
|
|
20
23
|
type LspBatchRequest,
|
|
21
24
|
PREVIEW_LIMITS,
|
|
25
|
+
type RenderedStringCache,
|
|
22
26
|
replaceTabs,
|
|
23
27
|
shortenPath,
|
|
24
28
|
truncateDiffByHunk,
|
|
@@ -138,6 +142,26 @@ export interface EditRenderContext {
|
|
|
138
142
|
}
|
|
139
143
|
|
|
140
144
|
const EDIT_STREAMING_PREVIEW_LINES = 12;
|
|
145
|
+
|
|
146
|
+
function plainDiffRender(diffText: string): string {
|
|
147
|
+
return diffText;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Lazily grown per-file preview cache slots: the file count of a streaming
|
|
152
|
+
* multi-file patch is discovered mid-stream, so a fixed-size array would
|
|
153
|
+
* silently bypass caching for late files.
|
|
154
|
+
*/
|
|
155
|
+
function previewCacheAt(caches: RenderedStringCache[] | undefined, index: number): RenderedStringCache | undefined {
|
|
156
|
+
if (!caches) return undefined;
|
|
157
|
+
let cache = caches[index];
|
|
158
|
+
if (cache === undefined) {
|
|
159
|
+
cache = createRenderedStringCache();
|
|
160
|
+
caches[index] = cache;
|
|
161
|
+
}
|
|
162
|
+
return cache;
|
|
163
|
+
}
|
|
164
|
+
|
|
141
165
|
const CALL_TEXT_PREVIEW_LINES = 6;
|
|
142
166
|
const CALL_TEXT_PREVIEW_WIDTH = 80;
|
|
143
167
|
|
|
@@ -313,7 +337,6 @@ function renderPlainTextPreview(text: string, uiTheme: Theme, filePath?: string)
|
|
|
313
337
|
}
|
|
314
338
|
return preview.trimEnd();
|
|
315
339
|
}
|
|
316
|
-
|
|
317
340
|
function formatStreamingDiff(
|
|
318
341
|
diff: string,
|
|
319
342
|
rawPath: string,
|
|
@@ -321,26 +344,30 @@ function formatStreamingDiff(
|
|
|
321
344
|
expanded: boolean,
|
|
322
345
|
label = "streaming",
|
|
323
346
|
spinnerFrame?: number,
|
|
347
|
+
cache?: RenderedStringCache,
|
|
324
348
|
): string {
|
|
325
349
|
if (!diff) return "";
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
350
|
+
let text = cachedRenderedString(cache, uiTheme, expanded, rawPath, diff, () => {
|
|
351
|
+
// Collapsed uses a "Cursor" tail window: pin the last
|
|
352
|
+
// EDIT_STREAMING_PREVIEW_LINES rows to the bottom so freshly streamed changes
|
|
353
|
+
// stay on screen. The whole-file diff is recomputed on every streamed chunk
|
|
354
|
+
// and its Myers alignment is not monotonic in payload length, so a hunk-aware
|
|
355
|
+
// window stutters as rows move between hunks. Expanded deliberately lifts that
|
|
356
|
+
// cap for the approval-time full view.
|
|
357
|
+
const allLines = diff.replace(/\n+$/u, "").split("\n");
|
|
358
|
+
const hiddenLines = expanded ? 0 : Math.max(0, allLines.length - EDIT_STREAMING_PREVIEW_LINES);
|
|
359
|
+
const visible = hiddenLines > 0 ? allLines.slice(hiddenLines) : allLines;
|
|
360
|
+
let rendered = "\n\n";
|
|
361
|
+
if (hiddenLines > 0) {
|
|
362
|
+
const hiddenHunks = getDiffStats(allLines.slice(0, hiddenLines).join("\n")).hunks;
|
|
363
|
+
const remainder: string[] = [];
|
|
364
|
+
if (hiddenHunks > 0) remainder.push(`${hiddenHunks} more hunks`);
|
|
365
|
+
remainder.push(`${hiddenLines} more lines`);
|
|
366
|
+
rendered += `${uiTheme.fg("dim", `… (${remainder.join(", ")} above)`)}\n`;
|
|
367
|
+
}
|
|
368
|
+
rendered += renderDiffColored(visible.join("\n"), { filePath: rawPath });
|
|
369
|
+
return rendered;
|
|
370
|
+
});
|
|
344
371
|
// The animated glyph rides this trailing line — inside the transcript's
|
|
345
372
|
// volatile-tail holdback — never the block header: an animating head row
|
|
346
373
|
// pins the native-scrollback commit boundary at the top of the block, so a
|
|
@@ -360,9 +387,11 @@ function formatMultiFileStreamingDiff(
|
|
|
360
387
|
uiTheme: Theme,
|
|
361
388
|
expanded: boolean,
|
|
362
389
|
spinnerFrame?: number,
|
|
390
|
+
caches?: RenderedStringCache[],
|
|
363
391
|
): string {
|
|
364
392
|
const parts: string[] = [];
|
|
365
|
-
for (
|
|
393
|
+
for (let index = 0; index < previews.length; index++) {
|
|
394
|
+
const preview = previews[index]!;
|
|
366
395
|
if (!preview.diff && !preview.error) continue;
|
|
367
396
|
const header = uiTheme.fg("dim", `\n\n── ${shortenPath(preview.path)} ──`);
|
|
368
397
|
if (preview.error) {
|
|
@@ -373,9 +402,10 @@ function formatMultiFileStreamingDiff(
|
|
|
373
402
|
// Only the last file's preview carries the animated streaming glyph;
|
|
374
403
|
// earlier files have settled and must stay byte-stable so their rows
|
|
375
404
|
// can commit to native scrollback mid-stream.
|
|
376
|
-
const isLast =
|
|
405
|
+
const isLast = index === previews.length - 1;
|
|
406
|
+
const cache = previewCacheAt(caches, index);
|
|
377
407
|
parts.push(
|
|
378
|
-
`${header}${formatStreamingDiff(preview.diff, preview.path, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined)}`,
|
|
408
|
+
`${header}${formatStreamingDiff(preview.diff, preview.path, uiTheme, expanded, "preview", isLast ? spinnerFrame : undefined, cache)}`,
|
|
379
409
|
);
|
|
380
410
|
}
|
|
381
411
|
}
|
|
@@ -389,16 +419,18 @@ function getCallPreview(
|
|
|
389
419
|
renderContext: EditRenderContext | undefined,
|
|
390
420
|
expanded: boolean,
|
|
391
421
|
spinnerFrame?: number,
|
|
422
|
+
caches?: RenderedStringCache[],
|
|
392
423
|
): string {
|
|
393
424
|
const multi = renderContext?.perFileDiffPreview;
|
|
394
425
|
if (multi && multi.length > 1 && multi.some(p => p.diff || p.error)) {
|
|
395
|
-
return formatMultiFileStreamingDiff(multi, uiTheme, expanded, spinnerFrame);
|
|
426
|
+
return formatMultiFileStreamingDiff(multi, uiTheme, expanded, spinnerFrame, caches);
|
|
396
427
|
}
|
|
428
|
+
const cache = previewCacheAt(caches, 0);
|
|
397
429
|
if (args.previewDiff) {
|
|
398
|
-
return formatStreamingDiff(args.previewDiff, rawPath, uiTheme, expanded, "preview", spinnerFrame);
|
|
430
|
+
return formatStreamingDiff(args.previewDiff, rawPath, uiTheme, expanded, "preview", spinnerFrame, cache);
|
|
399
431
|
}
|
|
400
432
|
if (args.diff && args.op) {
|
|
401
|
-
return formatStreamingDiff(args.diff, rawPath, uiTheme, expanded, "streaming", spinnerFrame);
|
|
433
|
+
return formatStreamingDiff(args.diff, rawPath, uiTheme, expanded, "streaming", spinnerFrame, cache);
|
|
402
434
|
}
|
|
403
435
|
if (args.diff) {
|
|
404
436
|
return renderPlainTextPreview(args.diff, uiTheme, rawPath);
|
|
@@ -492,30 +524,32 @@ function formatDiffStatsSuffix(diff: string, uiTheme: Theme): string {
|
|
|
492
524
|
].filter(value => value !== undefined);
|
|
493
525
|
return ` ${uiTheme.fg("dim", uiTheme.format.bracketLeft)}${stats.join(uiTheme.fg("dim", "/"))}${uiTheme.fg("dim", uiTheme.format.bracketRight)}`;
|
|
494
526
|
}
|
|
495
|
-
|
|
496
527
|
function renderDiffSection(
|
|
497
528
|
diff: string,
|
|
498
529
|
rawPath: string,
|
|
499
530
|
expanded: boolean,
|
|
500
531
|
uiTheme: Theme,
|
|
501
532
|
renderDiffFn: (t: string, o?: { filePath?: string }) => string,
|
|
533
|
+
cache?: RenderedStringCache,
|
|
502
534
|
): string {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
535
|
+
return cachedRenderedString(cache, uiTheme, expanded, rawPath, diff, () => {
|
|
536
|
+
const {
|
|
537
|
+
text: truncatedDiff,
|
|
538
|
+
hiddenHunks,
|
|
539
|
+
hiddenLines,
|
|
540
|
+
} = expanded
|
|
541
|
+
? { text: diff, hiddenHunks: 0, hiddenLines: 0 }
|
|
542
|
+
: truncateDiffByHunk(diff, PREVIEW_LIMITS.DIFF_COLLAPSED_HUNKS, PREVIEW_LIMITS.DIFF_COLLAPSED_LINES);
|
|
543
|
+
|
|
544
|
+
let text = `\n${renderDiffFn(truncatedDiff, { filePath: rawPath })}`;
|
|
545
|
+
if (!expanded && (hiddenHunks > 0 || hiddenLines > 0)) {
|
|
546
|
+
const remainder: string[] = [];
|
|
547
|
+
if (hiddenHunks > 0) remainder.push(`${hiddenHunks} more hunks`);
|
|
548
|
+
if (hiddenLines > 0) remainder.push(`${hiddenLines} more lines`);
|
|
549
|
+
text += uiTheme.fg("toolOutput", `\n… (${remainder.join(", ")}) ${formatExpandHint(uiTheme)}`);
|
|
550
|
+
}
|
|
551
|
+
return text;
|
|
552
|
+
});
|
|
519
553
|
}
|
|
520
554
|
|
|
521
555
|
function wrapEditRendererLine(line: string, width: number): string[] {
|
|
@@ -574,6 +608,7 @@ export const editToolRenderer = {
|
|
|
574
608
|
if (Array.isArray(editArgs.edits)) {
|
|
575
609
|
fileCount = countEditFiles(editArgs.edits);
|
|
576
610
|
}
|
|
611
|
+
const callPreviewCaches: RenderedStringCache[] = [];
|
|
577
612
|
return framedBlock(uiTheme, width => {
|
|
578
613
|
// Static pending icon, never the animated glyph: the header is the
|
|
579
614
|
// head row of the framed block, and native-scrollback commits are
|
|
@@ -588,7 +623,15 @@ export const editToolRenderer = {
|
|
|
588
623
|
rename,
|
|
589
624
|
extraSuffix: fileCount > 1 ? uiTheme.fg("dim", ` (+${fileCount - 1} more)`) : undefined,
|
|
590
625
|
});
|
|
591
|
-
let body = getCallPreview(
|
|
626
|
+
let body = getCallPreview(
|
|
627
|
+
editArgs,
|
|
628
|
+
rawPath,
|
|
629
|
+
uiTheme,
|
|
630
|
+
renderContext,
|
|
631
|
+
options.expanded,
|
|
632
|
+
options?.spinnerFrame,
|
|
633
|
+
callPreviewCaches,
|
|
634
|
+
);
|
|
592
635
|
if (applyPatchSummary?.error) {
|
|
593
636
|
body += `\n${uiTheme.fg("error", truncateToWidth(replaceTabs(applyPatchSummary.error, rawPath), Math.max(1, width - 2)))}`;
|
|
594
637
|
}
|
|
@@ -652,11 +695,18 @@ function renderSingleFileResult(
|
|
|
652
695
|
(result.content?.find(c => c.type === "text")?.text ?? "")
|
|
653
696
|
: "";
|
|
654
697
|
|
|
698
|
+
let diffSectionRenderDiffFn: ((t: string, o?: { filePath?: string }) => string) | undefined;
|
|
699
|
+
const diffSectionCache = createRenderedStringCache();
|
|
700
|
+
|
|
655
701
|
return framedBlock(uiTheme, width => {
|
|
656
702
|
const { expanded, renderContext } = options;
|
|
657
703
|
const editDiffPreview = renderContext?.editDiffPreview;
|
|
658
|
-
const renderDiffFn = renderContext?.renderDiff ??
|
|
704
|
+
const renderDiffFn = renderContext?.renderDiff ?? plainDiffRender;
|
|
659
705
|
|
|
706
|
+
if (diffSectionRenderDiffFn !== renderDiffFn) {
|
|
707
|
+
diffSectionRenderDiffFn = renderDiffFn;
|
|
708
|
+
invalidateRenderedStringCache(diffSectionCache);
|
|
709
|
+
}
|
|
660
710
|
const firstChangedLine =
|
|
661
711
|
(editDiffPreview && "firstChangedLine" in editDiffPreview ? editDiffPreview.firstChangedLine : undefined) ||
|
|
662
712
|
(details && !isError ? details.firstChangedLine : undefined);
|
|
@@ -681,11 +731,11 @@ function renderSingleFileResult(
|
|
|
681
731
|
if (isError) {
|
|
682
732
|
if (errorText) body = uiTheme.fg("error", replaceTabs(errorText, rawPath));
|
|
683
733
|
} else if (details?.diff) {
|
|
684
|
-
body = renderDiffSection(details.diff, rawPath, expanded, uiTheme, renderDiffFn);
|
|
734
|
+
body = renderDiffSection(details.diff, rawPath, expanded, uiTheme, renderDiffFn, diffSectionCache);
|
|
685
735
|
} else if (editDiffPreview) {
|
|
686
736
|
if ("error" in editDiffPreview) body = uiTheme.fg("error", replaceTabs(editDiffPreview.error, rawPath));
|
|
687
737
|
else if (editDiffPreview.diff)
|
|
688
|
-
body = renderDiffSection(editDiffPreview.diff, rawPath, expanded, uiTheme, renderDiffFn);
|
|
738
|
+
body = renderDiffSection(editDiffPreview.diff, rawPath, expanded, uiTheme, renderDiffFn, diffSectionCache);
|
|
689
739
|
}
|
|
690
740
|
if (details?.diagnostics) {
|
|
691
741
|
body += formatDiagnostics(details.diagnostics, expanded, uiTheme, (fp: string) =>
|
|
@@ -99,6 +99,7 @@ function singleResult(options: ExecutorOptions, overrides: Partial<SingleResult>
|
|
|
99
99
|
truncated: false,
|
|
100
100
|
durationMs: 1,
|
|
101
101
|
tokens: 0,
|
|
102
|
+
requests: 0,
|
|
102
103
|
...overrides,
|
|
103
104
|
};
|
|
104
105
|
}
|
|
@@ -178,7 +179,7 @@ describe("runEvalAgent", () => {
|
|
|
178
179
|
expect(runSpy).not.toHaveBeenCalled();
|
|
179
180
|
});
|
|
180
181
|
|
|
181
|
-
it("passes
|
|
182
|
+
it("passes parent execution options and only sets outputSchema when schema is supplied", async () => {
|
|
182
183
|
mockAgents();
|
|
183
184
|
const runSpy = vi.spyOn(taskExecutor, "runSubprocess").mockImplementation(async options => singleResult(options));
|
|
184
185
|
const abortController = new AbortController();
|
|
@@ -186,7 +187,7 @@ describe("runEvalAgent", () => {
|
|
|
186
187
|
const session = makeSession({ depth: 2, activeModel: "p/current", modelString: "p/fallback" });
|
|
187
188
|
|
|
188
189
|
await runEvalAgent(
|
|
189
|
-
{ prompt: " hello ",
|
|
190
|
+
{ prompt: " hello ", label: "My Agent", model: "p/override", schema },
|
|
190
191
|
{ session, signal: abortController.signal },
|
|
191
192
|
);
|
|
192
193
|
await runEvalAgent({ prompt: "plain" }, { session });
|
|
@@ -199,7 +200,6 @@ describe("runEvalAgent", () => {
|
|
|
199
200
|
expect(firstOptions.parentActiveModelPattern).toBe("p/current");
|
|
200
201
|
expect(firstOptions.outputSchema).toBe(schema);
|
|
201
202
|
expect(firstOptions.assignment).toBe("hello");
|
|
202
|
-
expect(firstOptions.context).toBe("context");
|
|
203
203
|
expect(firstOptions.description).toBe("My Agent");
|
|
204
204
|
expect(firstOptions.modelOverride).toEqual(["p/override"]);
|
|
205
205
|
expect(secondOptions.outputSchema).toBeUndefined();
|
|
@@ -542,6 +542,7 @@ describe("agent() through eval runtimes", () => {
|
|
|
542
542
|
recentOutput: [],
|
|
543
543
|
toolCount: 0,
|
|
544
544
|
tokens: 0,
|
|
545
|
+
requests: 0,
|
|
545
546
|
cost: 0,
|
|
546
547
|
durationMs: 0,
|
|
547
548
|
...overrides,
|
|
@@ -674,6 +675,7 @@ describe("agent() through eval runtimes", () => {
|
|
|
674
675
|
recentOutput: [],
|
|
675
676
|
toolCount: i,
|
|
676
677
|
tokens: 0,
|
|
678
|
+
requests: 0,
|
|
677
679
|
cost: 0,
|
|
678
680
|
durationMs: i * 10,
|
|
679
681
|
});
|
package/src/eval/agent-bridge.ts
CHANGED
|
@@ -34,7 +34,6 @@ const agentArgsSchema = z.object({
|
|
|
34
34
|
prompt: z.string().min(1, "prompt must be a non-empty string"),
|
|
35
35
|
agentType: z.string().min(1).optional(),
|
|
36
36
|
model: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).optional(),
|
|
37
|
-
context: z.string().optional(),
|
|
38
37
|
label: z.string().optional(),
|
|
39
38
|
schema: z.unknown().optional(),
|
|
40
39
|
});
|
|
@@ -43,7 +42,6 @@ interface EvalAgentArgs {
|
|
|
43
42
|
prompt: string;
|
|
44
43
|
agentType?: string;
|
|
45
44
|
model?: string | string[];
|
|
46
|
-
context?: string;
|
|
47
45
|
label?: string;
|
|
48
46
|
schema?: unknown;
|
|
49
47
|
}
|
|
@@ -111,7 +109,7 @@ function assertNotPlanMode(session: ToolSession): void {
|
|
|
111
109
|
}
|
|
112
110
|
|
|
113
111
|
function renderSubagentPrompt(assignment: string): string {
|
|
114
|
-
return prompt.render(subagentUserPromptTemplate, { assignment: assignment.trim()
|
|
112
|
+
return prompt.render(subagentUserPromptTemplate, { assignment: assignment.trim() });
|
|
115
113
|
}
|
|
116
114
|
|
|
117
115
|
function trimToUndefined(value: string | undefined): string | undefined {
|
|
@@ -135,20 +133,12 @@ function getOutputManager(session: ToolSession): AgentOutputManager {
|
|
|
135
133
|
async function getArtifacts(session: ToolSession): Promise<{
|
|
136
134
|
sessionFile: string | null;
|
|
137
135
|
artifactsDir: string;
|
|
138
|
-
contextFile?: string;
|
|
139
136
|
}> {
|
|
140
137
|
const sessionFile = session.getSessionFile();
|
|
141
138
|
const sessionArtifactsDir = sessionFile ? sessionFile.slice(0, -6) : null;
|
|
142
139
|
const artifactsDir = sessionArtifactsDir ?? path.join(os.tmpdir(), `omp-eval-agent-${Snowflake.next()}`);
|
|
143
140
|
await fs.mkdir(artifactsDir, { recursive: true });
|
|
144
|
-
|
|
145
|
-
const shouldWriteConversationContext = session.settings.get("irc.enabled") !== true;
|
|
146
|
-
const compactContext = shouldWriteConversationContext ? session.getCompactContext?.() : undefined;
|
|
147
|
-
if (!compactContext) return { sessionFile, artifactsDir };
|
|
148
|
-
|
|
149
|
-
const contextFile = path.join(artifactsDir, "context.md");
|
|
150
|
-
await Bun.write(contextFile, compactContext);
|
|
151
|
-
return { sessionFile, artifactsDir, contextFile };
|
|
141
|
+
return { sessionFile, artifactsDir };
|
|
152
142
|
}
|
|
153
143
|
|
|
154
144
|
function emitProgressStatus(emitStatus: ((event: JsStatusEvent) => void) | undefined, progress: AgentProgress): void {
|
|
@@ -246,11 +236,10 @@ export async function runEvalAgent(args: unknown, options: EvalAgentBridgeOption
|
|
|
246
236
|
};
|
|
247
237
|
const parentArtifactManager = options.session.getArtifactManager?.() ?? undefined;
|
|
248
238
|
const mcpManager = options.session.mcpManager ?? MCPManager.instance();
|
|
249
|
-
const { sessionFile, artifactsDir
|
|
239
|
+
const { sessionFile, artifactsDir } = await getArtifacts(options.session);
|
|
250
240
|
const outputManager = getOutputManager(options.session);
|
|
251
241
|
const id = await outputManager.allocate(outputIdBase(parsed.label, agentName));
|
|
252
242
|
const assignment = parsed.prompt.trim();
|
|
253
|
-
const context = trimToUndefined(parsed.context);
|
|
254
243
|
// Suspend eval timeout accounting while the subagent owns control. The
|
|
255
244
|
// timeout clock restarts once the bridge returns to the cell runtime.
|
|
256
245
|
const result = await withBridgeTimeoutPause(options.emitStatus, () =>
|
|
@@ -259,7 +248,6 @@ export async function runEvalAgent(args: unknown, options: EvalAgentBridgeOption
|
|
|
259
248
|
agent: effectiveAgent,
|
|
260
249
|
task: renderSubagentPrompt(assignment),
|
|
261
250
|
assignment,
|
|
262
|
-
context,
|
|
263
251
|
description: trimToUndefined(parsed.label),
|
|
264
252
|
index: 0,
|
|
265
253
|
id,
|
|
@@ -271,7 +259,6 @@ export async function runEvalAgent(args: unknown, options: EvalAgentBridgeOption
|
|
|
271
259
|
sessionFile,
|
|
272
260
|
persistArtifacts: Boolean(sessionFile),
|
|
273
261
|
artifactsDir,
|
|
274
|
-
contextFile,
|
|
275
262
|
// Eval `agent()` subagents are short-lived programmatic helpers (data
|
|
276
263
|
// collection, structured output, parallel() fan-out). LSP server
|
|
277
264
|
// cold-start costs tens of seconds and is pure overhead here, so it is
|
|
@@ -65,7 +65,7 @@ if (!globalThis.__omp_js_prelude_loaded__) {
|
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
const agent = async (prompt, opts, ...rest) => {
|
|
68
|
-
const o = optionsArg("agent", opts, rest, "{ agentType, model,
|
|
68
|
+
const o = optionsArg("agent", opts, rest, "{ agentType, model, label, schema }");
|
|
69
69
|
const res = await globalThis.__omp_call_tool__("__agent__", { prompt, ...o });
|
|
70
70
|
const text = res && typeof res === "object" ? res.text : res;
|
|
71
71
|
return hasOwn(o, "schema") ? JSON.parse(text) : text;
|
package/src/eval/py/prelude.py
CHANGED
|
@@ -519,21 +519,20 @@ if "__omp_prelude_loaded__" not in globals():
|
|
|
519
519
|
text = res.get("text") if isinstance(res, dict) else res
|
|
520
520
|
return json.loads(text) if schema is not None else text
|
|
521
521
|
|
|
522
|
-
def agent(prompt, *, agent_type="task", model=None,
|
|
522
|
+
def agent(prompt, *, agent_type="task", model=None, label=None, schema=None):
|
|
523
523
|
"""Run a subagent and return its final output.
|
|
524
524
|
|
|
525
525
|
`agent_type` selects the subagent definition (default "task"). Pass
|
|
526
|
-
`model` to override that agent's model, `
|
|
527
|
-
|
|
528
|
-
|
|
526
|
+
`model` to override that agent's model, `label` for the output artifact
|
|
527
|
+
id, and `schema` to request structured JSON output; when `schema` is
|
|
528
|
+
supplied the parsed object is returned. Share background by writing a
|
|
529
|
+
local:// file and referencing it in the prompt.
|
|
529
530
|
"""
|
|
530
531
|
args = {"prompt": prompt}
|
|
531
532
|
if agent_type is not None:
|
|
532
533
|
args["agentType"] = agent_type
|
|
533
534
|
if model is not None:
|
|
534
535
|
args["model"] = model
|
|
535
|
-
if context is not None:
|
|
536
|
-
args["context"] = context
|
|
537
536
|
if label is not None:
|
|
538
537
|
args["label"] = label
|
|
539
538
|
if schema is not None:
|