@qlzt/dsh-tool-fs 0.0.0-stage → 0.2.1-alpha.3
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 +21 -0
- package/README.i18n.yaml +142 -0
- package/README.md +259 -2
- package/README.zh.md +260 -0
- package/lib/index.js +1225 -0
- package/lib/types/diff.d.ts +47 -0
- package/lib/types/edit.d.ts +43 -0
- package/lib/types/error.d.ts +19 -0
- package/lib/types/index.d.ts +27 -0
- package/lib/types/mutation-result.d.ts +18 -0
- package/lib/types/read-image.d.ts +81 -0
- package/lib/types/read-render.d.ts +111 -0
- package/lib/types/read-target.d.ts +19 -0
- package/lib/types/read.d.ts +49 -0
- package/lib/types/sandbox.d.ts +82 -0
- package/lib/types/session-cwd.d.ts +18 -0
- package/lib/types/write.d.ts +36 -0
- package/package.json +69 -4
|
@@ -0,0 +1,47 @@
|
|
|
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 @qlzt/dsh-tool-fs/src/diff
|
|
5
|
+
*/
|
|
6
|
+
import type { FileDiff } from '@qlzt/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, and for `write` whether the call created or updated
|
|
12
|
+
* the file, which tells an empty hunk list of a create from one of an
|
|
13
|
+
* unchanged overwrite. Attached opaquely (as `unknown`) on the tool result and
|
|
14
|
+
* persisted with the session log — it must be JSON-serializable (the session
|
|
15
|
+
* validates this at `append`), so `presentResult` reproduces the diff card on
|
|
16
|
+
* replay. The producing tool owns and narrows this opaque shape.
|
|
17
|
+
*/
|
|
18
|
+
export type FsDiffMeta = {
|
|
19
|
+
diffs: FileDiff[];
|
|
20
|
+
operation?: 'create' | 'update';
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Compute one {@link FileDiff} per hunk between `before` and `after`, each carrying the
|
|
24
|
+
* applied change plus {@link DIFF_CONTEXT} context lines. Pure insertions use `oldText: null`,
|
|
25
|
+
* patch-only no-newline markers are omitted, and scattered replacements remain separate hunks.
|
|
26
|
+
*
|
|
27
|
+
* @param path - the path stamped on every produced diff (the model-facing `file_path`; the
|
|
28
|
+
* bridge relativizes it).
|
|
29
|
+
* @param before - the file text before the change (the backend's LF-normalized diff basis).
|
|
30
|
+
* @param after - the file text after the change, on the same basis.
|
|
31
|
+
* @returns one diff per applied hunk, in file order; empty when the texts are identical.
|
|
32
|
+
*/
|
|
33
|
+
export declare function computeHunkDiffs(path: string, before: string, after: string): FileDiff[];
|
|
34
|
+
/**
|
|
35
|
+
* Narrow opaque live or replayed result metadata to non-empty file diffs. Malformed metadata
|
|
36
|
+
* returns `undefined` so presentation can fall back instead of throwing during replay.
|
|
37
|
+
* @param meta - result metadata.
|
|
38
|
+
* @returns validated hunks, or `undefined` for absent or malformed data.
|
|
39
|
+
*/
|
|
40
|
+
export declare function diffsFromMeta(meta: unknown): FileDiff[] | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Read the operation's file path even when the applied diff is empty.
|
|
43
|
+
* @param meta - opaque persisted result metadata.
|
|
44
|
+
* @returns the recorded path, or undefined for old or malformed metadata.
|
|
45
|
+
*/
|
|
46
|
+
export declare function pathFromMeta(meta: unknown): string | undefined;
|
|
47
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -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 @qlzt/dsh-tool-fs/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 scope-aware 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,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing diagnostics for guarded-mutation failures. Providers and
|
|
3
|
+
* policies retain operation-specific causes, while this package owns the
|
|
4
|
+
* stable message shown to the model.
|
|
5
|
+
* @module @qlzt/dsh-tool-fs/src/error
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Render the stable model-facing diagnostic for a guarded-mutation failure.
|
|
9
|
+
* `FS_STALE_VERSION` keeps the provider's reason and appends its re-read
|
|
10
|
+
* remedy. `FS_NOT_OBSERVED` replaces operation-specific policy/provider text
|
|
11
|
+
* with one path-aware reason and read remedy. The original error remains the
|
|
12
|
+
* cause, and both diagnostics preserve its code for machine routing. Anything
|
|
13
|
+
* else passes through untouched.
|
|
14
|
+
* @param error - the caught value from a write/edit execution.
|
|
15
|
+
* @param displayPath - the resolved target path shown to the model.
|
|
16
|
+
* @returns a remediated `FsError` for the two guarded-mutation codes, else the original value.
|
|
17
|
+
*/
|
|
18
|
+
export declare function remediateFsError(error: unknown, displayPath: string): unknown;
|
|
19
|
+
//# sourceMappingURL=error.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
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 @qlzt/dsh-tool-fs
|
|
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
|
+
}
|
|
24
|
+
export declare const Config: z<Config>;
|
|
25
|
+
/** Register the full `read`/`write`/`edit` filesystem tool suite, plus `read_image` while `attachments` is mounted. */
|
|
26
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Shared observation and result fields for successful file mutations. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { FsTarget, FsVersion } from '@qlzt/dsh-fs';
|
|
4
|
+
import type { ToolExecution } from '@qlzt/dsh-tools';
|
|
5
|
+
/**
|
|
6
|
+
* Record the new file version and expose the mutation result with its canonical path.
|
|
7
|
+
* @param ctx - filesystem and observation event owner.
|
|
8
|
+
* @param target - resolved file that was mutated.
|
|
9
|
+
* @param outcome - successful provider result; its version stays in the observation event.
|
|
10
|
+
* @param exec - calling tool execution.
|
|
11
|
+
* @returns the provider's result fields without the version, plus the canonical absolute path.
|
|
12
|
+
*/
|
|
13
|
+
export declare function mutationResult<T extends {
|
|
14
|
+
version: FsVersion;
|
|
15
|
+
}>(ctx: Context, target: FsTarget, outcome: T, exec: ToolExecution): Omit<T, 'version'> & {
|
|
16
|
+
path: string;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=mutation-result.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The model-facing `read_image` tool commits a PNG/JPEG/WebP/GIF file. A path
|
|
3
|
+
* without a file extension is identified from its file signature, while the
|
|
4
|
+
* attachment service's full decode stays authoritative. The mounted `ctx.fs`
|
|
5
|
+
* backend owns path resolution and read access; names only declare media type.
|
|
6
|
+
*
|
|
7
|
+
* The route gate is deliberately stricter than the host upload preflight. An
|
|
8
|
+
* image-reading tool is useful only when the exact calling route can inspect
|
|
9
|
+
* its result, so unknown capability refuses instead of relying on an adapter
|
|
10
|
+
* failure after filesystem and attachment work.
|
|
11
|
+
* @module @qlzt/dsh-tool-fs/src/read-image
|
|
12
|
+
*/
|
|
13
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import type { ImageAttachmentRef, ImageMediaType } from '@qlzt/dsh-attachment';
|
|
15
|
+
import type { ToolExecution } from '@qlzt/dsh-tools';
|
|
16
|
+
/**
|
|
17
|
+
* Identify the media type declared by a supported image file signature.
|
|
18
|
+
* @param data - file bytes read through the current filesystem backend.
|
|
19
|
+
* @returns the detected supported media type, or undefined for other bytes.
|
|
20
|
+
*/
|
|
21
|
+
export declare function sniffImageMediaType(data: Uint8Array): ImageMediaType | undefined;
|
|
22
|
+
/** The structured outcome declared by the `read_image` output schema. */
|
|
23
|
+
export interface ImageReadValue {
|
|
24
|
+
/** Canonical absolute path in the filesystem provider's execution world. */
|
|
25
|
+
path: string;
|
|
26
|
+
image: {
|
|
27
|
+
attachmentId: string;
|
|
28
|
+
mediaType: ImageMediaType;
|
|
29
|
+
bytes: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
name?: string;
|
|
33
|
+
/** Orientation-applied file dimensions before normalization; present only when storage reduced it. */
|
|
34
|
+
originalDimensions?: {
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Map a model-supplied path to its declared image media type by extension.
|
|
42
|
+
* @param filePath - the raw `file_path` argument (not yet resolved).
|
|
43
|
+
* @returns the declared media type, or undefined when the path does not claim an image.
|
|
44
|
+
*/
|
|
45
|
+
export declare function imageMediaTypeForPath(filePath: string): ImageMediaType | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Enforce the strict image-capability gate for the calling route. Resolves the
|
|
48
|
+
* session's latest routed provider/model (request header config, then agent
|
|
49
|
+
* options) and requires the exact resolved route to declare `image` input explicitly.
|
|
50
|
+
* @param ctx - the plugin context used to resolve the optional `llm` service.
|
|
51
|
+
* @param exec - the tool-execution context supplying the calling agent.
|
|
52
|
+
* @param requestedPath - the raw, not-yet-resolved path rendered in refusal messages.
|
|
53
|
+
*/
|
|
54
|
+
export declare function assertImageCapableRoute(ctx: Context, exec: ToolExecution, requestedPath: string): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Re-brand a structured image outcome into the durable attachment reference an
|
|
57
|
+
* `ImageBlock` carries.
|
|
58
|
+
* @param image - the image metadata from the output schema.
|
|
59
|
+
* @returns the branded attachment reference.
|
|
60
|
+
*/
|
|
61
|
+
export declare function imageRefFromValue(image: ImageReadValue['image']): ImageAttachmentRef;
|
|
62
|
+
/**
|
|
63
|
+
* Format an image read as the model-facing envelope beside its image block.
|
|
64
|
+
* A downscaled read names the on-disk dimensions and the multiplier that maps
|
|
65
|
+
* coordinates measured on the attached image back onto the original file.
|
|
66
|
+
* @param displayPath - the backend-resolved path rendered in the envelope's `<path>` element.
|
|
67
|
+
* @param image - the image metadata to summarize.
|
|
68
|
+
* @returns the model-facing envelope; the image itself rides the adjacent image block.
|
|
69
|
+
*/
|
|
70
|
+
export declare function formatImageReadOutput(displayPath: string, image: ImageReadValue['image']): string;
|
|
71
|
+
/**
|
|
72
|
+
* Register the `read_image` tool into the given context. The composing plugin
|
|
73
|
+
* owns the attachments gate: `src/index.ts` calls this inside
|
|
74
|
+
* `ctx.inject(['attachments'], …)` so the tool exists only while a durable
|
|
75
|
+
* store is mounted. Execution still re-checks `ctx.get('attachments')` for
|
|
76
|
+
* direct callers and gates on the calling route's declared image input.
|
|
77
|
+
* @param ctx - the registration scope; execution uses its `fs` service plus
|
|
78
|
+
* the optional `attachments`/`llm` services.
|
|
79
|
+
*/
|
|
80
|
+
export declare function applyReadImageTool(ctx: Context): void;
|
|
81
|
+
//# sourceMappingURL=read-image.d.ts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure read presentation: turn provider-decoded text into a bounded, line-numbered window and
|
|
3
|
+
* model-facing envelope. Chunk scanning caps the current line, so even one newline-free giant
|
|
4
|
+
* line cannot grow memory without bound.
|
|
5
|
+
* @module @qlzt/dsh-tool-fs/read-render
|
|
6
|
+
*/
|
|
7
|
+
/** Default maximum characters returned for a single line (the `readMaxLineLength` config). */
|
|
8
|
+
export declare const READ_MAX_LINE_LENGTH = 2000;
|
|
9
|
+
/** Default maximum bytes returned for selected file lines (the `readMaxBytes` config). */
|
|
10
|
+
export declare const READ_MAX_BYTES: number;
|
|
11
|
+
/** Resolved read window. The consumer applies its defaults/caps before calling. */
|
|
12
|
+
export interface ReadWindow {
|
|
13
|
+
/** 1-based first line to return. */
|
|
14
|
+
offset: number;
|
|
15
|
+
/** Maximum number of lines to return. */
|
|
16
|
+
limit: number;
|
|
17
|
+
/** Maximum characters returned for a single line; overflow is truncated with a suffix. */
|
|
18
|
+
maxLineLength: number;
|
|
19
|
+
/** Maximum bytes of selected output; overflow stops the scan and marks `truncatedByBytes`. */
|
|
20
|
+
maxBytes: number;
|
|
21
|
+
}
|
|
22
|
+
/** One line returned from a text file. */
|
|
23
|
+
export interface FileTextLine {
|
|
24
|
+
/** 1-based line number in the file. */
|
|
25
|
+
number: number;
|
|
26
|
+
/** Line text without its trailing newline. */
|
|
27
|
+
text: string;
|
|
28
|
+
}
|
|
29
|
+
/** The windowed result {@link buildWindow} produces from a file's decoded text. */
|
|
30
|
+
export interface WindowResult {
|
|
31
|
+
/** Returned lines, already numbered. */
|
|
32
|
+
lines: FileTextLine[];
|
|
33
|
+
/** Exact total line count in the file. */
|
|
34
|
+
totalLines: number;
|
|
35
|
+
/** Whether selected output hit the byte cap. */
|
|
36
|
+
truncatedByBytes: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** Outcome of a bounded text read — what {@link formatReadOutput} renders. */
|
|
39
|
+
export interface FileReadOutcome {
|
|
40
|
+
/** 1-based first line requested. */
|
|
41
|
+
offset: number;
|
|
42
|
+
/** Returned lines, already numbered. */
|
|
43
|
+
lines: FileTextLine[];
|
|
44
|
+
/** Exact total line count in the file. */
|
|
45
|
+
totalLines: number;
|
|
46
|
+
/** Whether selected output hit the byte cap. */
|
|
47
|
+
truncatedByBytes?: true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build one window from streamed or whole-file chunks, enforcing line and byte caps while still
|
|
51
|
+
* scanning to an exact total line count, and throwing `FS_NOT_FOUND` when the requested offset is
|
|
52
|
+
* past EOF.
|
|
53
|
+
* @param chunks - decoded text chunks in file order; chunk boundaries carry no meaning.
|
|
54
|
+
* @param request - the resolved window; the caller has already applied its defaults and caps.
|
|
55
|
+
* @param displayPath - the caller-facing path used in the offset-out-of-range error.
|
|
56
|
+
* @returns the numbered window lines, the total line count seen, and the byte-cap truncation flag.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildWindow(chunks: AsyncIterable<string> | Iterable<string>, request: ReadWindow, displayPath: string): Promise<WindowResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Format a read outcome as one OpenCode-style line-numbered text block body.
|
|
61
|
+
* @param displayPath - the backend-resolved path rendered in the envelope's `<path>` element.
|
|
62
|
+
* @param outcome - the windowed read to render.
|
|
63
|
+
* @returns the model-facing envelope: numbered lines plus a continuation or end-of-file footer.
|
|
64
|
+
*/
|
|
65
|
+
export declare function formatReadOutput(displayPath: string, outcome: FileReadOutcome): string;
|
|
66
|
+
/**
|
|
67
|
+
* Derive the persisted `lang` hint from a read path's file extension. The shared
|
|
68
|
+
* table in `@qlzt/dsh-util-code-language` owns the recognized suffixes and
|
|
69
|
+
* the path rules (both separators, a leading dot as the extension separator, and
|
|
70
|
+
* prototype-key safety); `readLangHintForPath` projects the read card's short ids
|
|
71
|
+
* over it, so a suffix whose value a recorded session already holds keeps it
|
|
72
|
+
* byte-identical while every other suffix uses its language's short name.
|
|
73
|
+
* @param path - the model-facing path the read reported.
|
|
74
|
+
* @returns the persisted language hint, or `undefined` when the extension maps to none.
|
|
75
|
+
*/
|
|
76
|
+
export { readLangHintForPath as langFromPath } from '@qlzt/dsh-util-code-language';
|
|
77
|
+
/**
|
|
78
|
+
* The `read` tool's private `tool/result` `meta` payload: the structured
|
|
79
|
+
* line-numbered window a capable UI renders as a code view. Attached opaquely (as
|
|
80
|
+
* `unknown`) on the tool result and persisted with the session log — it must be
|
|
81
|
+
* JSON-serializable (the session validates this at `append`), so `presentResult`
|
|
82
|
+
* reproduces the read card on replay when the raw structured output is no longer
|
|
83
|
+
* on the wire. The producing tool owns and narrows this opaque shape.
|
|
84
|
+
*/
|
|
85
|
+
export interface FsReadMeta {
|
|
86
|
+
/** The read file's model-facing path. */
|
|
87
|
+
path: string;
|
|
88
|
+
/** The 1-based first line the window requested, kept even when `lines` is empty. */
|
|
89
|
+
offset: number;
|
|
90
|
+
/** The returned window's lines, each keeping its file line number. */
|
|
91
|
+
lines: FileTextLine[];
|
|
92
|
+
/** Exact total line count in the file. */
|
|
93
|
+
totalLines: number;
|
|
94
|
+
/** Syntax-highlighting language hint from the extension, or omitted for plain text. */
|
|
95
|
+
lang?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Narrow opaque live or replayed result metadata to a structured read window.
|
|
99
|
+
* Malformed metadata returns `undefined` so presentation can fall back to the
|
|
100
|
+
* generic text card instead of throwing during replay. Beyond shape, the
|
|
101
|
+
* semantic contract of a read window is enforced against replayed JSON that is
|
|
102
|
+
* well-typed but out of range: `offset` must be a 1-based integer, `totalLines`
|
|
103
|
+
* must be a non-negative integer, each line number must be a 1-based integer no
|
|
104
|
+
* less than `offset`, the line numbers must strictly increase, and no line number
|
|
105
|
+
* may exceed `totalLines`. Any violation declines to the generic fallback rather
|
|
106
|
+
* than emitting a card that misnumbers or overcounts.
|
|
107
|
+
* @param meta - result metadata.
|
|
108
|
+
* @returns the validated read window, or `undefined` for absent, malformed, or semantically invalid data.
|
|
109
|
+
*/
|
|
110
|
+
export declare function readMetaFromMeta(meta: unknown): FsReadMeta | undefined;
|
|
111
|
+
//# sourceMappingURL=read-render.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared path resolution and regular-file validation for model-facing read tools.
|
|
3
|
+
* @module @qlzt/dsh-tool-fs/src/read-target
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
import type { FsInfo, FsTarget } from '@qlzt/dsh-fs';
|
|
7
|
+
import type { ToolExecution } from '@qlzt/dsh-tools';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a model-supplied path, observe absence, and require a regular file.
|
|
10
|
+
* @param ctx - the plugin context providing filesystem resolution and observation events.
|
|
11
|
+
* @param exec - the current tool execution, including the owning Agent and cancellation.
|
|
12
|
+
* @param requestedPath - the raw path supplied to the tool.
|
|
13
|
+
* @returns the resolved target and its single stat result.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveRegularReadTarget(ctx: Context, exec: ToolExecution, requestedPath: string): Promise<{
|
|
16
|
+
target: FsTarget;
|
|
17
|
+
info: FsInfo;
|
|
18
|
+
}>;
|
|
19
|
+
//# sourceMappingURL=read-target.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing UTF-8 read. It performs one provider stat for type, routing, and observed version,
|
|
3
|
+
* streams large or size-unknown files, renders a bounded window, then emits the observation.
|
|
4
|
+
* @module @qlzt/dsh-tool-fs/src/read
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
/** Default and maximum number of lines returned by one `read` call (the `readLimit` config). */
|
|
8
|
+
export declare const READ_LIMIT = 2000;
|
|
9
|
+
/**
|
|
10
|
+
* Default streaming threshold (the `readStreamMinSize` config): files at or
|
|
11
|
+
* above this size stream; smaller files read whole into memory.
|
|
12
|
+
*/
|
|
13
|
+
export declare const STREAM_MIN_SIZE: number;
|
|
14
|
+
/** Resolved read-tool caps — plugin config after defaulting (see `Config` in index.ts). */
|
|
15
|
+
export interface ReadToolCaps {
|
|
16
|
+
/** Default and maximum number of lines returned by one call. */
|
|
17
|
+
limit: number;
|
|
18
|
+
/** Maximum characters returned for a single line. */
|
|
19
|
+
maxLineLength: number;
|
|
20
|
+
/** Maximum bytes returned for selected file lines. */
|
|
21
|
+
maxBytes: number;
|
|
22
|
+
/** Files at or above this size stream; smaller files read whole into memory. */
|
|
23
|
+
streamMinSize: number;
|
|
24
|
+
}
|
|
25
|
+
/** Validated `read` arguments after defaulting. */
|
|
26
|
+
interface ReadInput {
|
|
27
|
+
filePath: string;
|
|
28
|
+
offset: number;
|
|
29
|
+
limit: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validate value constraints the schema DSL can't express. `maxLimit` is the deployment's line cap.
|
|
33
|
+
* @param args - the schema-validated raw tool arguments; `offset`/`limit` must be positive integers when given.
|
|
34
|
+
* @param maxLimit - the configured line cap: both the default `limit` and the largest one accepted.
|
|
35
|
+
* @returns the validated input with `offset` defaulted to 1 and `limit` to `maxLimit`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseReadArgs(args: {
|
|
38
|
+
file_path: string;
|
|
39
|
+
offset?: number;
|
|
40
|
+
limit?: number;
|
|
41
|
+
}, maxLimit: number): ReadInput;
|
|
42
|
+
/**
|
|
43
|
+
* Register the `read` tool and its scope-aware system-prompt guidance.
|
|
44
|
+
* @param ctx - the plugin context; registrations are effects scoped to it, and execution uses its `fs` service.
|
|
45
|
+
* @param caps - the deployment's resolved read caps (plugin config after defaulting).
|
|
46
|
+
*/
|
|
47
|
+
export declare function applyReadTool(ctx: Context, caps: ReadToolCaps): void;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=read.d.ts.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sandbox-escalation API shared by the `write` and `edit` tools: the
|
|
3
|
+
* per-call policy resolution, the advertised escalation fields, and the denial-marker
|
|
4
|
+
* mapping — all delegating the vocabulary and the fail-closed approval
|
|
5
|
+
* sequence to `@qlzt/dsh-sandbox` (the same pieces `@qlzt/dsh-tool-bash`
|
|
6
|
+
* uses), so bash and fs escalate identically. Built ONCE per plugin from
|
|
7
|
+
* `ctx.fs.sandboxMode` (the capability fact — is a confining backend mounted?)
|
|
8
|
+
* and shared by both mutating tools.
|
|
9
|
+
*
|
|
10
|
+
* @module @qlzt/dsh-tool-fs/sandbox
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import type { ToolExecution } from '@qlzt/dsh-tools';
|
|
14
|
+
import type { SandboxExecutionPolicy, SandboxMode } from '@qlzt/dsh-sandbox';
|
|
15
|
+
/** The two escalation arguments a mutating tool may carry (advertised only under a confining backend). */
|
|
16
|
+
export interface FsEscalationArgs {
|
|
17
|
+
sandbox_permissions?: string;
|
|
18
|
+
justification?: string;
|
|
19
|
+
}
|
|
20
|
+
/** The schema fields for the escalation arguments, spread into a tool's `parameters` when a confining backend is mounted. */
|
|
21
|
+
export interface EscalationSchemaFields {
|
|
22
|
+
sandbox_permissions: {
|
|
23
|
+
type: 'string';
|
|
24
|
+
enum: string[];
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
justification: {
|
|
28
|
+
type: 'string';
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The filesystem escalation API: advertisement gating, per-call policy
|
|
34
|
+
* resolution, the one-approved wider retry, and denial-marker mapping. A pure
|
|
35
|
+
* product of `ctx` at plugin apply time.
|
|
36
|
+
*/
|
|
37
|
+
export declare class FsSandboxController {
|
|
38
|
+
private readonly ctx;
|
|
39
|
+
/** The escalation targets this composition advertises (`[]` when no confining backend is mounted). */
|
|
40
|
+
readonly escalationModes: readonly SandboxMode[];
|
|
41
|
+
/** Shared per-session policy resolver, required by a confining backend. */
|
|
42
|
+
private readonly policy;
|
|
43
|
+
constructor(ctx: Context);
|
|
44
|
+
/**
|
|
45
|
+
* The escalation schema fields for a mutating tool's `parameters`. Call it
|
|
46
|
+
* only under a confining backend (guard on {@link escalationModes}); the
|
|
47
|
+
* enum pins the closed target vocabulary, the strict-wider check happens per
|
|
48
|
+
* call at execution.
|
|
49
|
+
* @returns the two escalation parameter specs.
|
|
50
|
+
*/
|
|
51
|
+
schemaFields(): EscalationSchemaFields;
|
|
52
|
+
/**
|
|
53
|
+
* The policy to stamp onto this mutation: an approved escalation grant (a
|
|
54
|
+
* strictly wider retry resolved through `ctx.approval` before anything
|
|
55
|
+
* executes), else the Session's standing mode. Repeating the standing mode
|
|
56
|
+
* requires no approval. The workspace root is the original Session
|
|
57
|
+
* directory, or the deployment fallback when absent. Validates the escalation argument
|
|
58
|
+
* pairing first.
|
|
59
|
+
* @param toolName - the mutating tool's name, for the approval audit trail.
|
|
60
|
+
* @param args - the call's escalation arguments.
|
|
61
|
+
* @param exec - the tool-execution context (agent, callId, signal).
|
|
62
|
+
* @returns the policy to pass to the mutation, or undefined for an
|
|
63
|
+
* unsandboxed backend.
|
|
64
|
+
*/
|
|
65
|
+
resolvePolicy(toolName: string, args: FsEscalationArgs, exec: ToolExecution): Promise<SandboxExecutionPolicy | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Map a thrown provider error for the model: a `FS_SANDBOX_DENIED` becomes a
|
|
68
|
+
* `FsError` whose text is the shared `[sandbox: …]` denial marker plus the
|
|
69
|
+
* same-turn escalation hint, so a policy denial reads identically to bash's
|
|
70
|
+
* WHILE keeping the structured `FS_SANDBOX_DENIED` code — `ToolRuntime`
|
|
71
|
+
* populates `result.error` only for `HarnessError` instances, so a plain
|
|
72
|
+
* `Error` would strip the code retry/observers key off. Any other error
|
|
73
|
+
* passes through unchanged. A `FS_SANDBOX_DENIED` only arises under a
|
|
74
|
+
* confining backend, which always advertises the escalation fields, so the
|
|
75
|
+
* hint always applies here.
|
|
76
|
+
* @param error - the error thrown by the mutation.
|
|
77
|
+
* @param policy - the policy stamped onto the call (names the mode in the marker).
|
|
78
|
+
* @returns the error to throw — the marker `FsError` for a sandbox denial, else the original.
|
|
79
|
+
*/
|
|
80
|
+
mapError(error: unknown, policy: SandboxExecutionPolicy | undefined): unknown;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolution options for filesystem calls in the Session's current directory.
|
|
3
|
+
* Non-agent calls leave directory defaults to the filesystem provider.
|
|
4
|
+
* @module @qlzt/dsh-tool-fs/session-cwd
|
|
5
|
+
*/
|
|
6
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
+
import type { ToolExecution } from '@qlzt/dsh-tools';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the current directory and cancellation for a filesystem call.
|
|
10
|
+
* @param ctx - context containing the working-directory service.
|
|
11
|
+
* @param exec - tool execution supplying the agent and cancellation.
|
|
12
|
+
* @returns filesystem resolution options for this call.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sessionResolveOptions(ctx: Context, exec: ToolExecution): Promise<{
|
|
15
|
+
cwd?: string;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=session-cwd.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing full-file write. It obtains an optional intent from the single policy slot, calls
|
|
3
|
+
* `ctx.fs.writeText` without a stat, then records the resulting version; no policy means an
|
|
4
|
+
* unconditional atomic create-or-overwrite.
|
|
5
|
+
* @module @qlzt/dsh-tool-fs/src/write
|
|
6
|
+
*/
|
|
7
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
8
|
+
import type { FsWriteOutcome } from '@qlzt/dsh-fs';
|
|
9
|
+
import type { FsSandboxController } from './sandbox.ts';
|
|
10
|
+
/**
|
|
11
|
+
* Validate value constraints the schema DSL can't express: only a non-blank
|
|
12
|
+
* `file_path` — an empty `content` is legitimate (it writes an empty file).
|
|
13
|
+
* @param args - the schema-validated raw tool arguments.
|
|
14
|
+
* @returns the camelCased input; `content` passes through untouched.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseWriteArgs(args: {
|
|
17
|
+
file_path: string;
|
|
18
|
+
content: string;
|
|
19
|
+
}): {
|
|
20
|
+
filePath: string;
|
|
21
|
+
content: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Format a write outcome as one model-facing text block body.
|
|
25
|
+
* @param displayPath - the backend-resolved path rendered in the envelope's `<path>` element.
|
|
26
|
+
* @param outcome - the write outcome; its `operation` selects the Created/Updated wording.
|
|
27
|
+
* @returns the model-facing confirmation envelope (no file content is echoed back).
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatWriteOutput(displayPath: string, outcome: Pick<FsWriteOutcome, 'operation'>): string;
|
|
30
|
+
/**
|
|
31
|
+
* Register the `write` tool and its scope-aware system-prompt guidance.
|
|
32
|
+
* @param ctx - the plugin context; registrations are effects scoped to it, and execution uses its `fs` service.
|
|
33
|
+
* @param sandbox - the shared sandbox-escalation API (advertisement, mode stamping, denial mapping).
|
|
34
|
+
*/
|
|
35
|
+
export declare function applyWriteTool(ctx: Context, sandbox: FsSandboxController): void;
|
|
36
|
+
//# sourceMappingURL=write.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,71 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qlzt/dsh-tool-fs",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"description": "Model-facing filesystem tools (read, write, edit) over the DeepSeek Harness filesystem seam (ctx.fs)",
|
|
4
|
+
"version": "0.2.1-alpha.3",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://gitee.com/zsping1989/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/fs/tool-fs"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./src/*": "./src/*",
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"lib/index.js",
|
|
26
|
+
"lib/types/**/*.d.ts"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"diff": "^9.0.0",
|
|
31
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1",
|
|
32
|
+
"@qlzt/dsh-util-code-language": "0.2.1-alpha.3"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@qlzt/dsh-attachment": "0.2.1-alpha.3",
|
|
36
|
+
"@qlzt/dsh-fs": "0.2.1-alpha.3",
|
|
37
|
+
"@qlzt/dsh-llm": "0.2.1-alpha.3",
|
|
38
|
+
"@qlzt/dsh-sandbox": "0.2.1-alpha.3",
|
|
39
|
+
"@qlzt/dsh-sandbox-policy": "0.2.1-alpha.3",
|
|
40
|
+
"@qlzt/dsh-session": "0.2.1-alpha.3",
|
|
41
|
+
"@qlzt/dsh-system-prompt": "0.2.1-alpha.3",
|
|
42
|
+
"@qlzt/dsh-tools": "0.2.1-alpha.3",
|
|
43
|
+
"@qlzt/dsh-user-approval": "0.2.1-alpha.3",
|
|
44
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
45
|
+
"@qlzt/dsh-working-directory": "0.2.1-alpha.3"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.3",
|
|
49
|
+
"@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.3",
|
|
50
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.3",
|
|
51
|
+
"@deepseek-ai/dsh-attachment": "0.2.1-alpha.3",
|
|
52
|
+
"@deepseek-ai/dsh-fs": "0.2.1-alpha.3",
|
|
53
|
+
"@deepseek-ai/dsh-fs-local": "0.2.1-alpha.3",
|
|
54
|
+
"@deepseek-ai/dsh-fs-observation-policy": "0.2.1-alpha.3",
|
|
55
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.3",
|
|
56
|
+
"@deepseek-ai/dsh-llm-deepseek": "0.2.1-alpha.3",
|
|
57
|
+
"@deepseek-ai/dsh-llm-deepseek-api-key": "0.2.1-alpha.3",
|
|
58
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.3",
|
|
59
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.3",
|
|
60
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.3",
|
|
61
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.3",
|
|
62
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.3",
|
|
63
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-alpha.3",
|
|
64
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
65
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.3",
|
|
66
|
+
"@deepseek-ai/dsh-scope": "0.2.1-alpha.3",
|
|
67
|
+
"@deepseek-ai/dsh-ptc-runtime": "0.2.1-alpha.3",
|
|
68
|
+
"@deepseek-ai/dsh-working-directory": "0.2.1-alpha.3"
|
|
69
|
+
},
|
|
70
|
+
"dsh": {}
|
|
71
|
+
}
|