@oh-my-pi/pi-coding-agent 14.9.9 → 15.0.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 +123 -0
- package/examples/extensions/plan-mode.ts +0 -1
- package/package.json +9 -9
- package/scripts/build-binary.ts +5 -0
- package/scripts/format-prompts.ts +1 -1
- package/src/autoresearch/helpers.ts +17 -0
- package/src/autoresearch/tools/log-experiment.ts +9 -17
- package/src/autoresearch/tools/run-experiment.ts +2 -17
- package/src/capability/skill.ts +7 -0
- package/src/cli/args.ts +2 -2
- package/src/cli/list-models.ts +1 -1
- package/src/cli/shell-cli.ts +3 -13
- package/src/cli/update-cli.ts +1 -1
- package/src/cli.ts +11 -29
- package/src/commands/acp.ts +24 -0
- package/src/commands/launch.ts +6 -4
- package/src/commit/agentic/prompts/system.md +1 -1
- package/src/commit/agentic/tools/propose-changelog.ts +8 -1
- package/src/commit/analysis/conventional.ts +8 -66
- package/src/commit/map-reduce/reduce-phase.ts +6 -65
- package/src/commit/pipeline.ts +2 -2
- package/src/commit/shared-llm.ts +89 -0
- package/src/config/config-file.ts +210 -0
- package/src/config/model-equivalence.ts +8 -11
- package/src/config/model-registry.ts +13 -2
- package/src/config/model-resolver.ts +31 -4
- package/src/config/settings-schema.ts +102 -1
- package/src/config/settings.ts +1 -1
- package/src/config.ts +3 -219
- package/src/edit/index.ts +22 -1
- package/src/edit/modes/patch.ts +10 -0
- package/src/edit/modes/replace.ts +3 -0
- package/src/edit/renderer.ts +17 -1
- package/src/eval/js/context-manager.ts +1 -1
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/shared/rewrite-imports.ts +122 -50
- package/src/eval/js/shared/runtime.ts +31 -4
- package/src/eval/js/tool-bridge.ts +43 -21
- package/src/eval/py/executor.ts +5 -0
- package/src/exa/factory.ts +2 -2
- package/src/exa/mcp-client.ts +74 -1
- package/src/exec/bash-executor.ts +5 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -11
- package/src/extensibility/extensions/runner.ts +55 -2
- package/src/extensibility/extensions/types.ts +98 -221
- package/src/extensibility/hooks/types.ts +89 -314
- package/src/extensibility/shared-events.ts +343 -0
- package/src/extensibility/skills.ts +42 -1
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +500 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +237 -0
- package/src/hashline/anchors.ts +2 -2
- package/src/hindsight/mental-models.ts +1 -1
- package/src/internal-urls/agent-protocol.ts +1 -20
- package/src/internal-urls/artifact-protocol.ts +1 -19
- package/src/internal-urls/docs-index.generated.ts +9 -10
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/issue-pr-protocol.ts +577 -0
- package/src/internal-urls/registry-helpers.ts +25 -0
- package/src/internal-urls/router.ts +6 -3
- package/src/internal-urls/types.ts +22 -1
- package/src/main.ts +24 -11
- package/src/mcp/oauth-flow.ts +20 -0
- package/src/modes/acp/acp-agent.ts +412 -71
- package/src/modes/acp/acp-client-bridge.ts +152 -0
- package/src/modes/acp/acp-event-mapper.ts +180 -15
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/assistant-message.ts +14 -8
- package/src/modes/components/bash-execution.ts +24 -63
- package/src/modes/components/custom-message.ts +14 -40
- package/src/modes/components/eval-execution.ts +27 -57
- package/src/modes/components/execution-shared.ts +102 -0
- package/src/modes/components/hook-message.ts +17 -49
- package/src/modes/components/mcp-add-wizard.ts +26 -5
- package/src/modes/components/message-frame.ts +88 -0
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/read-tool-group.ts +29 -1
- package/src/modes/components/session-observer-overlay.ts +6 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +55 -4
- package/src/modes/components/status-line/types.ts +4 -0
- package/src/modes/components/status-line.ts +28 -10
- package/src/modes/components/tool-execution.ts +7 -8
- package/src/modes/controllers/command-controller-shared.ts +108 -0
- package/src/modes/controllers/command-controller.ts +27 -10
- package/src/modes/controllers/event-controller.ts +60 -18
- package/src/modes/controllers/extension-ui-controller.ts +8 -2
- package/src/modes/controllers/input-controller.ts +85 -39
- package/src/modes/controllers/mcp-command-controller.ts +56 -61
- package/src/modes/controllers/ssh-command-controller.ts +18 -57
- package/src/modes/interactive-mode.ts +675 -39
- package/src/modes/print-mode.ts +16 -86
- package/src/modes/rpc/rpc-mode.ts +30 -88
- package/src/modes/runtime-init.ts +115 -0
- package/src/modes/theme/defaults/dark-poimandres.json +2 -0
- package/src/modes/theme/defaults/light-poimandres.json +2 -0
- package/src/modes/theme/theme.ts +18 -6
- package/src/modes/types.ts +20 -5
- package/src/modes/utils/context-usage.ts +13 -13
- package/src/modes/utils/ui-helpers.ts +25 -6
- package/src/plan-mode/approved-plan.ts +35 -1
- package/src/prompts/agents/designer.md +5 -5
- package/src/prompts/agents/explore.md +7 -7
- package/src/prompts/agents/init.md +9 -9
- package/src/prompts/agents/librarian.md +14 -14
- package/src/prompts/agents/plan.md +4 -4
- package/src/prompts/agents/reviewer.md +5 -5
- package/src/prompts/agents/task.md +10 -10
- package/src/prompts/commands/orchestrate.md +2 -2
- package/src/prompts/compaction/branch-summary.md +3 -3
- package/src/prompts/compaction/compaction-short-summary.md +7 -7
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +5 -5
- package/src/prompts/compaction/compaction-turn-prefix.md +3 -3
- package/src/prompts/compaction/compaction-update-summary.md +11 -11
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/memories/consolidation.md +2 -2
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +5 -5
- package/src/prompts/review-request.md +4 -4
- package/src/prompts/system/agent-creation-architect.md +17 -17
- package/src/prompts/system/agent-creation-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +2 -2
- package/src/prompts/system/custom-system-prompt.md +2 -2
- package/src/prompts/system/eager-todo.md +6 -6
- package/src/prompts/system/handoff-document.md +1 -1
- package/src/prompts/system/plan-mode-active.md +25 -24
- package/src/prompts/system/plan-mode-approved.md +4 -4
- package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
- package/src/prompts/system/plan-mode-reference.md +2 -2
- package/src/prompts/system/plan-mode-subagent.md +8 -8
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +3 -3
- package/src/prompts/system/project-prompt.md +4 -4
- package/src/prompts/system/subagent-system-prompt.md +7 -7
- package/src/prompts/system/subagent-yield-reminder.md +4 -4
- package/src/prompts/system/system-prompt.md +72 -71
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/tools/apply-patch.md +1 -1
- package/src/prompts/tools/ast-edit.md +3 -3
- package/src/prompts/tools/ast-grep.md +3 -3
- package/src/prompts/tools/bash.md +6 -0
- package/src/prompts/tools/browser.md +3 -3
- package/src/prompts/tools/checkpoint.md +3 -3
- package/src/prompts/tools/find.md +3 -3
- package/src/prompts/tools/github.md +2 -5
- package/src/prompts/tools/goal.md +13 -0
- package/src/prompts/tools/hashline.md +104 -116
- package/src/prompts/tools/image-gen.md +3 -3
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +6 -6
- package/src/prompts/tools/read.md +8 -7
- package/src/prompts/tools/replace.md +5 -5
- package/src/prompts/tools/resolve.md +6 -5
- package/src/prompts/tools/retain.md +1 -1
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search.md +2 -2
- package/src/prompts/tools/ssh.md +2 -2
- package/src/prompts/tools/task.md +12 -6
- package/src/prompts/tools/web-search.md +2 -2
- package/src/prompts/tools/write.md +3 -3
- package/src/sdk.ts +81 -17
- package/src/session/agent-session.ts +656 -125
- package/src/session/blob-store.ts +36 -3
- package/src/session/client-bridge.ts +81 -0
- package/src/session/compaction/errors.ts +31 -0
- package/src/session/compaction/index.ts +1 -0
- package/src/session/messages.ts +67 -2
- package/src/session/session-manager.ts +131 -12
- package/src/session/session-storage.ts +33 -15
- package/src/session/streaming-output.ts +309 -13
- package/src/slash-commands/acp-builtins.ts +46 -0
- package/src/slash-commands/builtin-registry.ts +717 -116
- package/src/slash-commands/helpers/context-report.ts +39 -0
- package/src/slash-commands/helpers/format.ts +23 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +532 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/ssh.ts +193 -0
- package/src/slash-commands/helpers/todo.ts +279 -0
- package/src/slash-commands/helpers/usage-report.ts +91 -0
- package/src/slash-commands/types.ts +126 -0
- package/src/ssh/ssh-executor.ts +5 -0
- package/src/system-prompt.ts +4 -2
- package/src/task/executor.ts +27 -10
- package/src/task/index.ts +20 -1
- package/src/task/render.ts +27 -18
- package/src/task/types.ts +4 -0
- package/src/tools/ast-edit.ts +21 -120
- package/src/tools/ast-grep.ts +21 -119
- package/src/tools/bash-interactive.ts +9 -1
- package/src/tools/bash.ts +203 -6
- package/src/tools/browser/attach.ts +3 -3
- package/src/tools/browser/launch.ts +81 -18
- package/src/tools/browser/registry.ts +1 -5
- package/src/tools/browser/tab-supervisor.ts +51 -14
- package/src/tools/conflict-detect.ts +21 -10
- package/src/tools/eval.ts +3 -1
- package/src/tools/fetch.ts +15 -4
- package/src/tools/find.ts +39 -39
- package/src/tools/gh-renderer.ts +0 -12
- package/src/tools/gh.ts +689 -182
- package/src/tools/github-cache.ts +548 -0
- package/src/tools/index.ts +25 -11
- package/src/tools/inspect-image.ts +3 -10
- package/src/tools/output-meta.ts +176 -37
- package/src/tools/path-utils.ts +125 -2
- package/src/tools/read.ts +605 -239
- package/src/tools/render-utils.ts +92 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +72 -44
- package/src/tools/search.ts +120 -186
- package/src/tools/write.ts +67 -10
- package/src/tui/code-cell.ts +70 -2
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/image-loading.ts +7 -3
- package/src/utils/image-resize.ts +32 -43
- package/src/vim/parser.ts +0 -17
- package/src/vim/render.ts +1 -1
- package/src/vim/types.ts +1 -1
- package/src/web/search/providers/gemini.ts +35 -95
- package/src/prompts/tools/exit-plan-mode.md +0 -6
- package/src/tools/exit-plan-mode.ts +0 -97
- package/src/utils/fuzzy.ts +0 -108
- package/src/utils/image-convert.ts +0 -27
|
@@ -460,6 +460,46 @@ export const SETTINGS_SCHEMA = {
|
|
|
460
460
|
],
|
|
461
461
|
},
|
|
462
462
|
},
|
|
463
|
+
"tools.artifactHeadBytes": {
|
|
464
|
+
type: "number",
|
|
465
|
+
default: 20,
|
|
466
|
+
ui: {
|
|
467
|
+
tab: "tools",
|
|
468
|
+
label: "Artifact head size (KB)",
|
|
469
|
+
description:
|
|
470
|
+
"Amount of head content kept inline alongside the tail when output spills to artifact (middle elision). 0 disables — keep tail only.",
|
|
471
|
+
options: [
|
|
472
|
+
{ value: "0", label: "0 KB", description: "Disabled; tail-only truncation" },
|
|
473
|
+
{ value: "1", label: "1 KB", description: "~250 tokens" },
|
|
474
|
+
{ value: "2.5", label: "2.5 KB", description: "~625 tokens" },
|
|
475
|
+
{ value: "5", label: "5 KB", description: "~1.25K tokens" },
|
|
476
|
+
{ value: "10", label: "10 KB", description: "~2.5K tokens" },
|
|
477
|
+
{ value: "20", label: "20 KB", description: "Default; ~5K tokens" },
|
|
478
|
+
{ value: "50", label: "50 KB", description: "~12.5K tokens" },
|
|
479
|
+
{ value: "100", label: "100 KB", description: "~25K tokens" },
|
|
480
|
+
{ value: "200", label: "200 KB", description: "~50K tokens" },
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
"tools.outputMaxColumns": {
|
|
485
|
+
type: "number",
|
|
486
|
+
default: 768,
|
|
487
|
+
ui: {
|
|
488
|
+
tab: "tools",
|
|
489
|
+
label: "Output column cap",
|
|
490
|
+
description:
|
|
491
|
+
"Per-line byte cap for streaming tool outputs (bash, ssh, python, js eval) and `read`. Lines wider than this are ellipsis-truncated; remaining bytes up to the next newline are dropped. 0 disables.",
|
|
492
|
+
options: [
|
|
493
|
+
{ value: "0", label: "Off", description: "No per-line cap" },
|
|
494
|
+
{ value: "256", label: "256", description: "Tight" },
|
|
495
|
+
{ value: "512", label: "512" },
|
|
496
|
+
{ value: "768", label: "768", description: "Default" },
|
|
497
|
+
{ value: "1024", label: "1024" },
|
|
498
|
+
{ value: "2048", label: "2048" },
|
|
499
|
+
{ value: "4096", label: "4096", description: "Loose" },
|
|
500
|
+
],
|
|
501
|
+
},
|
|
502
|
+
},
|
|
463
503
|
"tools.artifactTailLines": {
|
|
464
504
|
type: "number",
|
|
465
505
|
default: 500,
|
|
@@ -1498,7 +1538,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1498
1538
|
|
|
1499
1539
|
"read.defaultLimit": {
|
|
1500
1540
|
type: "number",
|
|
1501
|
-
default:
|
|
1541
|
+
default: 300,
|
|
1502
1542
|
ui: {
|
|
1503
1543
|
tab: "editing",
|
|
1504
1544
|
label: "Default Read Limit",
|
|
@@ -1864,6 +1904,37 @@ export const SETTINGS_SCHEMA = {
|
|
|
1864
1904
|
},
|
|
1865
1905
|
},
|
|
1866
1906
|
|
|
1907
|
+
"github.cache.enabled": {
|
|
1908
|
+
type: "boolean",
|
|
1909
|
+
default: true,
|
|
1910
|
+
ui: {
|
|
1911
|
+
tab: "tools",
|
|
1912
|
+
label: "GitHub view cache",
|
|
1913
|
+
description: "Cache rendered issue/PR view output in ~/.omp/cache/github-cache.db so repeated reads are free",
|
|
1914
|
+
},
|
|
1915
|
+
},
|
|
1916
|
+
|
|
1917
|
+
"github.cache.softTtlSec": {
|
|
1918
|
+
type: "number",
|
|
1919
|
+
default: 300,
|
|
1920
|
+
ui: {
|
|
1921
|
+
tab: "tools",
|
|
1922
|
+
label: "GitHub cache soft TTL (seconds)",
|
|
1923
|
+
description: "Within this window, cached issue/PR view rows are returned directly. Default 5 minutes.",
|
|
1924
|
+
},
|
|
1925
|
+
},
|
|
1926
|
+
|
|
1927
|
+
"github.cache.hardTtlSec": {
|
|
1928
|
+
type: "number",
|
|
1929
|
+
default: 604800,
|
|
1930
|
+
ui: {
|
|
1931
|
+
tab: "tools",
|
|
1932
|
+
label: "GitHub cache hard TTL (seconds)",
|
|
1933
|
+
description:
|
|
1934
|
+
"Past soft TTL but within hard TTL, the tool returns the cached row and refreshes it in the background. Past hard TTL, the row is dropped. Default 7 days.",
|
|
1935
|
+
},
|
|
1936
|
+
},
|
|
1937
|
+
|
|
1867
1938
|
"web_search.enabled": {
|
|
1868
1939
|
type: "boolean",
|
|
1869
1940
|
default: true,
|
|
@@ -2064,6 +2135,36 @@ export const SETTINGS_SCHEMA = {
|
|
|
2064
2135
|
},
|
|
2065
2136
|
},
|
|
2066
2137
|
|
|
2138
|
+
"goal.enabled": {
|
|
2139
|
+
type: "boolean",
|
|
2140
|
+
default: true,
|
|
2141
|
+
ui: {
|
|
2142
|
+
tab: "tasks",
|
|
2143
|
+
label: "Goal Mode",
|
|
2144
|
+
description: "Enable per-session goal mode and the hidden goal tool",
|
|
2145
|
+
},
|
|
2146
|
+
},
|
|
2147
|
+
|
|
2148
|
+
"goal.statusInFooter": {
|
|
2149
|
+
type: "boolean",
|
|
2150
|
+
default: true,
|
|
2151
|
+
ui: {
|
|
2152
|
+
tab: "tasks",
|
|
2153
|
+
label: "Goal Status In Footer",
|
|
2154
|
+
description: "Show token budget alongside the goal indicator in the status line",
|
|
2155
|
+
},
|
|
2156
|
+
},
|
|
2157
|
+
|
|
2158
|
+
"goal.continuationModes": {
|
|
2159
|
+
type: "array",
|
|
2160
|
+
default: ["interactive"],
|
|
2161
|
+
ui: {
|
|
2162
|
+
tab: "tasks",
|
|
2163
|
+
label: "Goal Continuation Modes",
|
|
2164
|
+
description: "Run modes where active goals may auto-continue between turns",
|
|
2165
|
+
},
|
|
2166
|
+
},
|
|
2167
|
+
|
|
2067
2168
|
// Delegation
|
|
2068
2169
|
"task.isolation.mode": {
|
|
2069
2170
|
type: "enum",
|
package/src/config/settings.ts
CHANGED
|
@@ -850,7 +850,7 @@ export function isSettingsInitialized(): boolean {
|
|
|
850
850
|
* Reset the global singleton for testing.
|
|
851
851
|
* @internal
|
|
852
852
|
*/
|
|
853
|
-
export function
|
|
853
|
+
export function resetSettingsForTest(): void {
|
|
854
854
|
globalInstance = null;
|
|
855
855
|
globalInstancePromise = null;
|
|
856
856
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
CONFIG_DIR_NAME,
|
|
6
|
-
getAgentDir,
|
|
7
|
-
getConfigAgentDirName,
|
|
8
|
-
getProjectDir,
|
|
9
|
-
isEnoent,
|
|
10
|
-
logger,
|
|
11
|
-
} from "@oh-my-pi/pi-utils";
|
|
12
|
-
import type { TSchema } from "@sinclair/typebox";
|
|
13
|
-
import { Value } from "@sinclair/typebox/value";
|
|
14
|
-
import type { ErrorObject } from "ajv";
|
|
15
|
-
import { JSONC, YAML } from "bun";
|
|
4
|
+
import { CONFIG_DIR_NAME, getConfigAgentDirName, getProjectDir } from "@oh-my-pi/pi-utils";
|
|
16
5
|
import { expandTilde } from "./tools/path-utils";
|
|
17
6
|
|
|
7
|
+
export * from "./config/config-file";
|
|
8
|
+
|
|
18
9
|
const priorityList = [
|
|
19
10
|
{ dir: CONFIG_DIR_NAME, globalAgentDir: getConfigAgentDirName },
|
|
20
11
|
{ dir: ".claude" },
|
|
@@ -53,213 +44,6 @@ export function getChangelogPath(): string {
|
|
|
53
44
|
return path.resolve(path.join(getPackageDir(), "CHANGELOG.md"));
|
|
54
45
|
}
|
|
55
46
|
|
|
56
|
-
// =============================================================================
|
|
57
|
-
// User Config Paths (~/.omp/agent/*)
|
|
58
|
-
// =============================================================================
|
|
59
|
-
|
|
60
|
-
function migrateJsonToYml(jsonPath: string, ymlPath: string) {
|
|
61
|
-
try {
|
|
62
|
-
if (fs.existsSync(ymlPath)) return;
|
|
63
|
-
if (!fs.existsSync(jsonPath)) return;
|
|
64
|
-
|
|
65
|
-
const content = fs.readFileSync(jsonPath, "utf-8");
|
|
66
|
-
const parsed = JSON.parse(content);
|
|
67
|
-
if (!parsed) {
|
|
68
|
-
logger.warn("migrateJsonToYml: invalid json structure", { path: jsonPath });
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
fs.writeFileSync(ymlPath, YAML.stringify(parsed, null, 2));
|
|
72
|
-
} catch (error) {
|
|
73
|
-
logger.warn("migrateJsonToYml: migration failed", { error: String(error) });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface IConfigFile<T> {
|
|
78
|
-
readonly id: string;
|
|
79
|
-
readonly schema: TSchema;
|
|
80
|
-
path?(): string;
|
|
81
|
-
load(): T | null;
|
|
82
|
-
invalidate?(): void;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export class ConfigError extends Error {
|
|
86
|
-
readonly #message: string;
|
|
87
|
-
constructor(
|
|
88
|
-
public readonly id: string,
|
|
89
|
-
public readonly schemaErrors: ErrorObject[] | null | undefined,
|
|
90
|
-
public readonly other?: { err: unknown; stage: string },
|
|
91
|
-
) {
|
|
92
|
-
let messages: string[] | undefined;
|
|
93
|
-
let cause: any | undefined;
|
|
94
|
-
let klass: string;
|
|
95
|
-
|
|
96
|
-
if (schemaErrors) {
|
|
97
|
-
klass = "Schema";
|
|
98
|
-
messages = schemaErrors.map(e => `${e.instancePath || "root"}: ${e.message}`);
|
|
99
|
-
} else if (other) {
|
|
100
|
-
klass = other.stage;
|
|
101
|
-
if (other.err instanceof Error) {
|
|
102
|
-
messages = [other.err.message];
|
|
103
|
-
cause = other.err;
|
|
104
|
-
} else {
|
|
105
|
-
messages = [String(other.err)];
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
klass = "Unknown";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const title = `Failed to load config file ${id}, ${klass} error:`;
|
|
112
|
-
let message: string;
|
|
113
|
-
switch (messages?.length ?? 0) {
|
|
114
|
-
case 0:
|
|
115
|
-
message = title.slice(0, -1);
|
|
116
|
-
break;
|
|
117
|
-
case 1:
|
|
118
|
-
message = `${title} ${messages![0]}`;
|
|
119
|
-
break;
|
|
120
|
-
default:
|
|
121
|
-
message = `${title}\n${messages!.map(m => ` - ${m}`).join("\n")}`;
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
super(message, { cause });
|
|
126
|
-
this.name = "LoadError";
|
|
127
|
-
this.#message = message;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
get message(): string {
|
|
131
|
-
return this.#message;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
toString(): string {
|
|
135
|
-
return this.message;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export type LoadStatus = "ok" | "error" | "not-found";
|
|
140
|
-
|
|
141
|
-
export type LoadResult<T> =
|
|
142
|
-
| { value?: null; error: ConfigError; status: "error" }
|
|
143
|
-
| { value: T; error?: undefined; status: "ok" }
|
|
144
|
-
| { value?: null; error?: unknown; status: "not-found" };
|
|
145
|
-
|
|
146
|
-
export class ConfigFile<T> implements IConfigFile<T> {
|
|
147
|
-
readonly #basePath: string;
|
|
148
|
-
#cache?: LoadResult<T>;
|
|
149
|
-
#auxValidate?: (value: T) => void;
|
|
150
|
-
|
|
151
|
-
constructor(
|
|
152
|
-
readonly id: string,
|
|
153
|
-
readonly schema: TSchema,
|
|
154
|
-
configPath: string = path.join(getAgentDir(), `${id}.yml`),
|
|
155
|
-
) {
|
|
156
|
-
this.#basePath = configPath;
|
|
157
|
-
if (configPath.endsWith(".yml")) {
|
|
158
|
-
const jsonPath = `${configPath.slice(0, -4)}.json`;
|
|
159
|
-
migrateJsonToYml(jsonPath, configPath);
|
|
160
|
-
} else if (configPath.endsWith(".yaml")) {
|
|
161
|
-
const jsonPath = `${configPath.slice(0, -5)}.json`;
|
|
162
|
-
migrateJsonToYml(jsonPath, configPath);
|
|
163
|
-
} else if (configPath.endsWith(".json") || configPath.endsWith(".jsonc")) {
|
|
164
|
-
// JSON configs are still supported without migration.
|
|
165
|
-
} else {
|
|
166
|
-
throw new Error(`Invalid config file path: ${configPath}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
relocate(path?: string): ConfigFile<T> {
|
|
171
|
-
if (!path || path === this.#basePath) return this;
|
|
172
|
-
const result = new ConfigFile<T>(this.id, this.schema, path);
|
|
173
|
-
result.#auxValidate = this.#auxValidate;
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
getMtimeMs(): number | null {
|
|
178
|
-
try {
|
|
179
|
-
return fs.statSync(this.path()).mtimeMs;
|
|
180
|
-
} catch (err) {
|
|
181
|
-
if (isEnoent(err)) return null;
|
|
182
|
-
throw err;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
withValidation(name: string, validate: (value: T) => void): this {
|
|
187
|
-
const prev = this.#auxValidate;
|
|
188
|
-
this.#auxValidate = (value: T) => {
|
|
189
|
-
prev?.(value);
|
|
190
|
-
try {
|
|
191
|
-
validate(value);
|
|
192
|
-
} catch (error) {
|
|
193
|
-
throw new ConfigError(this.id, undefined, { err: error, stage: `Validate(${name})` });
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
return this;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
createDefault() {
|
|
200
|
-
return Value.Default(this.schema, [], undefined) as T;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
#storeCache(result: LoadResult<T>): LoadResult<T> {
|
|
204
|
-
this.#cache = result;
|
|
205
|
-
return result;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
tryLoad(): LoadResult<T> {
|
|
209
|
-
if (this.#cache) return this.#cache;
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
const content = fs.readFileSync(this.path(), "utf-8").trim();
|
|
213
|
-
|
|
214
|
-
let parsed: unknown;
|
|
215
|
-
if (this.#basePath.endsWith(".json") || this.#basePath.endsWith(".jsonc")) {
|
|
216
|
-
parsed = JSONC.parse(content);
|
|
217
|
-
} else if (this.#basePath.endsWith(".yml") || this.#basePath.endsWith(".yaml")) {
|
|
218
|
-
parsed = YAML.parse(content);
|
|
219
|
-
} else {
|
|
220
|
-
throw new Error(`Invalid config file path: ${this.#basePath}`);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (!Value.Check(this.schema, parsed)) {
|
|
224
|
-
const schemaErrors: ErrorObject[] = [];
|
|
225
|
-
for (const err of Value.Errors(this.schema, parsed)) {
|
|
226
|
-
schemaErrors.push({ instancePath: err.path, message: err.message } as ErrorObject);
|
|
227
|
-
if (schemaErrors.length >= 50) break;
|
|
228
|
-
}
|
|
229
|
-
const error = new ConfigError(this.id, schemaErrors);
|
|
230
|
-
logger.warn("Failed to parse config file", { path: this.path(), error });
|
|
231
|
-
return this.#storeCache({ error, status: "error" });
|
|
232
|
-
}
|
|
233
|
-
return this.#storeCache({ value: parsed as T, status: "ok" });
|
|
234
|
-
} catch (error) {
|
|
235
|
-
if (isEnoent(error)) {
|
|
236
|
-
return this.#storeCache({ status: "not-found" });
|
|
237
|
-
}
|
|
238
|
-
logger.warn("Failed to parse config file", { path: this.path(), error });
|
|
239
|
-
return this.#storeCache({
|
|
240
|
-
error: new ConfigError(this.id, undefined, { err: error, stage: "Unexpected" }),
|
|
241
|
-
status: "error",
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
load(): T | null {
|
|
247
|
-
return this.tryLoad().value ?? null;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
loadOrDefault(): T {
|
|
251
|
-
return this.tryLoad().value ?? this.createDefault();
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
path(): string {
|
|
255
|
-
return this.#basePath;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
invalidate() {
|
|
259
|
-
this.#cache = undefined;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
47
|
// =============================================================================
|
|
264
48
|
// Multi-Config Directory Helpers
|
|
265
49
|
// =============================================================================
|
package/src/edit/index.ts
CHANGED
|
@@ -145,13 +145,15 @@ async function executeApplyPatchPerFile(
|
|
|
145
145
|
const result = await run(batchRequest);
|
|
146
146
|
const details = result.details;
|
|
147
147
|
perFileResults.push({
|
|
148
|
-
path,
|
|
148
|
+
path: details?.path ?? path,
|
|
149
149
|
diff: details?.diff ?? "",
|
|
150
150
|
firstChangedLine: details?.firstChangedLine,
|
|
151
151
|
diagnostics: details?.diagnostics,
|
|
152
152
|
op: details?.op,
|
|
153
153
|
move: details?.move,
|
|
154
154
|
meta: details?.meta,
|
|
155
|
+
oldText: details?.oldText,
|
|
156
|
+
newText: details?.newText,
|
|
155
157
|
});
|
|
156
158
|
const text = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
157
159
|
if (text) contentTexts.push(text);
|
|
@@ -205,6 +207,11 @@ async function executeSinglePathEntries(
|
|
|
205
207
|
const diffTexts: string[] = [];
|
|
206
208
|
let firstChangedLine: number | undefined;
|
|
207
209
|
let errorCount = 0;
|
|
210
|
+
let metadataPath: string | undefined;
|
|
211
|
+
let hasFirstOldText = false;
|
|
212
|
+
let firstOldText: string | undefined;
|
|
213
|
+
let hasLastNewText = false;
|
|
214
|
+
let lastNewText: string | undefined;
|
|
208
215
|
|
|
209
216
|
for (let i = 0; i < runs.length; i++) {
|
|
210
217
|
const isLast = i === runs.length - 1;
|
|
@@ -217,6 +224,17 @@ async function executeSinglePathEntries(
|
|
|
217
224
|
const details = result.details;
|
|
218
225
|
if (details?.diff) diffTexts.push(details.diff);
|
|
219
226
|
firstChangedLine ??= details?.firstChangedLine;
|
|
227
|
+
if (details?.path) {
|
|
228
|
+
metadataPath ??= details.path;
|
|
229
|
+
}
|
|
230
|
+
if (details && "oldText" in details && !hasFirstOldText) {
|
|
231
|
+
firstOldText = details.oldText;
|
|
232
|
+
hasFirstOldText = true;
|
|
233
|
+
}
|
|
234
|
+
if (details && "newText" in details) {
|
|
235
|
+
lastNewText = details.newText;
|
|
236
|
+
hasLastNewText = true;
|
|
237
|
+
}
|
|
220
238
|
const text = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
221
239
|
if (text) contentTexts.push(text);
|
|
222
240
|
} catch (err) {
|
|
@@ -242,6 +260,9 @@ async function executeSinglePathEntries(
|
|
|
242
260
|
details: {
|
|
243
261
|
diff: diffTexts.join("\n"),
|
|
244
262
|
firstChangedLine,
|
|
263
|
+
path: metadataPath ?? path,
|
|
264
|
+
...(hasFirstOldText ? { oldText: firstOldText } : {}),
|
|
265
|
+
...(hasLastNewText ? { newText: lastNewText } : {}),
|
|
245
266
|
},
|
|
246
267
|
// Any per-entry failure marks the aggregate result as an error so the
|
|
247
268
|
// renderer takes the error branch instead of falling through to the
|
package/src/edit/modes/patch.ts
CHANGED
|
@@ -1772,15 +1772,25 @@ export async function executePatchSingle(
|
|
|
1772
1772
|
.diagnostics(mergedDiagnostics?.summary ?? "", mergedDiagnostics?.messages ?? [])
|
|
1773
1773
|
.get();
|
|
1774
1774
|
|
|
1775
|
+
const oldText = result.change.type !== "create" ? result.change.oldContent : undefined;
|
|
1776
|
+
const newText = result.change.type !== "delete" ? result.change.newContent : undefined;
|
|
1777
|
+
|
|
1775
1778
|
return {
|
|
1776
1779
|
content: [{ type: "text", text: resultText }],
|
|
1777
1780
|
details: {
|
|
1778
1781
|
diff: diffResult.diff,
|
|
1782
|
+
// When the patch moves the file, anchor the diff to the destination
|
|
1783
|
+
// path. ACP `ToolCallContent.diff.path` comes from this field, and
|
|
1784
|
+
// clients use it to open or focus the file post-change; pointing at
|
|
1785
|
+
// the (now-deleted) source navigates to nothing.
|
|
1786
|
+
path: result.change.newPath ?? resolvedPath,
|
|
1779
1787
|
firstChangedLine: diffResult.firstChangedLine,
|
|
1780
1788
|
diagnostics: mergedDiagnostics,
|
|
1781
1789
|
op,
|
|
1782
1790
|
move: effectiveRename,
|
|
1783
1791
|
meta,
|
|
1792
|
+
oldText,
|
|
1793
|
+
newText,
|
|
1784
1794
|
},
|
|
1785
1795
|
};
|
|
1786
1796
|
}
|
|
@@ -1094,9 +1094,12 @@ export async function executeReplaceSingle(
|
|
|
1094
1094
|
content: [{ type: "text", text: resultText }],
|
|
1095
1095
|
details: {
|
|
1096
1096
|
diff: diffResult.diff,
|
|
1097
|
+
path: absolutePath,
|
|
1097
1098
|
firstChangedLine: diffResult.firstChangedLine,
|
|
1098
1099
|
diagnostics,
|
|
1099
1100
|
meta,
|
|
1101
|
+
oldText: rawContent,
|
|
1102
|
+
newText: finalContent,
|
|
1100
1103
|
},
|
|
1101
1104
|
};
|
|
1102
1105
|
}
|
package/src/edit/renderer.ts
CHANGED
|
@@ -55,6 +55,10 @@ export interface EditToolPerFileResult {
|
|
|
55
55
|
* Set when the underlying error carries a `displayMessage` (e.g. {@link HashlineMismatchError}). */
|
|
56
56
|
displayErrorText?: string;
|
|
57
57
|
meta?: OutputMeta;
|
|
58
|
+
/** Source-of-truth content before the edit; `undefined` for create operations. */
|
|
59
|
+
oldText?: string;
|
|
60
|
+
/** Source-of-truth content after the edit; `undefined` for delete operations. */
|
|
61
|
+
newText?: string;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
export interface EditToolDetails {
|
|
@@ -72,6 +76,12 @@ export interface EditToolDetails {
|
|
|
72
76
|
meta?: OutputMeta;
|
|
73
77
|
/** Per-file results (multi-file edits) */
|
|
74
78
|
perFileResults?: EditToolPerFileResult[];
|
|
79
|
+
/** Absolute file path for single-file edit results. Required by ACP diff metadata consumers. */
|
|
80
|
+
path?: string;
|
|
81
|
+
/** Source-of-truth content before the edit; `undefined` for create operations. */
|
|
82
|
+
oldText?: string;
|
|
83
|
+
/** Source-of-truth content after the edit; `undefined` for delete operations. */
|
|
84
|
+
newText?: string;
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -330,7 +340,13 @@ function normalizeHashlineInputPreviewPath(rawPath: string): string {
|
|
|
330
340
|
|
|
331
341
|
function parseHashlineInputPreviewHeader(line: string): string | null {
|
|
332
342
|
if (!line.startsWith(HL_INPUT_HEADER_PREFIX)) return null;
|
|
333
|
-
|
|
343
|
+
// The real parser (`parseHashlineHeaderLine` in `hashline/input.ts`) strips
|
|
344
|
+
// every leading "@" before resolving the path so canonical "@@ PATH" headers
|
|
345
|
+
// (and stray "@ PATH" / "@@@ PATH" runs) all route to the same file. Mirror
|
|
346
|
+
// that here so the renderer doesn't surface a literal "@ " in the title.
|
|
347
|
+
let prefixEnd = 0;
|
|
348
|
+
while (prefixEnd < line.length && line[prefixEnd] === HL_INPUT_HEADER_PREFIX) prefixEnd++;
|
|
349
|
+
const body = line.slice(prefixEnd).trim();
|
|
334
350
|
const previewPath = normalizeHashlineInputPreviewPath(body);
|
|
335
351
|
return previewPath.length > 0 ? previewPath : null;
|
|
336
352
|
}
|
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
WorkerOutbound,
|
|
18
18
|
} from "./worker-protocol";
|
|
19
19
|
|
|
20
|
-
export {
|
|
20
|
+
export { rewriteImports, wrapCode } from "./shared/rewrite-imports";
|
|
21
21
|
export type { JsDisplayOutput } from "./worker-protocol";
|
|
22
22
|
|
|
23
23
|
export interface VmRunState {
|
package/src/eval/js/executor.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_MAX_BYTES, OutputSink } from "../../session/streaming-output";
|
|
2
2
|
import type { ToolSession } from "../../tools";
|
|
3
|
+
import { resolveOutputMaxColumns, resolveOutputSinkHeadBytes } from "../../tools/output-meta";
|
|
3
4
|
import { executeInVmContext, type JsDisplayOutput } from "./context-manager";
|
|
4
5
|
|
|
5
6
|
export interface JsExecutorOptions {
|
|
@@ -49,6 +50,8 @@ export async function executeJs(code: string, options: JsExecutorOptions): Promi
|
|
|
49
50
|
artifactPath: options.artifactPath,
|
|
50
51
|
artifactId: options.artifactId,
|
|
51
52
|
spillThreshold: DEFAULT_MAX_BYTES,
|
|
53
|
+
headBytes: resolveOutputSinkHeadBytes(options.session.settings),
|
|
54
|
+
maxColumns: resolveOutputMaxColumns(options.session.settings),
|
|
52
55
|
onChunk: chunk => options.onChunk?.(chunk),
|
|
53
56
|
});
|
|
54
57
|
const timeoutMs = getExecutionTimeoutMs(options);
|