@oh-my-pi/pi-coding-agent 15.5.6 → 15.5.8
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 +72 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +8 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +60 -12
- package/dist/types/edit/file-snapshot-store.d.ts +9 -6
- package/dist/types/edit/hashline/diff.d.ts +4 -5
- package/dist/types/edit/streaming.d.ts +2 -1
- package/dist/types/eval/py/index.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/vault-protocol.d.ts +93 -0
- package/dist/types/lib/xai-http.d.ts +40 -0
- package/dist/types/mcp/transports/http.d.ts +9 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -1
- package/dist/types/session/agent-session.d.ts +4 -1
- package/dist/types/tools/fetch.d.ts +16 -0
- package/dist/types/tools/image-gen.d.ts +6 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/match-line-format.d.ts +2 -2
- package/dist/types/tools/plan-mode-guard.d.ts +5 -6
- package/dist/types/tools/render-utils.d.ts +3 -1
- package/dist/types/tools/tts.d.ts +18 -0
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +2 -0
- package/package.json +8 -8
- package/src/cli/args.ts +2 -0
- package/src/cli/auth-broker-cli.ts +2 -1
- package/src/cli/auth-gateway-cli.ts +210 -9
- package/src/commands/auth-gateway.ts +7 -1
- package/src/config/model-registry.ts +41 -9
- package/src/config/settings-schema.ts +55 -13
- package/src/edit/file-snapshot-store.ts +9 -6
- package/src/edit/hashline/diff.ts +26 -13
- package/src/edit/hashline/execute.ts +13 -9
- package/src/edit/renderer.ts +9 -9
- package/src/edit/streaming.ts +4 -6
- package/src/eval/py/index.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/shared-events.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/router.ts +2 -0
- package/src/internal-urls/vault-protocol.ts +936 -0
- package/src/lib/xai-http.ts +124 -0
- package/src/main.ts +1 -2
- package/src/mcp/transports/http.ts +29 -2
- package/src/modes/components/tool-execution.ts +6 -4
- package/src/modes/controllers/event-controller.ts +10 -3
- package/src/modes/controllers/selector-controller.ts +7 -2
- package/src/modes/interactive-mode.ts +11 -3
- package/src/modes/utils/ui-helpers.ts +2 -1
- package/src/prompts/system/system-prompt.md +3 -0
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/read.md +3 -3
- package/src/prompts/tools/search.md +1 -1
- package/src/sdk.ts +41 -10
- package/src/session/agent-session.ts +112 -14
- package/src/system-prompt.ts +2 -0
- package/src/tools/ast-edit.ts +10 -7
- package/src/tools/ast-grep.ts +12 -11
- package/src/tools/eval.ts +28 -3
- package/src/tools/fetch.ts +52 -24
- package/src/tools/image-gen.ts +205 -7
- package/src/tools/index.ts +1 -0
- package/src/tools/match-line-format.ts +2 -2
- package/src/tools/path-utils.ts +2 -0
- package/src/tools/plan-mode-guard.ts +20 -7
- package/src/tools/read.ts +70 -55
- package/src/tools/render-utils.ts +15 -0
- package/src/tools/search.ts +14 -14
- package/src/tools/tts.ts +133 -0
- package/src/tools/write.ts +61 -6
- package/src/utils/file-mentions.ts +11 -5
- package/src/web/search/providers/codex.ts +2 -1
|
@@ -1568,16 +1568,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
1568
1568
|
},
|
|
1569
1569
|
},
|
|
1570
1570
|
|
|
1571
|
-
"edit.hashlineAutoDropPureInsertDuplicates": {
|
|
1572
|
-
type: "boolean",
|
|
1573
|
-
default: false,
|
|
1574
|
-
ui: {
|
|
1575
|
-
tab: "editing",
|
|
1576
|
-
label: "Hashline Duplicate Insert Drop",
|
|
1577
|
-
description:
|
|
1578
|
-
"Drop payload lines that duplicate adjacent file context — 2+-line context echoes on `↑`/`↓` inserts, and a single boundary line at either edge of an `A-B:` replacement",
|
|
1579
|
-
},
|
|
1580
|
-
},
|
|
1581
1571
|
"edit.blockAutoGenerated": {
|
|
1582
1572
|
type: "boolean",
|
|
1583
1573
|
default: true,
|
|
@@ -1605,7 +1595,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1605
1595
|
tab: "editing",
|
|
1606
1596
|
label: "Hash Lines",
|
|
1607
1597
|
description:
|
|
1608
|
-
"Include
|
|
1598
|
+
"Include snapshot-tag headers and line numbers in read output for hashline edit mode (¶PATH#tag plus LINE:content)",
|
|
1609
1599
|
},
|
|
1610
1600
|
},
|
|
1611
1601
|
|
|
@@ -2036,6 +2026,15 @@ export const SETTINGS_SCHEMA = {
|
|
|
2036
2026
|
},
|
|
2037
2027
|
},
|
|
2038
2028
|
|
|
2029
|
+
"tts.enabled": {
|
|
2030
|
+
type: "boolean",
|
|
2031
|
+
default: false,
|
|
2032
|
+
ui: {
|
|
2033
|
+
tab: "tools",
|
|
2034
|
+
label: "Text-to-Speech",
|
|
2035
|
+
description: "Enable the tts tool for xAI Grok Voice speech synthesis",
|
|
2036
|
+
},
|
|
2037
|
+
},
|
|
2039
2038
|
"recipe.enabled": {
|
|
2040
2039
|
type: "boolean",
|
|
2041
2040
|
default: true,
|
|
@@ -2074,6 +2073,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
2074
2073
|
ui: { tab: "tools", label: "Read URLs", description: "Allow the read tool to fetch and process URLs" },
|
|
2075
2074
|
},
|
|
2076
2075
|
|
|
2076
|
+
"vault.enabled": {
|
|
2077
|
+
type: "boolean",
|
|
2078
|
+
default: false,
|
|
2079
|
+
ui: {
|
|
2080
|
+
tab: "tools",
|
|
2081
|
+
label: "Obsidian Vault",
|
|
2082
|
+
description:
|
|
2083
|
+
"Enable the vault:// internal URL for reading and editing Obsidian vault content via the Obsidian CLI. When disabled, vault:// resolution is refused and the vault:// entry is omitted from the system prompt.",
|
|
2084
|
+
},
|
|
2085
|
+
},
|
|
2086
|
+
|
|
2077
2087
|
"github.enabled": {
|
|
2078
2088
|
type: "boolean",
|
|
2079
2089
|
default: false,
|
|
@@ -2698,7 +2708,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2698
2708
|
},
|
|
2699
2709
|
"providers.image": {
|
|
2700
2710
|
type: "enum",
|
|
2701
|
-
values: ["auto", "openai", "gemini", "openrouter"] as const,
|
|
2711
|
+
values: ["auto", "openai", "antigravity", "xai", "gemini", "openrouter"] as const,
|
|
2702
2712
|
default: "auto",
|
|
2703
2713
|
ui: {
|
|
2704
2714
|
tab: "providers",
|
|
@@ -2708,9 +2718,19 @@ export const SETTINGS_SCHEMA = {
|
|
|
2708
2718
|
{
|
|
2709
2719
|
value: "auto",
|
|
2710
2720
|
label: "Auto",
|
|
2711
|
-
description: "Priority: GPT model image tool > Antigravity > OpenRouter > Gemini",
|
|
2721
|
+
description: "Priority: GPT model image tool > Antigravity > xAI > OpenRouter > Gemini",
|
|
2712
2722
|
},
|
|
2713
2723
|
{ value: "openai", label: "OpenAI", description: "Uses the active GPT Responses/Codex model" },
|
|
2724
|
+
{
|
|
2725
|
+
value: "antigravity",
|
|
2726
|
+
label: "Antigravity",
|
|
2727
|
+
description: "Requires google-antigravity OAuth",
|
|
2728
|
+
},
|
|
2729
|
+
{
|
|
2730
|
+
value: "xai",
|
|
2731
|
+
label: "xAI Grok Imagine",
|
|
2732
|
+
description: "Requires xAI Grok OAuth or XAI_API_KEY",
|
|
2733
|
+
},
|
|
2714
2734
|
{ value: "gemini", label: "Gemini", description: "Requires GEMINI_API_KEY" },
|
|
2715
2735
|
{ value: "openrouter", label: "OpenRouter", description: "Requires OPENROUTER_API_KEY" },
|
|
2716
2736
|
],
|
|
@@ -2748,6 +2768,28 @@ export const SETTINGS_SCHEMA = {
|
|
|
2748
2768
|
},
|
|
2749
2769
|
},
|
|
2750
2770
|
|
|
2771
|
+
"providers.openrouterVariant": {
|
|
2772
|
+
type: "enum",
|
|
2773
|
+
values: ["default", "nitro", "floor", "online", "exacto"] as const,
|
|
2774
|
+
default: "default",
|
|
2775
|
+
ui: {
|
|
2776
|
+
tab: "providers",
|
|
2777
|
+
label: "OpenRouter Routing",
|
|
2778
|
+
description:
|
|
2779
|
+
"Default routing-variant suffix appended to OpenRouter model IDs (overridden when the selector already names a variant)",
|
|
2780
|
+
options: [
|
|
2781
|
+
{ value: "default", label: "Default", description: "No suffix; use OpenRouter's default routing" },
|
|
2782
|
+
{ value: "nitro", label: ":nitro", description: "Prioritize throughput / lowest latency" },
|
|
2783
|
+
{ value: "floor", label: ":floor", description: "Prioritize cheapest available provider" },
|
|
2784
|
+
{ value: "online", label: ":online", description: "Enable OpenRouter's web-search plugin" },
|
|
2785
|
+
{
|
|
2786
|
+
value: "exacto",
|
|
2787
|
+
label: ":exacto",
|
|
2788
|
+
description: "Cherry-picked high-quality providers (only defined for select models)",
|
|
2789
|
+
},
|
|
2790
|
+
],
|
|
2791
|
+
},
|
|
2792
|
+
},
|
|
2751
2793
|
"providers.parallelFetch": {
|
|
2752
2794
|
type: "boolean",
|
|
2753
2795
|
default: true,
|
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
* Session-bound file snapshot store.
|
|
3
3
|
*
|
|
4
4
|
* Used by `read` and `search` to record exactly what the model saw, and by
|
|
5
|
-
* the hashline patcher to recover from stale section
|
|
6
|
-
* externally between read and edit, or a prior in-session edit
|
|
7
|
-
* the
|
|
5
|
+
* the hashline patcher to verify or recover from stale section tags (file
|
|
6
|
+
* changed externally between read and edit, or a prior in-session edit
|
|
7
|
+
* advanced the tag). The store is the {@link InMemorySnapshotStore}
|
|
8
8
|
* from `@oh-my-pi/hashline`; the only coding-agent-specific concern here
|
|
9
|
-
* is wiring it onto the per-session
|
|
9
|
+
* is wiring it onto the per-session owner object.
|
|
10
10
|
*/
|
|
11
11
|
import { InMemorySnapshotStore } from "@oh-my-pi/hashline";
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
interface FileSnapshotStoreOwner {
|
|
14
|
+
fileSnapshotStore?: InMemorySnapshotStore;
|
|
15
|
+
}
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Look up (or lazily create) the file snapshot store attached to a session.
|
|
16
19
|
* Storage lives on `session.fileSnapshotStore` so it ages out exactly with
|
|
17
20
|
* the session itself.
|
|
18
21
|
*/
|
|
19
|
-
export function getFileSnapshotStore(session:
|
|
22
|
+
export function getFileSnapshotStore(session: FileSnapshotStoreOwner): InMemorySnapshotStore {
|
|
20
23
|
if (!session.fileSnapshotStore) session.fileSnapshotStore = new InMemorySnapshotStore();
|
|
21
24
|
return session.fileSnapshotStore;
|
|
22
25
|
}
|
|
@@ -5,16 +5,17 @@
|
|
|
5
5
|
* pair to {@link generateDiffString} so the renderer can show the diff
|
|
6
6
|
* while the tool call is still streaming.
|
|
7
7
|
*
|
|
8
|
-
* Validation is intentionally light: only the section
|
|
8
|
+
* Validation is intentionally light: only the section snapshot tag is checked
|
|
9
9
|
* (so the preview goes red when anchors are stale), no plan-mode guards
|
|
10
10
|
* and no auto-generated-file refusal — those belong on the write path.
|
|
11
11
|
*/
|
|
12
12
|
import {
|
|
13
|
-
computeFileHash,
|
|
14
13
|
Patch as HashlinePatch,
|
|
15
14
|
normalizeToLF,
|
|
16
15
|
type Patch,
|
|
17
16
|
type PatchSection,
|
|
17
|
+
type Snapshot,
|
|
18
|
+
type SnapshotStore,
|
|
18
19
|
stripBom,
|
|
19
20
|
} from "@oh-my-pi/hashline";
|
|
20
21
|
import { resolveToCwd } from "../../tools/path-utils";
|
|
@@ -22,7 +23,6 @@ import { generateDiffString } from "../diff";
|
|
|
22
23
|
import { readEditFileText } from "../read-file";
|
|
23
24
|
|
|
24
25
|
export interface HashlineDiffOptions {
|
|
25
|
-
autoDropPureInsertDuplicates?: boolean;
|
|
26
26
|
/**
|
|
27
27
|
* Use the streaming-tolerant applier ({@link PatchSection.applyPartialTo})
|
|
28
28
|
* so trailing in-flight ops do not throw or emit phantom edits. Streaming
|
|
@@ -44,20 +44,34 @@ function hasAnchorScoped(section: PatchSection): boolean {
|
|
|
44
44
|
return section.hasAnchorScopedEdit;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function
|
|
47
|
+
function snapshotMatchesCurrent(snapshot: Snapshot, currentText: string, anchorLines: readonly number[]): boolean {
|
|
48
|
+
if (snapshot.fullText !== undefined) return snapshot.fullText === currentText;
|
|
49
|
+
for (const lineNumber of anchorLines) {
|
|
50
|
+
if (snapshot.get(lineNumber) === undefined) return false;
|
|
51
|
+
}
|
|
52
|
+
return snapshot.matchesLiveFile(currentText.split("\n"));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function validateSectionHash(
|
|
56
|
+
section: PatchSection,
|
|
57
|
+
absolutePath: string,
|
|
58
|
+
text: string,
|
|
59
|
+
snapshots: SnapshotStore,
|
|
60
|
+
): string | null {
|
|
48
61
|
if (section.fileHash === undefined) {
|
|
49
62
|
return hasAnchorScoped(section)
|
|
50
|
-
? `Missing hashline
|
|
63
|
+
? `Missing hashline snapshot tag for anchored edit to ${section.path}; use \`¶${section.path}#tag\` from your latest read.`
|
|
51
64
|
: null;
|
|
52
65
|
}
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
55
|
-
return `Hashline
|
|
66
|
+
const snapshot = snapshots.byHash(absolutePath, section.fileHash);
|
|
67
|
+
if (snapshot && snapshotMatchesCurrent(snapshot, text, section.collectAnchorLines())) return null;
|
|
68
|
+
return `Hashline snapshot tag mismatch for ${section.path}: section is bound to #${section.fileHash}, but current file does not match that snapshot; re-read and try again.`;
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
export async function computeHashlineSectionDiff(
|
|
59
72
|
section: PatchSection,
|
|
60
73
|
cwd: string,
|
|
74
|
+
snapshots: SnapshotStore,
|
|
61
75
|
options: HashlineDiffOptions = {},
|
|
62
76
|
): Promise<{ diff: string; firstChangedLine: number | undefined } | { error: string }> {
|
|
63
77
|
try {
|
|
@@ -65,11 +79,9 @@ export async function computeHashlineSectionDiff(
|
|
|
65
79
|
const rawContent = await readSectionText(absolutePath, section.path);
|
|
66
80
|
const { text: content } = stripBom(rawContent);
|
|
67
81
|
const normalized = normalizeToLF(content);
|
|
68
|
-
const hashError = validateSectionHash(section, normalized);
|
|
82
|
+
const hashError = validateSectionHash(section, absolutePath, normalized, snapshots);
|
|
69
83
|
if (hashError) return { error: hashError };
|
|
70
|
-
const result = options.streaming
|
|
71
|
-
? section.applyPartialTo(normalized, options)
|
|
72
|
-
: section.applyTo(normalized, options);
|
|
84
|
+
const result = options.streaming ? section.applyPartialTo(normalized) : section.applyTo(normalized);
|
|
73
85
|
if (normalized === result.text) return { error: `No changes would be made to ${section.path}.` };
|
|
74
86
|
return generateDiffString(normalized, result.text);
|
|
75
87
|
} catch (err) {
|
|
@@ -80,6 +92,7 @@ export async function computeHashlineSectionDiff(
|
|
|
80
92
|
export async function computeHashlineDiff(
|
|
81
93
|
input: { input: string },
|
|
82
94
|
cwd: string,
|
|
95
|
+
snapshots: SnapshotStore,
|
|
83
96
|
options: HashlineDiffOptions = {},
|
|
84
97
|
): Promise<{ diff: string; firstChangedLine: number | undefined } | { error: string }> {
|
|
85
98
|
let patch: Patch;
|
|
@@ -91,5 +104,5 @@ export async function computeHashlineDiff(
|
|
|
91
104
|
if (patch.sections.length !== 1) {
|
|
92
105
|
return { error: "Streaming diff preview supports exactly one hashline section." };
|
|
93
106
|
}
|
|
94
|
-
return computeHashlineSectionDiff(patch.sections[0], cwd, options);
|
|
107
|
+
return computeHashlineSectionDiff(patch.sections[0], cwd, snapshots, options);
|
|
95
108
|
}
|
|
@@ -37,14 +37,19 @@ export interface ExecuteHashlineSingleOptions {
|
|
|
37
37
|
beginDeferredDiagnosticsForPath: (path: string) => WritethroughDeferredHandle;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function getHashlineApplyOptions(session: ToolSession): { autoDropPureInsertDuplicates: boolean } {
|
|
41
|
-
return {
|
|
42
|
-
autoDropPureInsertDuplicates: session.settings.get("edit.hashlineAutoDropPureInsertDuplicates"),
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
40
|
function noChangeDiagnostic(path: string): string {
|
|
47
|
-
|
|
41
|
+
// The patch parsed and applied cleanly but produced no change — the
|
|
42
|
+
// `|literal` body rows matched the file content at the targeted lines
|
|
43
|
+
// byte-for-byte. The model usually misreads this as "wrong anchor, try
|
|
44
|
+
// again with a bigger payload" and starts duplicating content; the
|
|
45
|
+
// message below names the cause directly so the next turn can re-read
|
|
46
|
+
// instead of expanding the patch.
|
|
47
|
+
return (
|
|
48
|
+
`Edits to ${path} parsed and applied cleanly, but produced no change: ` +
|
|
49
|
+
`your body row(s) are byte-identical to the file at the targeted lines. ` +
|
|
50
|
+
`The bug is somewhere else — re-read the file before issuing another edit. ` +
|
|
51
|
+
`Do NOT widen the payload or add lines; verify the anchor first.`
|
|
52
|
+
);
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
function assertUniqueCanonicalPaths(prepared: readonly PreparedSection[]): void {
|
|
@@ -128,8 +133,7 @@ export async function executeHashlineSingle(
|
|
|
128
133
|
batchRequest: options.batchRequest,
|
|
129
134
|
});
|
|
130
135
|
const snapshots = getFileSnapshotStore(options.session);
|
|
131
|
-
const
|
|
132
|
-
const patcher = new Patcher({ fs, snapshots, applyOptions });
|
|
136
|
+
const patcher = new Patcher({ fs, snapshots });
|
|
133
137
|
|
|
134
138
|
// Single-section fast path: prepare, commit, render.
|
|
135
139
|
if (patch.sections.length === 1) {
|
package/src/edit/renderer.ts
CHANGED
|
@@ -235,24 +235,24 @@ function renderPlainTextPreview(text: string, uiTheme: Theme, filePath?: string)
|
|
|
235
235
|
|
|
236
236
|
function formatStreamingDiff(diff: string, rawPath: string, uiTheme: Theme, label = "streaming"): string {
|
|
237
237
|
if (!diff) return "";
|
|
238
|
-
// Hunk-aware truncation keeps the change rows themselves visible
|
|
239
|
-
//
|
|
240
|
-
//
|
|
238
|
+
// Hunk-aware truncation keeps the change rows themselves visible. Tail-mode
|
|
239
|
+
// pins the visible window to the bottom of the diff so newly streamed
|
|
240
|
+
// hunks stay on screen as more arrives, instead of leaving the user stuck
|
|
241
|
+
// staring at the head of the file while the tail scrolls offscreen.
|
|
241
242
|
const {
|
|
242
243
|
text: truncatedDiff,
|
|
243
244
|
hiddenHunks,
|
|
244
245
|
hiddenLines,
|
|
245
|
-
} = truncateDiffByHunk(diff, PREVIEW_LIMITS.DIFF_COLLAPSED_HUNKS, EDIT_STREAMING_PREVIEW_LINES);
|
|
246
|
+
} = truncateDiffByHunk(diff, PREVIEW_LIMITS.DIFF_COLLAPSED_HUNKS, EDIT_STREAMING_PREVIEW_LINES, { fromTail: true });
|
|
246
247
|
let text = "\n\n";
|
|
247
|
-
text += renderDiffColored(truncatedDiff, { filePath: rawPath });
|
|
248
248
|
if (hiddenHunks > 0 || hiddenLines > 0) {
|
|
249
249
|
const remainder: string[] = [];
|
|
250
250
|
if (hiddenHunks > 0) remainder.push(`${hiddenHunks} more hunks`);
|
|
251
251
|
if (hiddenLines > 0) remainder.push(`${hiddenLines} more lines`);
|
|
252
|
-
text += uiTheme.fg("dim",
|
|
253
|
-
} else {
|
|
254
|
-
text += uiTheme.fg("dim", `\n(${label})`);
|
|
252
|
+
text += `${uiTheme.fg("dim", `… (${remainder.join(", ")} above)`)}\n`;
|
|
255
253
|
}
|
|
254
|
+
text += renderDiffColored(truncatedDiff, { filePath: rawPath });
|
|
255
|
+
text += uiTheme.fg("dim", `\n(${label})`);
|
|
256
256
|
return text;
|
|
257
257
|
}
|
|
258
258
|
|
|
@@ -312,7 +312,7 @@ const MISSING_APPLY_PATCH_END_ERROR = "The last line of the patch must be '*** E
|
|
|
312
312
|
|
|
313
313
|
function normalizeHashlineInputPreviewPath(rawPath: string): string {
|
|
314
314
|
const trimmed = rawPath.trim();
|
|
315
|
-
const hashStart = /#[0-9a-
|
|
315
|
+
const hashStart = /#[0-9a-fA-F]{3}$/u.exec(trimmed)?.index;
|
|
316
316
|
const withoutHash = hashStart === undefined ? trimmed : trimmed.slice(0, hashStart);
|
|
317
317
|
if (withoutHash.length < 2) return withoutHash;
|
|
318
318
|
const first = withoutHash[0];
|
package/src/edit/streaming.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
END_PATCH_MARKER,
|
|
21
21
|
type PatchSection as HashlineInputSection,
|
|
22
22
|
Patch as HashlinePatch,
|
|
23
|
+
type SnapshotStore,
|
|
23
24
|
} from "@oh-my-pi/hashline";
|
|
24
25
|
import type { Theme } from "../modes/theme/theme";
|
|
25
26
|
import { type EditMode, resolveEditMode } from "../utils/edit-mode";
|
|
@@ -39,9 +40,9 @@ export interface PerFileDiffPreview {
|
|
|
39
40
|
export interface StreamingDiffContext {
|
|
40
41
|
cwd: string;
|
|
41
42
|
signal: AbortSignal;
|
|
43
|
+
snapshots: SnapshotStore;
|
|
42
44
|
fuzzyThreshold?: number;
|
|
43
45
|
allowFuzzy?: boolean;
|
|
44
|
-
hashlineAutoDropPureInsertDuplicates?: boolean;
|
|
45
46
|
/**
|
|
46
47
|
* True while the tool's arguments are still streaming in. Strategies that
|
|
47
48
|
* accept free-form text input (apply_patch, hashline) trim the trailing
|
|
@@ -325,9 +326,7 @@ const hashlineStrategy: EditStreamingStrategy<HashlineArgs> = {
|
|
|
325
326
|
// to parse; suppress until the next chunk arrives. Once args are
|
|
326
327
|
// complete, surface the error so the model sees what went wrong.
|
|
327
328
|
if (ctx.isStreaming) return null;
|
|
328
|
-
const result = await computeHashlineDiff({ input }, ctx.cwd,
|
|
329
|
-
autoDropPureInsertDuplicates: ctx.hashlineAutoDropPureInsertDuplicates,
|
|
330
|
-
});
|
|
329
|
+
const result = await computeHashlineDiff({ input }, ctx.cwd, ctx.snapshots);
|
|
331
330
|
ctx.signal.throwIfAborted();
|
|
332
331
|
return [toPerFilePreview("", result)];
|
|
333
332
|
}
|
|
@@ -346,8 +345,7 @@ const hashlineStrategy: EditStreamingStrategy<HashlineArgs> = {
|
|
|
346
345
|
for (let i = 0; i < sectionsToProcess.length; i++) {
|
|
347
346
|
ctx.signal.throwIfAborted();
|
|
348
347
|
const section = sectionsToProcess[i];
|
|
349
|
-
const result = await computeHashlineSectionDiff(section, ctx.cwd, {
|
|
350
|
-
autoDropPureInsertDuplicates: ctx.hashlineAutoDropPureInsertDuplicates,
|
|
348
|
+
const result = await computeHashlineSectionDiff(section, ctx.cwd, ctx.snapshots, {
|
|
351
349
|
streaming: ctx.isStreaming,
|
|
352
350
|
});
|
|
353
351
|
ctx.signal.throwIfAborted();
|
package/src/eval/py/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { checkPythonKernelAvailability } from "./kernel";
|
|
|
5
5
|
|
|
6
6
|
const PYTHON_SESSION_PREFIX = "python:";
|
|
7
7
|
|
|
8
|
-
function namespaceSessionId(sessionId: string): string {
|
|
8
|
+
export function namespaceSessionId(sessionId: string): string {
|
|
9
9
|
return sessionId.startsWith(PYTHON_SESSION_PREFIX) ? sessionId : `${PYTHON_SESSION_PREFIX}${sessionId}`;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -100,7 +100,7 @@ export type CustomToolSessionEvent =
|
|
|
100
100
|
}
|
|
101
101
|
| {
|
|
102
102
|
reason: "auto_compaction_start";
|
|
103
|
-
trigger: "threshold" | "overflow" | "idle";
|
|
103
|
+
trigger: "threshold" | "overflow" | "idle" | "incomplete";
|
|
104
104
|
action: "context-full" | "handoff";
|
|
105
105
|
}
|
|
106
106
|
| {
|
|
@@ -203,7 +203,7 @@ export interface TurnEndEvent {
|
|
|
203
203
|
/** Fired when auto-compaction starts */
|
|
204
204
|
export interface AutoCompactionStartEvent {
|
|
205
205
|
type: "auto_compaction_start";
|
|
206
|
-
reason: "threshold" | "overflow" | "idle";
|
|
206
|
+
reason: "threshold" | "overflow" | "idle" | "incomplete";
|
|
207
207
|
action: "context-full" | "handoff";
|
|
208
208
|
}
|
|
209
209
|
|