@qlzt/dsh-tool-present 0.0.0-stage → 0.2.1-alpha.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/deliverables/tool-present/README.md
5
+ /:
6
+ en: 77fa72665646fedb
7
+ zh: 2d360098a76804c8
8
+ /deepseek-ai-dsh-tool-present:
9
+ en: 7a9bcbc94c211b65
10
+ zh: 5e53b10028b5d918
11
+ /deepseek-ai-dsh-tool-present/summary:
12
+ en: 79708e6e895b94d0
13
+ zh: c98dc1ac9316eb50
14
+ /deepseek-ai-dsh-tool-present/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /deepseek-ai-dsh-tool-present/use-this-package:
18
+ en: 3bb0413b8aa3bd10
19
+ zh: ab753bb651a62209
20
+ /deepseek-ai-dsh-tool-present/understand-the-implementation:
21
+ en: 6e73ca41dddc096e
22
+ zh: b59fdfa3f3cc47e1
23
+ /deepseek-ai-dsh-tool-present/further-exploration:
24
+ en: 66269384f8c0d0f7
25
+ zh: b3d694d2f0ae588a
26
+ /deepseek-ai-dsh-tool-present/model-experience:
27
+ en: 215e7ba838619b7b
28
+ zh: a311da3843709f90
29
+ /deepseek-ai-dsh-tool-present/model-experience/present:
30
+ en: 695c689adf61838a
31
+ zh: 695c689adf61838a
32
+ /deepseek-ai-dsh-tool-present/model-experience/present/what-the-model-sees:
33
+ en: 3dcc5c0e182a3032
34
+ zh: 646f27cd4038017e
35
+ /deepseek-ai-dsh-tool-present/model-experience/present/token-effect:
36
+ en: fbe1ca0b530500a0
37
+ zh: e198e106b85714b6
38
+ /deepseek-ai-dsh-tool-present/model-experience/present/kv-cache-effect:
39
+ en: c5e95beba09dce3c
40
+ zh: 50f71fac2592f652
41
+ /deepseek-ai-dsh-tool-present/known-limitations-and-deferred-work:
42
+ en: 918d8b8f09063e68
43
+ zh: 48591e5324a462c4
44
+ /deepseek-ai-dsh-tool-present/known-limitations-and-deferred-work/dev-note:
45
+ en: e67f633dc6fe7773
46
+ zh: 3bb0c20c93f16f98
package/README.md CHANGED
@@ -1,3 +1,102 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "Declare accessible files as deliverables with present; configuration, Session ownership, and source-file opening."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @deepseek-ai/dsh-tool-present
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ Use `present` to declare final files accessible through the Session filesystem, including files created through shell commands. Users open the current source files in their default application. The tool records paths and optional descriptions without copying file contents.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ `present` validates paths against the Session's current working directory and records absolute paths, so later directory changes do not redirect delivered files.
29
+
30
+ The `standard`, `ptc`, and `cordis` agent presets mount this plugin. Call `present` with `files: [{ path, description? }]` after creating the files. Files must be regular files accessible through the Session filesystem. Relative paths resolve against the Session working directory; absolute paths may name files outside it, including `/tmp` or Downloads. Missing files, directories, final symbolic links, and provider-denied paths fail the call. Files in a shell sandbox’s private `/tmp` must first be written somewhere the Session filesystem can access.
31
+
32
+ Mount it in an agent's Cordis composition with `tools`, `fs`, and the `turnBoundary` Session projection available:
33
+
34
+ ```yaml
35
+ - name: '@deepseek-ai/dsh-tool-present'
36
+ config:
37
+ maxFiles: 8
38
+ ```
39
+
40
+ | Field | Default | Meaning |
41
+ |---|---|---|
42
+ | `maxFiles` | `8` | Positive maximum file count per call |
43
+
44
+ The file-count limit is validated at mount. The tool requires an agent Session with a workspace and an open turn. Delivery belongs to the calling Session; a parent must call `present` itself to declare files created by a subagent.
45
+
46
+ -----
47
+
48
+ <a id="understand-the-implementation"></a>
49
+ ## Understand the implementation
50
+
51
+ <details>
52
+ <summary>Implementation internals — click to expand</summary>
53
+
54
+ The tool resolves paths through the configured filesystem provider and checks regular-file metadata without reading contents. Successful final `tools/result` notifications append `deliverables/presented`, including nested calls. A later enclosing program failure does not revoke an already completed declaration. Blocked results publish none. Each plugin instance records only calls it executed; scoped tools with the same name cannot publish through another instance.
55
+
56
+ The pure `./types` entry declares `PresentedFile` and the Session event without importing Host runtime code. The Web consumer validates persisted declarations before displaying or opening them. New declarations store absolute paths. Legacy relative declarations retain the viewed Session's original workspace as their base.
57
+
58
+ </details>
59
+
60
+ -----
61
+
62
+ <a id="further-exploration"></a>
63
+ ## Further Exploration
64
+
65
+ - [Filesystem subsystem](../../../docs/subsystems/filesystem.md) — provider paths and errors.
66
+ - [Web deliverables](../../client/ui-deliverables/README.md) — source-file opening and cards.
67
+ - [Delivery decision](../../../.agents/notes/implemented/feature/2026-09-08-present-workspace-source-files.md) — Session ownership and required-on-read events.
68
+
69
+ <a id="model-experience"></a>
70
+ ## Model Experience
71
+
72
+ ### present
73
+
74
+ #### What the model sees
75
+
76
+ The [present schema](../../../docs/tool-catalog.md#present) asks for existing accessible files: “Declare existing files as final deliverables for the user. Use it when the user needs a separate file, especially Office documents, spreadsheets, and slide decks; prefer your final response when that suffices. The user opens the current files; their contents are not copied.” The `files` parameter asks for the 1-2 most important deliverables and at most 4 per call. Results report `Presented <path>` for each file; the program result and durable event contain paths and optional descriptions.
77
+
78
+ #### Token effect
79
+
80
+ One tool schema per mounted agent and one result line per delivered file. File bytes do not enter model messages.
81
+
82
+ #### KV Cache effect
83
+
84
+ The tool schema is static for the mount lifetime. Delivery result text extends the conversation without rewriting its prompt prefix.
85
+
86
+ ## Known Limitations and Deferred Work
87
+
88
+ <a id="known-limitations-and-deferred-work"></a>
89
+
90
+ - Metadata and Host-path checks cannot atomically prevent replacement before a desktop application opens a file.
91
+ - Edits change what opens. Deleted or moved source files cannot be opened from their declarations.
92
+ - Session ZIP exports contain declarations, not file contents. Persistent delivery versions and copy-on-write storage are deferred.
93
+
94
+ <a id="dev-note"></a>
95
+ ### Dev Note
96
+
97
+ <details>
98
+ <summary>Working context for maintainers — click to expand</summary>
99
+
100
+ None.
101
+
102
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,102 @@
1
+ ---
2
+ description: "通过 present 声明交付可访问的文件;配置、Session 归属与源文件打开。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-tool-present
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 使用 `present` 声明交付Session 文件系统可访问的最终文件,包括通过 shell 命令创建的文件。用户使用默认应用打开当前源文件。工具记录路径和可选说明,不复制文件内容。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ `present` 按 Session 的当前工作目录校验路径并记录绝对路径,因此后续目录切换不会让已交付文件指向其他位置。
29
+
30
+ `standard`、`ptc` 与 `cordis` Agent preset 挂载本插件。创建文件后,以 `files: [{ path, description? }]` 调用 `present`。文件必须是 Session 文件系统可访问的普通文件。相对路径按 Session 工作目录解析;绝对路径可以指向工作区外的文件,包括 `/tmp` 或 Downloads。文件缺失、为目录、最终路径为符号链接或提供方拒绝访问时,调用失败。Shell 沙箱私有 `/tmp` 中的文件需要先写入 Session 文件系统可访问的位置。
31
+
32
+ 在 Agent 的 Cordis 组合中挂载,并提供 `tools`、`fs` 和 `turnBoundary` Session 投影:
33
+
34
+ ```yaml
35
+ - name: '@deepseek-ai/dsh-tool-present'
36
+ config:
37
+ maxFiles: 8
38
+ ```
39
+
40
+ | 字段 | 默认值 | 含义 |
41
+ |---|---|---|
42
+ | `maxFiles` | `8` | 每次调用的最大文件数,为正整数 |
43
+
44
+ 挂载时校验文件数量上限。工具要求 Agent Session 具有工作区和尚未结束的轮次。交付归调用方 Session 所有;父 Session 如需声明交付子 Agent 创建的文件,必须自行调用 `present`。
45
+
46
+ -----
47
+
48
+ <a id="understand-the-implementation"></a>
49
+ ## 理解实现
50
+
51
+ <details>
52
+ <summary>实现细节——点击展开</summary>
53
+
54
+ 工具通过配置的文件系统提供方解析路径,检查普通文件元数据,不读取内容。成功的最终 `tools/result` 通知追加 `deliverables/presented`,嵌套调用也适用。外层程序随后失败不会撤销已完成的声明。被阻止的结果不发布声明。每个插件实例只记录其实际执行的调用;同名作用域工具不能通过其他实例发布交付。
55
+
56
+ 纯 `./types` 入口声明 `PresentedFile` 与 Session 事件,不导入 Host 运行时代码。Web 消费方在展示或打开文件前校验持久声明。新声明保存绝对路径。历史相对路径声明继续以当前查看的 Session 原始工作区作为基准。
57
+
58
+ </details>
59
+
60
+ -----
61
+
62
+ <a id="further-exploration"></a>
63
+ ## 进一步探索
64
+
65
+ - [文件系统子系统](../../../docs/subsystems/filesystem.zh.md)——提供方路径与错误。
66
+ - [Web 交付](../../client/ui-deliverables/README.zh.md)——源文件打开与卡片。
67
+ - [交付决策](../../../.agents/notes/implemented/feature/2026-09-08-present-workspace-source-files.zh.md)——Session 归属与读取端必须识别的事件。
68
+
69
+ <a id="model-experience"></a>
70
+ ## 模型体验
71
+
72
+ ### present
73
+
74
+ #### 模型看到的内容
75
+
76
+ [present schema](../../../docs/tool-catalog.zh.md#present)要求已有且可访问的文件:“Declare existing files as final deliverables for the user. Use it when the user needs a separate file, especially Office documents, spreadsheets, and slide decks; prefer your final response when that suffices. The user opens the current files; their contents are not copied.” `files` 参数要求选择 1-2 个最重要的交付物,每次调用最多 4 个。每个文件的结果为 `Presented <path>`;程序结果和持久事件包含路径及可选说明。
77
+
78
+ #### Token 影响
79
+
80
+ 每个挂载的 Agent 增加一个工具 schema,每个交付文件增加一行结果。文件字节不进入模型消息。
81
+
82
+ #### KV Cache 影响
83
+
84
+ 工具 schema 在挂载期间保持静态。交付结果文本扩展对话,不重写提示词前缀。
85
+
86
+ ## 已知限制与延期工作
87
+
88
+ <a id="known-limitations-and-deferred-work"></a>
89
+
90
+ - 元数据和 Host 路径检查无法原子性地阻止桌面应用打开文件前发生的路径替换。
91
+ - 编辑会改变打开的内容。源文件删除或移动后,无法通过原声明打开。
92
+ - Session ZIP 导出包含声明,不包含文件内容。交付版本持久化和写时复制存储延期实现。
93
+
94
+ <a id="dev-note"></a>
95
+ ### 开发备注
96
+
97
+ <details>
98
+ <summary>维护者的工作上下文——点击展开</summary>
99
+
100
+ 无。
101
+
102
+ </details>
package/lib/index.js ADDED
@@ -0,0 +1,127 @@
1
+ import z from "@deepseek-ai/schemastery";
2
+ import { FsError } from "@qlzt/dsh-fs";
3
+ import { defineTool } from "@qlzt/dsh-tools";
4
+ //#region lib/types/index.js
5
+ /** Stable Loader identity. */
6
+ const name = "tool-present";
7
+ /** Validated delivery limit. */
8
+ const Config = z.object({ maxFiles: z.number().default(8) });
9
+ /** Services used by the scoped delivery tool. */
10
+ const inject = [
11
+ "tools",
12
+ "fs",
13
+ "sessionProjections",
14
+ "workingDirectory"
15
+ ];
16
+ /**
17
+ * Register present with durable file references in its tool result.
18
+ * @param ctx - agent-scoped services.
19
+ * @param config - maximum files per call.
20
+ */
21
+ function apply(ctx, config) {
22
+ if (!Number.isSafeInteger(config.maxFiles) || config.maxFiles < 1) throw new Error("present requires a positive integer maxFiles");
23
+ const pending = /* @__PURE__ */ new WeakMap();
24
+ ctx.tools.register(defineTool({
25
+ name: "present",
26
+ description: "Declare existing files as final deliverables for the user. Use it when the user needs a separate file, especially Office documents, spreadsheets, and slide decks; prefer your final response when that suffices. The user opens the current files; their contents are not copied.",
27
+ parameters: { files: {
28
+ type: "array",
29
+ required: true,
30
+ description: "Usually the 1-2 most important deliverables; at most 4 per call.",
31
+ items: {
32
+ type: "object",
33
+ additionalProperties: false,
34
+ properties: {
35
+ path: {
36
+ type: "string",
37
+ required: true,
38
+ description: "Path of an existing regular file. Relative paths use the Session working directory."
39
+ },
40
+ description: {
41
+ type: "string",
42
+ description: "Brief description for the user."
43
+ }
44
+ }
45
+ }
46
+ } },
47
+ output: {
48
+ schema: {
49
+ type: "object",
50
+ additionalProperties: false,
51
+ properties: {
52
+ turn: {
53
+ type: "integer",
54
+ required: true
55
+ },
56
+ files: {
57
+ type: "array",
58
+ required: true,
59
+ items: {
60
+ type: "object",
61
+ additionalProperties: false,
62
+ properties: {
63
+ path: {
64
+ type: "string",
65
+ required: true
66
+ },
67
+ description: { type: "string" }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ },
73
+ render: (_args, value) => [{
74
+ type: "text",
75
+ text: value.files.map((file) => `Presented ${file.path}`).join("\n")
76
+ }]
77
+ },
78
+ async execute(args, exec) {
79
+ if (exec.agent === void 0) throw new Error("present requires an agent Session");
80
+ const boundary = ctx.sessionProjections.stateOf(exec.agent.session, "turnBoundary");
81
+ if (boundary === void 0 || boundary.openTurnStartSeq === null) throw new Error("present requires an open turn");
82
+ if (args.files.length === 0 || args.files.length > config.maxFiles) throw new Error(`present accepts 1 to ${config.maxFiles} files`);
83
+ const cwd = await ctx.workingDirectory.ensure(exec.agent, exec.signal);
84
+ const options = {
85
+ cwd,
86
+ signal: exec.signal
87
+ };
88
+ const files = [];
89
+ for (const file of args.files) {
90
+ if (file.path.trim().length === 0) throw new Error("present requires a non-empty file path");
91
+ const entry = await ctx.fs.lstat(file.path, { cwd }, exec.signal);
92
+ if (entry !== void 0 && entry.type !== "file") throw new Error(`Cannot present ${file.path}: not a regular file`);
93
+ const target = await ctx.fs.resolve(file.path, options);
94
+ const info = await ctx.fs.stat(target, exec.signal);
95
+ if (info === void 0) throw new FsError(`Cannot present ${file.path}: file not found. Check the path, create the file if needed, and retry.`, "FS_NOT_FOUND");
96
+ if (info.type !== "file") throw new Error(`Cannot present ${file.path}: not a regular file`);
97
+ files.push({
98
+ ...file,
99
+ path: ctx.fs.processPath(target)
100
+ });
101
+ }
102
+ exec.signal.throwIfAborted();
103
+ pending.set(exec, {
104
+ session: exec.agent.session,
105
+ turn: boundary.lastTurn,
106
+ files
107
+ });
108
+ return {
109
+ turn: boundary.lastTurn,
110
+ files
111
+ };
112
+ }
113
+ }));
114
+ ctx.on("tools/result", (exec, result) => {
115
+ const delivery = pending.get(exec);
116
+ pending.delete(exec);
117
+ if (delivery === void 0 || result.isError) return;
118
+ const { session, turn, files } = delivery;
119
+ session.append("deliverables/presented", {
120
+ turn,
121
+ callId: exec.callId,
122
+ files
123
+ });
124
+ });
125
+ }
126
+ //#endregion
127
+ export { Config, apply, inject, name };
@@ -0,0 +1,20 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import z from '@deepseek-ai/schemastery';
3
+ /** Stable Loader identity. */
4
+ export declare const name = "tool-present";
5
+ /** Per-call delivery limit. */
6
+ export interface Config {
7
+ /** Maximum number of files in one call. */
8
+ maxFiles: number;
9
+ }
10
+ /** Validated delivery limit. */
11
+ export declare const Config: z<Config>;
12
+ /** Services used by the scoped delivery tool. */
13
+ export declare const inject: string[];
14
+ /**
15
+ * Register present with durable file references in its tool result.
16
+ * @param ctx - agent-scoped services.
17
+ * @param config - maximum files per call.
18
+ */
19
+ export declare function apply(ctx: Context, config: Config): void;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,101 @@
1
+ import z from '@deepseek-ai/schemastery';
2
+ import { FsError } from '@qlzt/dsh-fs';
3
+ import { defineTool } from '@qlzt/dsh-tools';
4
+ /** Stable Loader identity. */
5
+ export const name = 'tool-present';
6
+ /** Validated delivery limit. */
7
+ export const Config = z.object({
8
+ maxFiles: z.number().default(8),
9
+ });
10
+ /** Services used by the scoped delivery tool. */
11
+ export const inject = ['tools', 'fs', 'sessionProjections', 'workingDirectory'];
12
+ /**
13
+ * Register present with durable file references in its tool result.
14
+ * @param ctx - agent-scoped services.
15
+ * @param config - maximum files per call.
16
+ */
17
+ export function apply(ctx, config) {
18
+ if (!Number.isSafeInteger(config.maxFiles) || config.maxFiles < 1) {
19
+ throw new Error('present requires a positive integer maxFiles');
20
+ }
21
+ const pending = new WeakMap();
22
+ ctx.tools.register(defineTool({
23
+ name: 'present',
24
+ description: 'Declare existing files as final deliverables for the user. '
25
+ + 'Use it when the user needs a separate file, especially Office documents, spreadsheets, and slide decks; '
26
+ + 'prefer your final response when that suffices. The user opens the current files; their contents are not copied.',
27
+ parameters: {
28
+ files: {
29
+ type: 'array', required: true,
30
+ // 4 is the recommended per-call count; `maxFiles` is the enforced ceiling above it.
31
+ description: 'Usually the 1-2 most important deliverables; at most 4 per call.',
32
+ items: {
33
+ type: 'object', additionalProperties: false,
34
+ properties: {
35
+ path: { type: 'string', required: true, description: 'Path of an existing regular file. Relative paths use the Session working directory.' },
36
+ description: { type: 'string', description: 'Brief description for the user.' },
37
+ },
38
+ },
39
+ },
40
+ },
41
+ output: {
42
+ schema: {
43
+ type: 'object', additionalProperties: false,
44
+ properties: {
45
+ turn: { type: 'integer', required: true },
46
+ files: {
47
+ type: 'array', required: true,
48
+ items: {
49
+ type: 'object', additionalProperties: false,
50
+ properties: {
51
+ path: { type: 'string', required: true },
52
+ description: { type: 'string' },
53
+ },
54
+ },
55
+ },
56
+ },
57
+ },
58
+ render: (_args, value) => [{ type: 'text', text: value.files.map(file => `Presented ${file.path}`).join('\n') }],
59
+ },
60
+ async execute(args, exec) {
61
+ if (exec.agent === undefined)
62
+ throw new Error('present requires an agent Session');
63
+ const boundary = ctx.sessionProjections.stateOf(exec.agent.session, 'turnBoundary');
64
+ if (boundary === undefined || boundary.openTurnStartSeq === null)
65
+ throw new Error('present requires an open turn');
66
+ if (args.files.length === 0 || args.files.length > config.maxFiles)
67
+ throw new Error(`present accepts 1 to ${config.maxFiles} files`);
68
+ const cwd = await ctx.workingDirectory.ensure(exec.agent, exec.signal);
69
+ const options = { cwd, signal: exec.signal };
70
+ const files = [];
71
+ for (const file of args.files) {
72
+ if (file.path.trim().length === 0)
73
+ throw new Error('present requires a non-empty file path');
74
+ const entry = await ctx.fs.lstat(file.path, { cwd }, exec.signal);
75
+ if (entry !== undefined && entry.type !== 'file')
76
+ throw new Error(`Cannot present ${file.path}: not a regular file`);
77
+ const target = await ctx.fs.resolve(file.path, options);
78
+ const info = await ctx.fs.stat(target, exec.signal);
79
+ if (info === undefined)
80
+ throw new FsError(`Cannot present ${file.path}: file not found. Check the path, create the file if needed, and retry.`, 'FS_NOT_FOUND');
81
+ if (info.type !== 'file')
82
+ throw new Error(`Cannot present ${file.path}: not a regular file`);
83
+ files.push({ ...file, path: ctx.fs.processPath(target) });
84
+ }
85
+ exec.signal.throwIfAborted();
86
+ pending.set(exec, { session: exec.agent.session, turn: boundary.lastTurn, files });
87
+ return { turn: boundary.lastTurn, files };
88
+ },
89
+ }));
90
+ ctx.on('tools/result', (exec, result) => {
91
+ const delivery = pending.get(exec);
92
+ pending.delete(exec);
93
+ if (delivery === undefined || result.isError)
94
+ return;
95
+ const { session, turn, files } = delivery;
96
+ session.append('deliverables/presented', {
97
+ turn, callId: exec.callId, files,
98
+ });
99
+ });
100
+ }
101
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,20 @@
1
+ /** Durable file deliveries produced by the present tool. */
2
+ import type { ToolCallId } from '@qlzt/dsh-llm/brand';
3
+ /** A declared filesystem file whose current contents remain at its source path. */
4
+ export interface PresentedFile {
5
+ /** Original absolute path or path relative to the Session working directory. */
6
+ path: string;
7
+ /** Optional description supplied by the model. */
8
+ description?: string;
9
+ }
10
+ declare module '@qlzt/dsh-session/types' {
11
+ interface SessionEventMap {
12
+ /** Declared filesystem files from a successful final present result, including nested calls. */
13
+ 'deliverables/presented': {
14
+ turn: number;
15
+ callId: ToolCallId;
16
+ files: PresentedFile[];
17
+ };
18
+ }
19
+ }
20
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@qlzt/dsh-tool-present",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "Explicit workspace file delivery declarations for the DeepSeek Harness",
4
+ "version": "0.2.1-alpha.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://gitee.com/zsping1989/deepseek-harness.git",
11
+ "directory": "packages/deliverables/tool-present"
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
+ "./types": {
22
+ "types": "./lib/types/types.d.ts",
23
+ "default": "./lib/types/types.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "license": "MIT",
29
+ "files": [
30
+ "lib/index.js",
31
+ "lib/types/**/*.js",
32
+ "lib/types/**/*.d.ts"
33
+ ],
34
+ "dependencies": {
35
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.1"
36
+ },
37
+ "peerDependencies": {
38
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
39
+ "@qlzt/dsh-agent": "0.2.1-alpha.2",
40
+ "@qlzt/dsh-fs": "0.2.1-alpha.2",
41
+ "@qlzt/dsh-llm": "0.2.1-alpha.2",
42
+ "@qlzt/dsh-session": "0.2.1-alpha.2",
43
+ "@qlzt/dsh-session-projection": "0.2.1-alpha.2",
44
+ "@qlzt/dsh-tools": "0.2.1-alpha.2",
45
+ "@qlzt/dsh-working-directory": "0.2.1-alpha.2"
46
+ },
47
+ "devDependencies": {
48
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
49
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
50
+ "@deepseek-ai/dsh-fs": "0.2.1-alpha.2",
51
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
52
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.2",
53
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
54
+ "@deepseek-ai/dsh-tools": "0.2.1-alpha.2",
55
+ "@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.2",
56
+ "@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.2",
57
+ "@deepseek-ai/dsh-fs-local": "0.2.1-alpha.2",
58
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.2",
59
+ "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
60
+ "@deepseek-ai/dsh-working-directory": "0.2.1-alpha.2"
61
+ },
62
+ "dsh": {}
63
+ }