@hy-sde-org/dsh-tool-fs-internal-urls 0.1.1-rc.2
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/LICENSE +37 -0
- package/README.md +56 -0
- package/dist/diff.d.ts +38 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +71 -0
- package/dist/diff.js.map +1 -0
- package/dist/edit.d.ts +43 -0
- package/dist/edit.d.ts.map +1 -0
- package/dist/edit.js +137 -0
- package/dist/edit.js.map +1 -0
- package/dist/error.d.ts +20 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +33 -0
- package/dist/error.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/internal-routing.d.ts +61 -0
- package/dist/internal-routing.d.ts.map +1 -0
- package/dist/internal-routing.js +95 -0
- package/dist/internal-routing.js.map +1 -0
- package/dist/invariant.d.ts +16 -0
- package/dist/invariant.d.ts.map +1 -0
- package/dist/invariant.js +22 -0
- package/dist/invariant.js.map +1 -0
- package/dist/read-image.d.ts +71 -0
- package/dist/read-image.d.ts.map +1 -0
- package/dist/read-image.js +240 -0
- package/dist/read-image.js.map +1 -0
- package/dist/read-render.d.ts +113 -0
- package/dist/read-render.d.ts.map +1 -0
- package/dist/read-render.js +196 -0
- package/dist/read-render.js.map +1 -0
- package/dist/read-target.d.ts +19 -0
- package/dist/read-target.d.ts.map +1 -0
- package/dist/read-target.js +26 -0
- package/dist/read-target.js.map +1 -0
- package/dist/read.d.ts +49 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +205 -0
- package/dist/read.js.map +1 -0
- package/dist/sandbox.d.ts +81 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +110 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/session-cwd.d.ts +30 -0
- package/dist/session-cwd.d.ts.map +1 -0
- package/dist/session-cwd.js +39 -0
- package/dist/session-cwd.js.map +1 -0
- package/dist/write.d.ts +36 -0
- package/dist/write.d.ts.map +1 -0
- package/dist/write.js +152 -0
- package/dist/write.js.map +1 -0
- package/package.json +86 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hy-sde
|
|
4
|
+
|
|
5
|
+
This repository ports the FS-shaped internal-URL system (conflict://, issue://,
|
|
6
|
+
pr:// — one resolver registry behind read/grep/write) from oh-my-pi's
|
|
7
|
+
coding-agent internal-urls and conflict-detect modules, adapted to the
|
|
8
|
+
DeepSeek Harness file-services seam. Ported portions are distributed under the
|
|
9
|
+
same MIT license with their original copyright held by their respective
|
|
10
|
+
authors:
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2025 Mario Zechner
|
|
13
|
+
Copyright (c) 2025-2026 Can Bölük
|
|
14
|
+
|
|
15
|
+
See THIRD-PARTY-NOTICES.md for full provenance per module.
|
|
16
|
+
|
|
17
|
+
Parts of this repository are derived from DeepSeek Harness
|
|
18
|
+
(https://github.com/deepseek-ai/deepseek-harness), MIT License,
|
|
19
|
+
Copyright (c) 2026 DeepSeek.
|
|
20
|
+
|
|
21
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
22
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
23
|
+
in the Software without restriction, including without limitation the rights
|
|
24
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
25
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
26
|
+
furnished to do so, subject to the following conditions:
|
|
27
|
+
|
|
28
|
+
The above copyright notice and this permission notice shall be included in all
|
|
29
|
+
copies or substantial portions of the Software.
|
|
30
|
+
|
|
31
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
32
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
33
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
34
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
35
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
36
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
37
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @hy-sde-org/dsh-tool-fs-internal-urls
|
|
2
|
+
|
|
3
|
+
The `read` / `write` / `edit` filesystem tool suite for DeepSeek Harness with
|
|
4
|
+
**internal-URL routing built in**: when `ctx.internalUrls` is mounted, these
|
|
5
|
+
tools resolve `conflict://`, `pr://`, `issue://` URLs (and `<path>:conflicts`
|
|
6
|
+
selectors) through the registry instead of a filesystem path, and scan plain
|
|
7
|
+
filesystem reads for git conflict blocks — registering them with the session
|
|
8
|
+
history and appending a resolution notice. This is the hy-sde fork's
|
|
9
|
+
`dsh-tool-fs` (with `src/internal-routing.ts` + the `read.ts`/`write.ts`
|
|
10
|
+
hunks) shipped as an agent-scope shadow so it works on **stock** DeepSeek
|
|
11
|
+
Harness releases (`dsh-v0.1.1-rc.2` and later).
|
|
12
|
+
|
|
13
|
+
Mount it in an agent preset (see `examples/agent-preset/` in
|
|
14
|
+
`@hy-sde-org/dsh-internal-urls`): agent-scope shadowing makes these THE
|
|
15
|
+
`read`/`write`/`edit` for that session, while plain filesystem behavior is
|
|
16
|
+
identical to the stock tools. Without a mounted `ctx.internalUrls` registry
|
|
17
|
+
the routing branch never triggers and the tools are stock-equivalent.
|
|
18
|
+
|
|
19
|
+
- `read` — line-numbered windows (caps: `readLimit` 2000 lines,
|
|
20
|
+
`readMaxLineLength` 2000 chars, `readMaxBytes` 50 KiB, stream threshold
|
|
21
|
+
10 MiB), `fs/observed` emission, internal-URL virtual reads, conflict
|
|
22
|
+
surfacing + notice.
|
|
23
|
+
- `write` — create/overwrite with sandbox-policy escalation
|
|
24
|
+
(`fs/write-intent` waterfall, error remediation). Internal-URL writes
|
|
25
|
+
(`conflict://<N>` resolution) dispatch to the handler with the session
|
|
26
|
+
context and the resolved policy so the backing-file splice stays fenced.
|
|
27
|
+
- `edit` — literal single-match (or replace-all) edits over `ctx.fs` with
|
|
28
|
+
guard + remediation, unchanged from the harness (this package keeps the
|
|
29
|
+
trio together).
|
|
30
|
+
- `read_image` — registers only while `attachments` is mounted, unchanged.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
As a routing surface this package is useless without
|
|
35
|
+
`@hy-sde-org/dsh-internal-urls`; follow that package's README — all three
|
|
36
|
+
packages install together:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
dsh plugin --profile web add @hy-sde-org/dsh-internal-urls \
|
|
40
|
+
@hy-sde-org/dsh-tool-fs-internal-urls \
|
|
41
|
+
@hy-sde-org/dsh-tool-fs-search-internal-urls
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then copy the preset from `packages/internal-urls/examples/agent-preset/` in
|
|
45
|
+
this repo (or the installed package) to `~/.dsh/.agent-presets/<id>/` — its
|
|
46
|
+
`tool-fs-internal-urls` row is this package.
|
|
47
|
+
|
|
48
|
+
## Defaults & caps
|
|
49
|
+
|
|
50
|
+
| Key | Default | Meaning |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `readLimit` | `2000` | lines per `read` call |
|
|
53
|
+
| `readMaxLineLength` | `2000` | chars per returned line |
|
|
54
|
+
| `readMaxBytes` | `50 KiB` | bytes of selected output |
|
|
55
|
+
| `readStreamMinSize` | `10 MiB` | stream files at/above this size |
|
|
56
|
+
| `enableEdit` | `true` | set `false` to leave `edit` to another provider |
|
package/dist/diff.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result-time contextual diff presentation for write and edit. Storage returns before/after
|
|
3
|
+
* text; this model-facing layer derives one three-line-context card per applied hunk.
|
|
4
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls/src/diff
|
|
5
|
+
*/
|
|
6
|
+
import type { FileDiff } from '@deepseek-ai/dsh-tools';
|
|
7
|
+
/** Context lines shown on each side of an applied hunk. */
|
|
8
|
+
export declare const DIFF_CONTEXT = 3;
|
|
9
|
+
/**
|
|
10
|
+
* The `write`/`edit` tools' private `tool/result` `meta` payload: the applied
|
|
11
|
+
* contextual-diff hunks. Attached opaquely (as `unknown`) on the tool result and
|
|
12
|
+
* persisted with the session log — it must be JSON-serializable (the session
|
|
13
|
+
* validates this at `append`), so `presentResult` reproduces the diff card on
|
|
14
|
+
* replay. The producing tool owns and narrows this opaque shape.
|
|
15
|
+
*/
|
|
16
|
+
export type FsDiffMeta = {
|
|
17
|
+
diffs: FileDiff[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Compute one {@link FileDiff} per hunk between `before` and `after`, each carrying the
|
|
21
|
+
* applied change plus {@link DIFF_CONTEXT} context lines. Pure insertions use `oldText: null`,
|
|
22
|
+
* patch-only no-newline markers are omitted, and scattered replacements remain separate hunks.
|
|
23
|
+
*
|
|
24
|
+
* @param path - the path stamped on every produced diff (the model-facing `file_path`; the
|
|
25
|
+
* bridge relativizes it).
|
|
26
|
+
* @param before - the file text before the change (the backend's LF-normalized diff basis).
|
|
27
|
+
* @param after - the file text after the change, on the same basis.
|
|
28
|
+
* @returns one diff per applied hunk, in file order; empty when the texts are identical.
|
|
29
|
+
*/
|
|
30
|
+
export declare function computeHunkDiffs(path: string, before: string, after: string): FileDiff[];
|
|
31
|
+
/**
|
|
32
|
+
* Narrow opaque live or replayed result metadata to non-empty file diffs. Malformed metadata
|
|
33
|
+
* returns `undefined` so presentation can fall back instead of throwing during replay.
|
|
34
|
+
* @param meta - result metadata.
|
|
35
|
+
* @returns validated hunks, or `undefined` for absent or malformed data.
|
|
36
|
+
*/
|
|
37
|
+
export declare function diffsFromMeta(meta: unknown): FileDiff[] | undefined;
|
|
38
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEtD,2DAA2D;AAC3D,eAAO,MAAM,YAAY,IAAI,CAAA;AAE7B;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAA;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,CAwBxF;AAWD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,EAAE,GAAG,SAAS,CAKnE"}
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result-time contextual diff presentation for write and edit. Storage returns before/after
|
|
3
|
+
* text; this model-facing layer derives one three-line-context card per applied hunk.
|
|
4
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls/src/diff
|
|
5
|
+
*/
|
|
6
|
+
import { structuredPatch } from 'diff';
|
|
7
|
+
/** Context lines shown on each side of an applied hunk. */
|
|
8
|
+
export const DIFF_CONTEXT = 3;
|
|
9
|
+
/**
|
|
10
|
+
* Compute one {@link FileDiff} per hunk between `before` and `after`, each carrying the
|
|
11
|
+
* applied change plus {@link DIFF_CONTEXT} context lines. Pure insertions use `oldText: null`,
|
|
12
|
+
* patch-only no-newline markers are omitted, and scattered replacements remain separate hunks.
|
|
13
|
+
*
|
|
14
|
+
* @param path - the path stamped on every produced diff (the model-facing `file_path`; the
|
|
15
|
+
* bridge relativizes it).
|
|
16
|
+
* @param before - the file text before the change (the backend's LF-normalized diff basis).
|
|
17
|
+
* @param after - the file text after the change, on the same basis.
|
|
18
|
+
* @returns one diff per applied hunk, in file order; empty when the texts are identical.
|
|
19
|
+
*/
|
|
20
|
+
export function computeHunkDiffs(path, before, after) {
|
|
21
|
+
const patch = structuredPatch('', '', before, after, undefined, undefined, { context: DIFF_CONTEXT });
|
|
22
|
+
const diffs = [];
|
|
23
|
+
for (const hunk of patch.hunks) {
|
|
24
|
+
const oldLines = [];
|
|
25
|
+
const newLines = [];
|
|
26
|
+
for (const line of hunk.lines) {
|
|
27
|
+
// The unified-diff marker for a missing trailing newline annotates the
|
|
28
|
+
// patch, not the content — skip it so it never leaks into a diff block.
|
|
29
|
+
if (line.startsWith('\\'))
|
|
30
|
+
continue;
|
|
31
|
+
const text = line.slice(1);
|
|
32
|
+
if (line.startsWith('-')) {
|
|
33
|
+
oldLines.push(text);
|
|
34
|
+
}
|
|
35
|
+
else if (line.startsWith('+')) {
|
|
36
|
+
newLines.push(text);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// A context (unchanged) line appears on both sides.
|
|
40
|
+
oldLines.push(text);
|
|
41
|
+
newLines.push(text);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
diffs.push({ path, oldText: oldLines.length > 0 ? oldLines.join('\n') : null, newText: newLines.join('\n') });
|
|
45
|
+
}
|
|
46
|
+
return diffs;
|
|
47
|
+
}
|
|
48
|
+
/** Whether `value` is a valid {@link FileDiff} (defensive narrowing from opaque `meta`). */
|
|
49
|
+
function isFileDiff(value) {
|
|
50
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
51
|
+
return false;
|
|
52
|
+
const { path, oldText, newText } = value;
|
|
53
|
+
return typeof path === 'string'
|
|
54
|
+
&& (oldText === null || typeof oldText === 'string')
|
|
55
|
+
&& typeof newText === 'string';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Narrow opaque live or replayed result metadata to non-empty file diffs. Malformed metadata
|
|
59
|
+
* returns `undefined` so presentation can fall back instead of throwing during replay.
|
|
60
|
+
* @param meta - result metadata.
|
|
61
|
+
* @returns validated hunks, or `undefined` for absent or malformed data.
|
|
62
|
+
*/
|
|
63
|
+
export function diffsFromMeta(meta) {
|
|
64
|
+
if (typeof meta !== 'object' || meta === null || Array.isArray(meta))
|
|
65
|
+
return undefined;
|
|
66
|
+
const diffs = meta.diffs;
|
|
67
|
+
if (!Array.isArray(diffs) || diffs.length === 0 || !diffs.every(isFileDiff))
|
|
68
|
+
return undefined;
|
|
69
|
+
return diffs;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=diff.js.map
|
package/dist/diff.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAGtC,2DAA2D;AAC3D,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAA;AAW7B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,MAAc,EAAE,KAAa;IAC1E,MAAM,KAAK,GAAG,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAA;IACrG,MAAM,KAAK,GAAe,EAAE,CAAA;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAa,EAAE,CAAA;QAC7B,MAAM,QAAQ,GAAa,EAAE,CAAA;QAC7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,uEAAuE;YACvE,wEAAwE;YACxE,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,SAAQ;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YAC1B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACrB,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACrB,CAAC;iBAAM,CAAC;gBACN,oDAAoD;gBACpD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACnB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACrB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC/G,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,4FAA4F;AAC5F,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACrF,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAgC,CAAA;IACnE,OAAO,OAAO,IAAI,KAAK,QAAQ;WAC1B,CAAC,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC;WACjD,OAAO,OAAO,KAAK,QAAQ,CAAA;AAClC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IACtF,MAAM,KAAK,GAAI,IAAgC,CAAC,KAAK,CAAA;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAA;IAC7F,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/dist/edit.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing literal edit, unique-match by default. It obtains an optional guard from the
|
|
3
|
+
* single intent slot, calls `ctx.fs.editText` without a separate stat, then records the observed
|
|
4
|
+
* version; no policy means an unconditional atomic edit.
|
|
5
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls/src/edit
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import type { FsSandboxController } from './sandbox.ts';
|
|
9
|
+
/** Validated `edit` arguments after defaulting. */
|
|
10
|
+
interface EditInput {
|
|
11
|
+
filePath: string;
|
|
12
|
+
oldString: string;
|
|
13
|
+
newString: string;
|
|
14
|
+
replaceAll: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate value constraints the schema DSL can't express: a non-blank
|
|
18
|
+
* `file_path`, a non-empty `old_string`, and `old_string !== new_string`
|
|
19
|
+
* (an equal pair would be a guaranteed no-op edit).
|
|
20
|
+
* @param args - the schema-validated raw tool arguments.
|
|
21
|
+
* @returns the camelCased input with `replace_all` defaulted to false.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseEditArgs(args: {
|
|
24
|
+
file_path: string;
|
|
25
|
+
old_string: string;
|
|
26
|
+
new_string: string;
|
|
27
|
+
replace_all?: boolean;
|
|
28
|
+
}): EditInput;
|
|
29
|
+
/**
|
|
30
|
+
* Format an edit success (single-match or replace-all) as a Claude-style model-facing message.
|
|
31
|
+
* @param displayPath - the backend-resolved path shown to the model.
|
|
32
|
+
* @param replaceAll - selects the all-occurrences wording over the single-replacement one.
|
|
33
|
+
* @returns the confirmation sentence the model sees as the tool result.
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatEditOutput(displayPath: string, replaceAll: boolean): string;
|
|
36
|
+
/**
|
|
37
|
+
* Register the `edit` tool and its system-prompt guidance.
|
|
38
|
+
* @param ctx - the plugin context; registrations are effects scoped to it, and execution uses its `fs` service.
|
|
39
|
+
* @param sandbox - the shared sandbox-escalation API (advertisement, mode stamping, denial mapping).
|
|
40
|
+
*/
|
|
41
|
+
export declare function applyEditTool(ctx: Context, sandbox: FsSandboxController): void;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=edit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAQlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAEvD,mDAAmD;AACnD,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;CACpB;AAgBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAUnI;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAIjF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CA4F9E"}
|
package/dist/edit.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing literal edit, unique-match by default. It obtains an optional guard from the
|
|
3
|
+
* single intent slot, calls `ctx.fs.editText` without a separate stat, then records the observed
|
|
4
|
+
* version; no policy means an unconditional atomic edit.
|
|
5
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls/src/edit
|
|
6
|
+
*/
|
|
7
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
8
|
+
import { computeHunkDiffs, diffsFromMeta } from "./diff.js";
|
|
9
|
+
import { remediateFsError } from "./error.js";
|
|
10
|
+
import { sessionResolveOptions } from "./session-cwd.js";
|
|
11
|
+
/**
|
|
12
|
+
* Validate value constraints the schema DSL can't express: a non-blank
|
|
13
|
+
* `file_path`, a non-empty `old_string`, and `old_string !== new_string`
|
|
14
|
+
* (an equal pair would be a guaranteed no-op edit).
|
|
15
|
+
* @param args - the schema-validated raw tool arguments.
|
|
16
|
+
* @returns the camelCased input with `replace_all` defaulted to false.
|
|
17
|
+
*/
|
|
18
|
+
export function parseEditArgs(args) {
|
|
19
|
+
if (args.file_path.trim().length === 0)
|
|
20
|
+
throw new Error('file_path must be a non-empty string');
|
|
21
|
+
if (args.old_string.length === 0)
|
|
22
|
+
throw new Error('old_string must be a non-empty string');
|
|
23
|
+
if (args.old_string === args.new_string)
|
|
24
|
+
throw new Error('old_string and new_string must differ');
|
|
25
|
+
return {
|
|
26
|
+
filePath: args.file_path,
|
|
27
|
+
oldString: args.old_string,
|
|
28
|
+
newString: args.new_string,
|
|
29
|
+
replaceAll: args.replace_all ?? false,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Format an edit success (single-match or replace-all) as a Claude-style model-facing message.
|
|
34
|
+
* @param displayPath - the backend-resolved path shown to the model.
|
|
35
|
+
* @param replaceAll - selects the all-occurrences wording over the single-replacement one.
|
|
36
|
+
* @returns the confirmation sentence the model sees as the tool result.
|
|
37
|
+
*/
|
|
38
|
+
export function formatEditOutput(displayPath, replaceAll) {
|
|
39
|
+
return replaceAll
|
|
40
|
+
? `The file ${displayPath} has been updated. All occurrences were successfully replaced.`
|
|
41
|
+
: `The file ${displayPath} has been updated successfully.`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Register the `edit` tool and its system-prompt guidance.
|
|
45
|
+
* @param ctx - the plugin context; registrations are effects scoped to it, and execution uses its `fs` service.
|
|
46
|
+
* @param sandbox - the shared sandbox-escalation API (advertisement, mode stamping, denial mapping).
|
|
47
|
+
*/
|
|
48
|
+
export function applyEditTool(ctx, sandbox) {
|
|
49
|
+
ctx.systemPrompt.section({
|
|
50
|
+
name: 'tool:edit',
|
|
51
|
+
order: 102,
|
|
52
|
+
text: 'Use the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-observation-policy requires it), unless you just created or edited it in this session.',
|
|
53
|
+
});
|
|
54
|
+
ctx.tools.register(defineTool({
|
|
55
|
+
name: 'edit',
|
|
56
|
+
description: 'Edit an existing UTF-8 text file by replacing literal text.',
|
|
57
|
+
parameters: {
|
|
58
|
+
file_path: { type: 'string', required: true, description: 'Path to edit, resolved by the filesystem backend.' },
|
|
59
|
+
old_string: { type: 'string', required: true, description: 'Literal text to replace. Must match exactly.' },
|
|
60
|
+
new_string: { type: 'string', required: true, description: 'Literal replacement text. Use an empty string to delete the match.' },
|
|
61
|
+
replace_all: { type: 'boolean', description: 'Replace all matches. Defaults to false; when false, old_string must appear exactly once.' },
|
|
62
|
+
...sandbox.escalationModes.length > 0 ? sandbox.schemaFields() : {},
|
|
63
|
+
},
|
|
64
|
+
output: {
|
|
65
|
+
schema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
additionalProperties: false,
|
|
68
|
+
properties: {
|
|
69
|
+
path: { type: 'string', required: true },
|
|
70
|
+
before: { type: 'string', required: true },
|
|
71
|
+
after: { type: 'string', required: true },
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
render: (args, value) => [{
|
|
75
|
+
type: 'text',
|
|
76
|
+
text: formatEditOutput(value.path, args.replace_all ?? false),
|
|
77
|
+
}],
|
|
78
|
+
presentationMeta: (args, value) => ({
|
|
79
|
+
diffs: computeHunkDiffs(args.file_path, value.before, value.after)
|
|
80
|
+
.map(({ path, oldText, newText }) => ({ path, oldText, newText })),
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
async execute(args, exec) {
|
|
84
|
+
const input = parseEditArgs(args);
|
|
85
|
+
// Resolve the per-call sandbox policy (approved mode > session override
|
|
86
|
+
// > backend default, plus the session cwd root) BEFORE anything executes.
|
|
87
|
+
const sandboxPolicy = await sandbox.resolvePolicy('edit', args, exec);
|
|
88
|
+
const target = await ctx.fs.resolve(input.filePath, sessionResolveOptions(exec, input.filePath, sandboxPolicy?.workspaceRoot));
|
|
89
|
+
// Single-slot decision: the policy plugin returns { version: vObserved } or
|
|
90
|
+
// throws FS_NOT_OBSERVED; the bare default is undefined (unconditional edit).
|
|
91
|
+
// No stat — the bare default never manufactures a version basis. The intent
|
|
92
|
+
// slot itself can throw FS_NOT_OBSERVED for an unread target, so it sits
|
|
93
|
+
// inside the try: both that refusal and the provider's guarded-mutation
|
|
94
|
+
// failure get the model-facing remedy below.
|
|
95
|
+
let outcome;
|
|
96
|
+
try {
|
|
97
|
+
const intent = await ctx.waterfall('fs/edit-intent', target, exec, () => undefined);
|
|
98
|
+
outcome = await ctx.fs.editText(target, { oldString: input.oldString, newString: input.newString, replaceAll: input.replaceAll }, intent, exec.signal, sandboxPolicy);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
// A sandbox denial becomes the shared [sandbox: …] marker (the model
|
|
102
|
+
// recognizes it from bash); stale/not-observed failures gain their
|
|
103
|
+
// model-facing remedy; anything else passes through.
|
|
104
|
+
throw remediateFsError(sandbox.mapError(error, sandboxPolicy));
|
|
105
|
+
}
|
|
106
|
+
// Record the present observation (a no-op when no policy plugin listens).
|
|
107
|
+
ctx.emit('fs/observed', target, { kind: 'present', version: outcome.version }, exec);
|
|
108
|
+
return {
|
|
109
|
+
path: target.displayPath,
|
|
110
|
+
before: outcome.before,
|
|
111
|
+
after: outcome.after,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
// Pure display: a diff card of the literal replacement (old_string → new_string), derived
|
|
115
|
+
// from the call args. `oldText: old_string || null` matches claude-agent-acp's Edit arm;
|
|
116
|
+
// new_string is a required arg here, so it maps straight to newText.
|
|
117
|
+
presentCall(args) {
|
|
118
|
+
return {
|
|
119
|
+
card: 'diff',
|
|
120
|
+
title: `Edit ${args.file_path}`,
|
|
121
|
+
diffs: [{ path: args.file_path, oldText: args.old_string || null, newText: args.new_string }],
|
|
122
|
+
locations: [{ path: args.file_path }],
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
// Applied metadata replaces the call-time snippet; errors or malformed replay metadata use
|
|
126
|
+
// the generic result rendering.
|
|
127
|
+
presentResult(args, result) {
|
|
128
|
+
if (result.isError)
|
|
129
|
+
return undefined;
|
|
130
|
+
const diffs = diffsFromMeta(result.meta);
|
|
131
|
+
if (diffs === undefined)
|
|
132
|
+
return undefined;
|
|
133
|
+
return { card: 'diff', title: `Edit ${args.file_path}`, diffs };
|
|
134
|
+
},
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=edit.js.map
|
package/dist/edit.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"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"}
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing remediation for guarded-mutation failures. The provider's
|
|
3
|
+
* `FS_STALE_VERSION` and `FS_NOT_OBSERVED` messages state the condition but
|
|
4
|
+
* not the only correct recovery (re-read / read the file), so this package
|
|
5
|
+
* appends the remedy at the model boundary; provider messages stay
|
|
6
|
+
* machine-oriented and unchanged.
|
|
7
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls/src/error
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Append the correct recovery instruction to a guarded-mutation failure's
|
|
11
|
+
* message. `FS_STALE_VERSION` (the file changed since this session's last
|
|
12
|
+
* observation, including a missing target) recovers only by re-reading;
|
|
13
|
+
* `FS_NOT_OBSERVED` (no prior read by this session) by reading. The `FsError`
|
|
14
|
+
* code is preserved so retry/permission/UI layers keep routing on it, and the
|
|
15
|
+
* original error chains as `cause`. Anything else passes through untouched.
|
|
16
|
+
* @param error - the caught value from a write/edit execution.
|
|
17
|
+
* @returns a remediated `FsError` for the two guarded-mutation codes, else the original value.
|
|
18
|
+
*/
|
|
19
|
+
export declare function remediateFsError(error: unknown): unknown;
|
|
20
|
+
//# sourceMappingURL=error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKxD"}
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing remediation for guarded-mutation failures. The provider's
|
|
3
|
+
* `FS_STALE_VERSION` and `FS_NOT_OBSERVED` messages state the condition but
|
|
4
|
+
* not the only correct recovery (re-read / read the file), so this package
|
|
5
|
+
* appends the remedy at the model boundary; provider messages stay
|
|
6
|
+
* machine-oriented and unchanged.
|
|
7
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls/src/error
|
|
8
|
+
*/
|
|
9
|
+
import { FsError } from '@deepseek-ai/dsh-fs';
|
|
10
|
+
/** The remedy appended to each remediable failure code's message. */
|
|
11
|
+
const REMEDIES = {
|
|
12
|
+
FS_STALE_VERSION: 're-read the file, then retry',
|
|
13
|
+
FS_NOT_OBSERVED: 'read the file, then retry',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Append the correct recovery instruction to a guarded-mutation failure's
|
|
17
|
+
* message. `FS_STALE_VERSION` (the file changed since this session's last
|
|
18
|
+
* observation, including a missing target) recovers only by re-reading;
|
|
19
|
+
* `FS_NOT_OBSERVED` (no prior read by this session) by reading. The `FsError`
|
|
20
|
+
* code is preserved so retry/permission/UI layers keep routing on it, and the
|
|
21
|
+
* original error chains as `cause`. Anything else passes through untouched.
|
|
22
|
+
* @param error - the caught value from a write/edit execution.
|
|
23
|
+
* @returns a remediated `FsError` for the two guarded-mutation codes, else the original value.
|
|
24
|
+
*/
|
|
25
|
+
export function remediateFsError(error) {
|
|
26
|
+
if (!(error instanceof FsError))
|
|
27
|
+
return error;
|
|
28
|
+
const remedy = REMEDIES[error.code];
|
|
29
|
+
if (!remedy)
|
|
30
|
+
return error;
|
|
31
|
+
return new FsError(`${error.message} — ${remedy}`, error.code, { cause: error });
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG7C,qEAAqE;AACrE,MAAM,QAAQ,GAAyC;IACrD,gBAAgB,EAAE,8BAA8B;IAChD,eAAe,EAAE,2BAA2B;CAC7C,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,CAAC,CAAC,KAAK,YAAY,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACzB,OAAO,IAAI,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,MAAM,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;AAClF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing read, read_image, write, and edit tools over `ctx.fs`. This package owns schemas, validation,
|
|
3
|
+
* read windows, formatting, and observation events, never a concrete provider. An optional
|
|
4
|
+
* event policy supplies mutation guards; without one the tools use unconditional provider calls.
|
|
5
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import z from '@deepseek-ai/schemastery';
|
|
9
|
+
/** Cordis plugin name used by loader diagnostics. */
|
|
10
|
+
export declare const name = "tool-fs";
|
|
11
|
+
/** Services required by the filesystem tool suite. */
|
|
12
|
+
export declare const inject: string[];
|
|
13
|
+
/** Plugin config (all optional — `Config` supplies the defaults). */
|
|
14
|
+
export interface Config {
|
|
15
|
+
/** Default and maximum number of lines returned by one `read` call. */
|
|
16
|
+
readLimit?: number;
|
|
17
|
+
/** Maximum characters returned for a single line before truncation. */
|
|
18
|
+
readMaxLineLength?: number;
|
|
19
|
+
/** Maximum bytes returned for the selected lines of one `read` call. */
|
|
20
|
+
readMaxBytes?: number;
|
|
21
|
+
/** Files at or above this size stream instead of loading whole into memory. */
|
|
22
|
+
readStreamMinSize?: number;
|
|
23
|
+
/** Register the literal `edit` tool. Set false when a rich editor replaces it; default true. */
|
|
24
|
+
enableEdit?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare const Config: z<Config>;
|
|
27
|
+
/** Register the full `read`/`write`/`edit` filesystem tool suite, plus `read_image` while `attachments` is mounted. */
|
|
28
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AASxC,qDAAqD;AACrD,eAAO,MAAM,IAAI,YAAY,CAAA;AAE7B,sDAAsD;AACtD,eAAO,MAAM,MAAM,UAAkC,CAAA;AAErD,qEAAqE;AACrE,MAAM,WAAW,MAAM;IACrB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,gGAAgG;IAChG,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAM3B,CAAA;AAYF,uHAAuH;AACvH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAyBxD"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing read, read_image, write, and edit tools over `ctx.fs`. This package owns schemas, validation,
|
|
3
|
+
* read windows, formatting, and observation events, never a concrete provider. An optional
|
|
4
|
+
* event policy supplies mutation guards; without one the tools use unconditional provider calls.
|
|
5
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls
|
|
6
|
+
*/
|
|
7
|
+
import z from '@deepseek-ai/schemastery';
|
|
8
|
+
import { applyReadTool, READ_LIMIT, STREAM_MIN_SIZE } from "./read.js";
|
|
9
|
+
import { applyWriteTool } from "./write.js";
|
|
10
|
+
import { applyEditTool } from "./edit.js";
|
|
11
|
+
import { applyReadImageTool } from "./read-image.js";
|
|
12
|
+
import { READ_MAX_BYTES, READ_MAX_LINE_LENGTH } from "./read-render.js";
|
|
13
|
+
import { FsSandboxController } from "./sandbox.js";
|
|
14
|
+
/** Cordis plugin name used by loader diagnostics. */
|
|
15
|
+
export const name = 'tool-fs';
|
|
16
|
+
/** Services required by the filesystem tool suite. */
|
|
17
|
+
export const inject = ['tools', 'fs', 'systemPrompt'];
|
|
18
|
+
export const Config = z.object({
|
|
19
|
+
readLimit: z.number().default(READ_LIMIT),
|
|
20
|
+
readMaxLineLength: z.number().default(READ_MAX_LINE_LENGTH),
|
|
21
|
+
readMaxBytes: z.number().default(READ_MAX_BYTES),
|
|
22
|
+
readStreamMinSize: z.number().default(STREAM_MIN_SIZE),
|
|
23
|
+
enableEdit: z.boolean().default(true),
|
|
24
|
+
});
|
|
25
|
+
/** Every read cap counts lines/chars/bytes — a positive integer, or windowing arithmetic misbehaves silently. */
|
|
26
|
+
function assertPositiveInteger(name, value) {
|
|
27
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
28
|
+
throw new Error(`tool-fs: ${name} must be a positive integer`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Register the full `read`/`write`/`edit` filesystem tool suite, plus `read_image` while `attachments` is mounted. */
|
|
32
|
+
export function apply(ctx, config) {
|
|
33
|
+
// schemastery (Config) has already filled every defaulted field.
|
|
34
|
+
const resolved = config;
|
|
35
|
+
assertPositiveInteger('readLimit', resolved.readLimit);
|
|
36
|
+
assertPositiveInteger('readMaxLineLength', resolved.readMaxLineLength);
|
|
37
|
+
assertPositiveInteger('readMaxBytes', resolved.readMaxBytes);
|
|
38
|
+
assertPositiveInteger('readStreamMinSize', resolved.readStreamMinSize);
|
|
39
|
+
applyReadTool(ctx, {
|
|
40
|
+
limit: resolved.readLimit,
|
|
41
|
+
maxLineLength: resolved.readMaxLineLength,
|
|
42
|
+
maxBytes: resolved.readMaxBytes,
|
|
43
|
+
streamMinSize: resolved.readStreamMinSize,
|
|
44
|
+
});
|
|
45
|
+
// read_image is composition-conditional: without a mounted attachment store
|
|
46
|
+
// the deployment cannot durably commit image bytes, so the tool never
|
|
47
|
+
// registers; the execute body keeps a defensive re-check for direct callers.
|
|
48
|
+
ctx.inject(['attachments'], (imageCtx) => {
|
|
49
|
+
applyReadImageTool(imageCtx);
|
|
50
|
+
});
|
|
51
|
+
// One escalation API shared by both mutating tools: advertisement gating,
|
|
52
|
+
// per-call policy resolution, and denial-marker mapping, all keyed off whether
|
|
53
|
+
// the mounted ctx.fs confines (ctx.fs.sandboxMode).
|
|
54
|
+
const sandbox = new FsSandboxController(ctx);
|
|
55
|
+
applyWriteTool(ctx, sandbox);
|
|
56
|
+
if (resolved.enableEdit)
|
|
57
|
+
applyEditTool(ctx, sandbox);
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAElD,qDAAqD;AACrD,MAAM,CAAC,MAAM,IAAI,GAAG,SAAS,CAAA;AAE7B,sDAAsD;AACtD,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;AAgBrD,MAAM,CAAC,MAAM,MAAM,GAAc,CAAC,CAAC,MAAM,CAAC;IACxC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC;IAC3D,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC;IAChD,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC;IACtD,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACtC,CAAC,CAAA;AAKF,iHAAiH;AACjH,SAAS,qBAAqB,CAAC,IAAY,EAAE,KAAa;IACxD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,6BAA6B,CAAC,CAAA;IAChE,CAAC;AACH,CAAC;AAED,uHAAuH;AACvH,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAc;IAChD,iEAAiE;IACjE,MAAM,QAAQ,GAAG,MAAwB,CAAA;IACzC,qBAAqB,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;IACtD,qBAAqB,CAAC,mBAAmB,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAA;IACtE,qBAAqB,CAAC,cAAc,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAA;IAC5D,qBAAqB,CAAC,mBAAmB,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAA;IACtE,aAAa,CAAC,GAAG,EAAE;QACjB,KAAK,EAAE,QAAQ,CAAC,SAAS;QACzB,aAAa,EAAE,QAAQ,CAAC,iBAAiB;QACzC,QAAQ,EAAE,QAAQ,CAAC,YAAY;QAC/B,aAAa,EAAE,QAAQ,CAAC,iBAAiB;KAC1C,CAAC,CAAA;IACF,4EAA4E;IAC5E,sEAAsE;IACtE,6EAA6E;IAC7E,GAAG,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE;QACvC,kBAAkB,CAAC,QAAQ,CAAC,CAAA;IAC9B,CAAC,CAAC,CAAA;IACF,0EAA0E;IAC1E,+EAA+E;IAC/E,oDAAoD;IACpD,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,GAAG,CAAC,CAAA;IAC5C,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAC5B,IAAI,QAAQ,CAAC,UAAU;QAAE,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;AACtD,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read tool's internal-URL routing: when `ctx.internalUrls` is mounted and
|
|
3
|
+
* the requested path is a handled `scheme://` URL (or a `<path>:conflicts`
|
|
4
|
+
* selector), the read resolves through the registry instead of the filesystem,
|
|
5
|
+
* renders the virtual content as a line-numbered window, and — for regular
|
|
6
|
+
* filesystem reads — scans the surfaced lines for git conflict blocks,
|
|
7
|
+
* registering them with the session history and appending a resolution notice.
|
|
8
|
+
* @module @hy-sde-org/dsh-tool-fs-internal-urls/internal-routing
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { FsTarget } from '@deepseek-ai/dsh-fs';
|
|
12
|
+
import type { InternalUrlsService } from '@hy-sde-org/dsh-internal-urls';
|
|
13
|
+
import type { ToolExecution } from '@deepseek-ai/dsh-tools';
|
|
14
|
+
import type { FileTextLine } from './read-render.ts';
|
|
15
|
+
/** The subset of read caps the routing needs to build a bounded window. */
|
|
16
|
+
export interface InternalReadCaps {
|
|
17
|
+
maxLineLength: number;
|
|
18
|
+
maxBytes: number;
|
|
19
|
+
}
|
|
20
|
+
/** Tool-owned window request (validated read args). */
|
|
21
|
+
export interface ReadRequest {
|
|
22
|
+
filePath: string;
|
|
23
|
+
offset: number;
|
|
24
|
+
limit: number;
|
|
25
|
+
}
|
|
26
|
+
/** A fully-formed read outcome, virtual or filesystem-backed. */
|
|
27
|
+
export interface InternalReadOutcome {
|
|
28
|
+
/** Model-facing path (the internal URL for virtual reads). */
|
|
29
|
+
path: string;
|
|
30
|
+
offset: number;
|
|
31
|
+
lines: FileTextLine[];
|
|
32
|
+
totalLines: number;
|
|
33
|
+
/** Optional conflict-resolution notice, rendered after the file body. */
|
|
34
|
+
notice?: string;
|
|
35
|
+
}
|
|
36
|
+
/** The calling agent's session id, used to key session-scoped handler state. */
|
|
37
|
+
export declare function sessionKeyOf(exec: ToolExecution): string | undefined;
|
|
38
|
+
/** Build a {@link ResolveContext} for the calling tool execution. */
|
|
39
|
+
export declare function resolveContextOf(exec: ToolExecution, path: string): {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
signal: AbortSignal;
|
|
42
|
+
sessionKey?: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a scheme URL (or `<path>:conflicts` selector) into a virtual window.
|
|
46
|
+
* Returns `undefined` when the path is not an internal URL, so the caller
|
|
47
|
+
* falls through to the filesystem path untouched.
|
|
48
|
+
*/
|
|
49
|
+
export declare function tryReadInternal(ctx: Context, iu: InternalUrlsService, exec: ToolExecution, request: ReadRequest, caps: InternalReadCaps): Promise<InternalReadOutcome | undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* Scan a completed filesystem read's window for conflict blocks, register them
|
|
52
|
+
* with the calling session's history (id reuse by path+start line), and render
|
|
53
|
+
* the resolution notice. Returns `undefined` when nothing to report or no
|
|
54
|
+
* session key / registry are available.
|
|
55
|
+
*/
|
|
56
|
+
export declare function conflictNoticeForRead(ctx: Context, iu: InternalUrlsService, exec: ToolExecution, target: FsTarget, outcome: {
|
|
57
|
+
path: string;
|
|
58
|
+
offset: number;
|
|
59
|
+
lines: FileTextLine[];
|
|
60
|
+
}): string | undefined;
|
|
61
|
+
//# sourceMappingURL=internal-routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-routing.d.ts","sourceRoot":"","sources":["../src/internal-routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAEnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAA;AAQxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAGpD,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAEpE;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAQ9H;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,mBAAmB,EACvB,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAmB1C;AAqCD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,mBAAmB,EACvB,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,GAC/D,MAAM,GAAG,SAAS,CAapB"}
|