@deepseek-ai/dsh-tool-ask-user 0.1.7-rc.2 → 0.2.0-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/README.i18n.yaml +22 -22
- package/README.md +27 -11
- package/README.zh.md +27 -11
- package/lib/index.js +211 -3
- package/lib/types/index.d.ts +10 -1
- package/lib/types/timed.d.ts +11 -0
- package/package.json +12 -9
package/README.i18n.yaml
CHANGED
|
@@ -9,32 +9,32 @@
|
|
|
9
9
|
en: 2a561a70c4a900d7
|
|
10
10
|
zh: 3d12372992cf2634
|
|
11
11
|
/deepseek-ai-dsh-tool-ask-user/summary:
|
|
12
|
-
en:
|
|
13
|
-
zh:
|
|
12
|
+
en: 1a9a38698a6cb1f3
|
|
13
|
+
zh: 35540765df5648e7
|
|
14
14
|
/deepseek-ai-dsh-tool-ask-user/table-of-contents:
|
|
15
15
|
en: d152484eb41ac6b4
|
|
16
16
|
zh: 09388d293f9be9cb
|
|
17
17
|
/deepseek-ai-dsh-tool-ask-user/use-this-package:
|
|
18
|
-
en:
|
|
19
|
-
zh:
|
|
18
|
+
en: 547a3ea61708bc3e
|
|
19
|
+
zh: 5e251ea392940c1a
|
|
20
20
|
/deepseek-ai-dsh-tool-ask-user/use-this-package/when-to-call-the-tool:
|
|
21
|
-
en:
|
|
22
|
-
zh:
|
|
21
|
+
en: 7c70d43d4cd9aa19
|
|
22
|
+
zh: 7597e972067ec5fe
|
|
23
23
|
/deepseek-ai-dsh-tool-ask-user/use-this-package/what-the-model-gets-back:
|
|
24
|
-
en:
|
|
25
|
-
zh:
|
|
24
|
+
en: c30ba8cefa64a287
|
|
25
|
+
zh: cc8bc285ff859983
|
|
26
26
|
/deepseek-ai-dsh-tool-ask-user/use-this-package/when-the-call-fails:
|
|
27
|
-
en:
|
|
28
|
-
zh:
|
|
27
|
+
en: c4ac3c584f900230
|
|
28
|
+
zh: bd8d227f0637b8f1
|
|
29
29
|
/deepseek-ai-dsh-tool-ask-user/understand-the-implementation:
|
|
30
30
|
en: e9b9d46be0b45ca2
|
|
31
31
|
zh: c02403486473cd80
|
|
32
32
|
/deepseek-ai-dsh-tool-ask-user/understand-the-implementation/source-map:
|
|
33
|
-
en:
|
|
34
|
-
zh:
|
|
33
|
+
en: 95a6f15bb08e239b
|
|
34
|
+
zh: 9def25d23f0e131d
|
|
35
35
|
/deepseek-ai-dsh-tool-ask-user/understand-the-implementation/consumer-role:
|
|
36
|
-
en:
|
|
37
|
-
zh:
|
|
36
|
+
en: 7de88d25287b969f
|
|
37
|
+
zh: be36dc5a39aeac3b
|
|
38
38
|
/deepseek-ai-dsh-tool-ask-user/understand-the-implementation/result-rendering:
|
|
39
39
|
en: 686176b2703dfa08
|
|
40
40
|
zh: 4ea4dab0da7f79f9
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
en: e4c7376d4f29dbd5
|
|
49
49
|
zh: 6b7a9eaf69c9ea53
|
|
50
50
|
/deepseek-ai-dsh-tool-ask-user/model-experience/tool-schema/what-the-model-sees:
|
|
51
|
-
en:
|
|
52
|
-
zh:
|
|
51
|
+
en: 577c8609a8bb915d
|
|
52
|
+
zh: 8607e0d39be0e492
|
|
53
53
|
/deepseek-ai-dsh-tool-ask-user/model-experience/tool-schema/token-effect:
|
|
54
54
|
en: 97cce067584eadff
|
|
55
55
|
zh: c376cab39ea9df68
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
en: 9717b983b6a857e3
|
|
61
61
|
zh: 611a2dadf7c0c706
|
|
62
62
|
/deepseek-ai-dsh-tool-ask-user/model-experience/tool-call-history-and-result/what-the-model-sees:
|
|
63
|
-
en:
|
|
64
|
-
zh:
|
|
63
|
+
en: c2ceed515656e10b
|
|
64
|
+
zh: 4e9b6768e4914718
|
|
65
65
|
/deepseek-ai-dsh-tool-ask-user/model-experience/tool-call-history-and-result/token-effect:
|
|
66
66
|
en: 3092d4afd4ba8678
|
|
67
67
|
zh: cb7a7bbefdbc3a1e
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
en: f5d5f97a153f7c70
|
|
70
70
|
zh: 4db81881ebdcfcc4
|
|
71
71
|
/deepseek-ai-dsh-tool-ask-user/known-limitations-and-deferred-work:
|
|
72
|
-
en:
|
|
73
|
-
zh:
|
|
72
|
+
en: c8df1368c2959465
|
|
73
|
+
zh: b14fb07a60e75ebf
|
|
74
74
|
/deepseek-ai-dsh-tool-ask-user/known-limitations-and-deferred-work/dev-note:
|
|
75
|
-
en:
|
|
76
|
-
zh:
|
|
75
|
+
en: 0b06d843322610c1
|
|
76
|
+
zh: a1e7cadd4f03c384
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
`ask_user_question`
|
|
12
|
+
`ask_user_question` asks the user for confirmation, a choice, or missing information. It waits for an answer by default. With `mode: timed`, a deadline can release the model to continue independent work while the question stays answerable; `timeout: -1` waits indefinitely. A live child agent cannot call the tool. Callers provide the answer UI.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -25,11 +25,23 @@ English | [中文](README.zh.md)
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
-
Compose this plugin
|
|
28
|
+
Compose this plugin with `ctx.userQuestions` when the model needs a user decision. Without an answerer, blocking calls fail; finite timed calls return pending at their deadline.
|
|
29
|
+
|
|
30
|
+
Shipped presets use blocking mode. To enable timed mode, set `mode: timed` on the `tool-ask-user` row in the active preset's `config.plugins` list:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
- id: tool-ask-user
|
|
34
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
35
|
+
config:
|
|
36
|
+
mode: timed
|
|
37
|
+
timeout: 120
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This is a plugin row, not a top-level `--patch` entry. In the Web profile, change the `preset-standard` plugin list or use the Agent Preset editor. `mode: legacy` or omitted config keeps the blocking schema. The row's `timeout` applies to each timed call by default; `-1` waits indefinitely unless the model supplies a positive timeout. A model-supplied `-1` applies only to that call, including all its questions.
|
|
29
41
|
|
|
30
42
|
### When to call the tool
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
Send one or more questions with ids unique within the call; the answer echoes those ids. Put a recommended option first and append `(Recommended)` to its label. The optional per-call `timeout` is in seconds; use `-1` when work cannot safely continue without an answer. A timeout is never approval. In the Web card, editing or choosing Take time also holds that Client's wait until submission or cancellation.
|
|
33
45
|
|
|
34
46
|
```json
|
|
35
47
|
{
|
|
@@ -49,7 +61,9 @@ The model calls `ask_user_question` when it needs confirmation, a choice, or mis
|
|
|
49
61
|
|
|
50
62
|
### What the model gets back
|
|
51
63
|
|
|
52
|
-
|
|
64
|
+
An answer returns one item per question. `selected` holds option labels; `custom` supplements a multi-select answer or replaces a single-select choice. A skipped item has empty `selected` and no `custom`.
|
|
65
|
+
|
|
66
|
+
For a finite timed call, `{ "pending": true, "callId": "…" }` means the foreground wait ended without an answer. The question remains answerable, and the model may continue independent work. A later answer arrives as a user message identified by `kind`, `tool`, and `callId`. Web chat pairs its questions and answers; other consumers receive compact JSON text.
|
|
53
67
|
|
|
54
68
|
```json
|
|
55
69
|
{ "answers": [{ "id": "cleanup", "selected": ["Yes, delete them (Recommended)"] }] }
|
|
@@ -57,7 +71,7 @@ The tool returns one answer object per question: `selected` holds the chosen opt
|
|
|
57
71
|
|
|
58
72
|
### When the call fails
|
|
59
73
|
|
|
60
|
-
|
|
74
|
+
Legacy and `timeout: -1` calls wait for an answer or cancellation; without an accepting answerer, they return an error. A finite timed call without an answerer returns pending at its deadline. Cancellation and a caller that is not the exact live runtime root also return errors. A live child agent is rejected with `DELEGATED_CALLER` and must include the unresolved decision in its final result.
|
|
61
75
|
|
|
62
76
|
-----
|
|
63
77
|
|
|
@@ -73,12 +87,13 @@ The observable behavior is covered in [Use this package](#use-this-package); thi
|
|
|
73
87
|
|
|
74
88
|
| File | Role |
|
|
75
89
|
|---|---|
|
|
76
|
-
| [`src/index.ts`](src/index.ts) |
|
|
90
|
+
| [`src/index.ts`](src/index.ts) | Default blocking tool definition and mode selection |
|
|
91
|
+
| [`src/timed.ts`](src/timed.ts) | Opt-in timed tool definition and result rendering |
|
|
77
92
|
| — | No runtime invariant companion is published; this model-facing adapter has no independent lifecycle stream; execution relations are owned by the capability seam it calls. |
|
|
78
93
|
|
|
79
94
|
### Consumer role
|
|
80
95
|
|
|
81
|
-
The plugin registers one `
|
|
96
|
+
The plugin registers one tool definition. Legacy mode calls `ask()`; timed mode calls `askTimed()` for positive timeouts and `ask()` for `-1`. Both forward the calling agent and turn signal to `ctx.userQuestions`. Timed requests include the tool call id in `wait`, so a Client can reopen their card. The projection identifies timed native calls from the `timeout` field in the recorded tool schema, even when a call omits that argument.
|
|
82
97
|
|
|
83
98
|
### Result rendering
|
|
84
99
|
|
|
@@ -107,7 +122,7 @@ Read these pages when the package-level contract is not enough. They move from t
|
|
|
107
122
|
|
|
108
123
|
#### What the model sees
|
|
109
124
|
|
|
110
|
-
The
|
|
125
|
+
The shipped presets expose the original blocking [`ask_user_question` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-ask-user). A custom Cordis row with `mode: timed` switches to the alternate schema, including question ids, prompts, headings, options, multi-select flags, `timeout`, and the pending result; the model sees only the selected definition.
|
|
111
126
|
|
|
112
127
|
#### Token effect
|
|
113
128
|
|
|
@@ -121,7 +136,7 @@ Prefix-stable while the definition and visibility are unchanged. Plugin lifecycl
|
|
|
121
136
|
|
|
122
137
|
#### What the model sees
|
|
123
138
|
|
|
124
|
-
The
|
|
139
|
+
The assistant tool call retains the questions. An answer during the wait appears in the next step as compact `{"answers":[{"id":"<id>","selected":["<label>"],"custom":"<text>"}]}` JSON; unused `custom` is omitted. A timed-out call returns `{"pending":true,"callId":"<pending-call-id>","message":"<instruction>"}`. A later answer arrives as a user message with `kind: "answer_to_pending_question"`, `tool: "ask_user_question"`, the call id, the original questions, and the answers. UI activity before submission is not model context.
|
|
125
140
|
|
|
126
141
|
#### Token effect
|
|
127
142
|
|
|
@@ -138,7 +153,8 @@ Append-only; newly visible content follows the reusable request prefix and does
|
|
|
138
153
|
|
|
139
154
|
These limits define when the tool is a poor fit. They are current package constraints, not a UI backlog.
|
|
140
155
|
|
|
141
|
-
- **
|
|
156
|
+
- **The legacy tool never reports pending** — it returns an answer or an error. Native legacy calls do not enter the `userQuestions` projection, and interrupted calls cannot take late answers.
|
|
157
|
+
- **An interrupted PTC wait may lose its question** — if the `run_code` process ends before an `ask_user_question` sub-call records its `tool/ptc-dispatch` result, the projection cannot reconstruct that sub-call for a late answer.
|
|
142
158
|
- **Runtime-owned subagents cannot ask the user** — `ask_user_question` rejects a live child owned by another agent with `DELEGATED_CALLER`; the child must include the unresolved question or decision in its final result. Durable lineage does not decide this boundary, so a lineage-bearing session resumed as a runtime root may ask normally.
|
|
143
159
|
- **Native answers render as JSON text** — the canonical value remains structured, but the model-facing result uses compact JSON rather than a richer content-block vocabulary.
|
|
144
160
|
|
|
@@ -148,6 +164,6 @@ These limits define when the tool is a poor fit. They are current package constr
|
|
|
148
164
|
<details>
|
|
149
165
|
<summary>Working context for maintainers — click to expand</summary>
|
|
150
166
|
|
|
151
|
-
|
|
167
|
+
The [`userQuestions` projection](../user-questions/src/projection.ts) reads each recorded tool schema rather than the call arguments: timed calls may omit `timeout`, and timed `-1` calls still use the timed schema. Changes to the projection fold require a `stateVersion` bump so stored caches refold from the log.
|
|
152
168
|
|
|
153
169
|
</details>
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`ask_user_question`
|
|
12
|
+
`ask_user_question` 向用户请求确认、选择或缺失的信息。默认情况下,它会等待回答。设置 `mode: timed` 后,期限到达时模型可以继续独立工作,而问题仍可回答;`timeout: -1` 则无限期等待。存活的子 agent 不能调用此工具。调用方需提供回答界面。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -25,11 +25,23 @@ kind: "package-reference"
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
当模型需要用户决定时,将此插件与 `ctx.userQuestions` 组合。没有 answerer 时,阻塞式调用返回错误;有限时长的 timed 调用到期后返回 pending。
|
|
29
|
+
|
|
30
|
+
随附 preset 使用阻塞模式。要启用 timed 模式,请在当前 preset 的 `config.plugins` 列表中,将 `tool-ask-user` 条目设为 `mode: timed`:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
- id: tool-ask-user
|
|
34
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
35
|
+
config:
|
|
36
|
+
mode: timed
|
|
37
|
+
timeout: 120
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
以上片段是插件条目,不是顶层 `--patch` 条目。在 Web profile 中,请修改 `preset-standard` 的插件列表,或使用 Agent Preset 编辑器。`mode: legacy` 或省略配置会保留阻塞式 schema。条目中的 `timeout` 默认适用于每次 timed 调用;设为 `-1` 时会无限期等待,除非模型传入正数期限。模型传入的 `-1` 只适用于该次调用,包括其中的所有问题。
|
|
29
41
|
|
|
30
42
|
### 何时调用该工具
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
发送一个或多个问题,每个问题的 `id` 在本次调用内必须唯一;回答会带回这些 id。推荐选项放在首位,并在标签末尾追加 `(Recommended)`。可选的单次调用 `timeout` 以秒为单位;如果没有回答就无法安全继续,请使用 `-1`。超时绝不表示批准。在 Web 卡片中,开始编辑或选择“慢慢回答”也会让该 Client 的等待持续到提交或取消。
|
|
33
45
|
|
|
34
46
|
```json
|
|
35
47
|
{
|
|
@@ -49,7 +61,9 @@ kind: "package-reference"
|
|
|
49
61
|
|
|
50
62
|
### 模型得到什么
|
|
51
63
|
|
|
52
|
-
|
|
64
|
+
收到回答时,工具为每个问题返回一项。`selected` 保存选项标签;`custom` 可以补充多选答案,或替代单选选项。跳过的回答项具有空 `selected`,且没有 `custom`。
|
|
65
|
+
|
|
66
|
+
对于有限时长的 timed 调用,`{ "pending": true, "callId": "…" }` 表示前台等待结束时仍没有回答。问题仍可回答,模型可以继续独立工作。之后的回答以用户消息送达,由 `kind`、`tool` 和 `callId` 标识。Web 对话会配对展示问题与回答;其他消费方收到紧凑 JSON 文本。
|
|
53
67
|
|
|
54
68
|
```json
|
|
55
69
|
{ "answers": [{ "id": "cleanup", "selected": ["Yes, delete them (Recommended)"] }] }
|
|
@@ -57,7 +71,7 @@ kind: "package-reference"
|
|
|
57
71
|
|
|
58
72
|
### 调用何时失败
|
|
59
73
|
|
|
60
|
-
|
|
74
|
+
Legacy 调用和 `timeout: -1` 调用会等待回答或取消;没有 answerer 接受时返回错误。有限时长的 timed 调用若没有 answerer,则在到期后返回 pending。取消调用,或调用方不是确切的存活运行时根时,也会返回错误。存活的子 agent 会以 `DELEGATED_CALLER` 被拒绝,必须在最终结果中包含尚未解决的决定。
|
|
61
75
|
|
|
62
76
|
-----
|
|
63
77
|
|
|
@@ -73,12 +87,13 @@ kind: "package-reference"
|
|
|
73
87
|
|
|
74
88
|
| 文件 | 职责 |
|
|
75
89
|
|---|---|
|
|
76
|
-
| [`src/index.ts`](src/index.ts) |
|
|
90
|
+
| [`src/index.ts`](src/index.ts) | 默认阻塞式工具定义与模式选择 |
|
|
91
|
+
| [`src/timed.ts`](src/timed.ts) | 显式启用的计时工具定义与结果渲染 |
|
|
77
92
|
| — | 不发布运行时不变式伴生入口;此模型侧适配器没有独立的生命周期流;执行关系由其调用的能力 seam 负责。 |
|
|
78
93
|
|
|
79
94
|
### 消费方角色
|
|
80
95
|
|
|
81
|
-
|
|
96
|
+
插件只注册一个工具定义。Legacy 模式调用 `ask()`;timed 模式对正数期限调用 `askTimed()`,对 `-1` 调用 `ask()`。两者都向 `ctx.userQuestions` 传递调用方 agent 和轮次信号。Timed 请求在 `wait` 中包含工具调用 id,Client 因此可以重新打开对应卡片。投影从记录的工具 schema 的 `timeout` 字段识别 timed 原生调用,即使某次调用省略该参数也一样。
|
|
82
97
|
|
|
83
98
|
### 结果渲染
|
|
84
99
|
|
|
@@ -107,7 +122,7 @@ kind: "package-reference"
|
|
|
107
122
|
|
|
108
123
|
#### 模型看到的内容
|
|
109
124
|
|
|
110
|
-
|
|
125
|
+
随附 preset 会公开原有的阻塞式 [`ask_user_question` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-ask-user)。自定义 Cordis 行设置 `mode: timed` 后,会切换到包含问题 id、提示语、标题、选项、多选标志、`timeout` 与待处理结果的异步 schema;模型只会看到被选中的定义。
|
|
111
126
|
|
|
112
127
|
#### Token 影响
|
|
113
128
|
|
|
@@ -121,7 +136,7 @@ kind: "package-reference"
|
|
|
121
136
|
|
|
122
137
|
#### 模型看到的内容
|
|
123
138
|
|
|
124
|
-
|
|
139
|
+
assistant 工具调用保留问题。等待期间收到的回答会在下一步显示为紧凑的 `{"answers":[{"id":"<id>","selected":["<label>"],"custom":"<text>"}]}` JSON;未使用的 `custom` 会省略。计时到期的调用返回 `{"pending":true,"callId":"<pending-call-id>","message":"<instruction>"}`。之后的回答以用户消息送达,包含 `kind: "answer_to_pending_question"`、`tool: "ask_user_question"`、调用 id、原问题和答案。提交之前的 UI 活动不属于模型上下文。
|
|
125
140
|
|
|
126
141
|
#### Token 影响
|
|
127
142
|
|
|
@@ -138,7 +153,8 @@ kind: "package-reference"
|
|
|
138
153
|
|
|
139
154
|
这些限制说明该工具何时不合适。它们是当前包约束,不是 UI 积压事项。
|
|
140
155
|
|
|
141
|
-
-
|
|
156
|
+
- **Legacy 工具不会报告 pending**:它只返回回答或错误。原生 legacy 调用不进入 `userQuestions` 投影;中断的调用不能接受迟到回答。
|
|
157
|
+
- **中断的 PTC 等待可能丢失问题**:若 `run_code` 进程在 `ask_user_question` 子调用记录 `tool/ptc-dispatch` 结果前结束,投影无法重建该子调用以接受迟到回答。
|
|
142
158
|
- **运行时中归属于其他 agent 的 subagent 不能向用户提问**:`ask_user_question` 会以 `DELEGATED_CALLER` 拒绝归属于另一个 agent 的存活子级;该子级必须在最终结果中包含尚未解决的问题或决定。持久谱系不能决定这一边界,因此带有谱系的会话恢复为运行时根后可以正常提问。
|
|
143
159
|
- **Native 回答渲染为 JSON 文本**:规范值仍为结构化数据,但模型侧结果使用紧凑 JSON,而非更丰富的内容块词汇。
|
|
144
160
|
|
|
@@ -148,6 +164,6 @@ kind: "package-reference"
|
|
|
148
164
|
<details>
|
|
149
165
|
<summary>维护者的工作上下文——点击展开</summary>
|
|
150
166
|
|
|
151
|
-
|
|
167
|
+
[`userQuestions` 投影](../user-questions/src/projection.ts)读取记录的工具 schema,而不是调用参数:timed 调用可以省略 `timeout`,timed `-1` 调用也仍使用 timed schema。改变投影折叠语义时必须提高 `stateVersion`,让持久化缓存从日志重新折叠。
|
|
152
168
|
|
|
153
169
|
</details>
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,205 @@
|
|
|
1
1
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
2
|
-
import "@deepseek-ai/
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { TIMED_WAIT_PARAMETER } from "@deepseek-ai/dsh-user-questions";
|
|
4
|
+
//#region lib/types/timed.js
|
|
5
|
+
/**
|
|
6
|
+
* Opt-in timed `ask_user_question` tool definition.
|
|
7
|
+
*/
|
|
8
|
+
function validateTimeout(timeout) {
|
|
9
|
+
if (timeout !== -1 && (!Number.isInteger(timeout) || timeout < 1 || timeout > 2147483)) throw new Error("timeout must be -1 or a positive integer up to 2147483 seconds");
|
|
10
|
+
return timeout;
|
|
11
|
+
}
|
|
12
|
+
function validateQuestionIds(questions) {
|
|
13
|
+
const ids = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const question of questions) {
|
|
15
|
+
if (ids.has(question.id)) throw new Error(`question id ${JSON.stringify(question.id)} must be unique within this call`);
|
|
16
|
+
ids.add(question.id);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const description$1 = "Ask brief, direct, self-contained questions about missing information, preferences, or decisions. Use user-facing terms; assume no knowledge of background work or internal names. Use distinct stable question IDs. A submitted skipped question is an answer item with empty selected and no custom; pending instead means no answer batch arrived before the timeout and the user can still answer.";
|
|
20
|
+
/**
|
|
21
|
+
* Instruction the pending result carries in its `message` field. It is a field
|
|
22
|
+
* of the result value rather than prose beside it because the recorded result
|
|
23
|
+
* text is read back as one JSON object: the `userQuestions` projection decides
|
|
24
|
+
* from it that the call stays answerable, and the Client question row decides
|
|
25
|
+
* from it that the recorded result is the timeout, not an answer batch.
|
|
26
|
+
*/
|
|
27
|
+
const pendingNotice = "No answer batch arrived before the timeout. This is pending, not a skipped answer. Continue useful independent work. The user can still answer; their reply will be a user message identified as answer_to_pending_question with this callId and the original questions. Do not treat this as permission.";
|
|
28
|
+
/**
|
|
29
|
+
* Translate the model schema into the service request without changing ownership.
|
|
30
|
+
* @param questions - Model-supplied questions in tool-schema form.
|
|
31
|
+
* @param exec - Execution context that owns the agent and cancellation signal.
|
|
32
|
+
* @returns The corresponding user-question service request.
|
|
33
|
+
*/
|
|
34
|
+
function questionRequest(questions, exec) {
|
|
35
|
+
return {
|
|
36
|
+
questions: questions.map((question) => ({
|
|
37
|
+
id: question.id,
|
|
38
|
+
question: question.question,
|
|
39
|
+
...question.header !== void 0 ? { header: question.header } : {},
|
|
40
|
+
...question.options !== void 0 ? { options: question.options.map((option) => ({ ...option })) } : {},
|
|
41
|
+
...question.multi_select !== void 0 ? { multiSelect: question.multi_select } : {}
|
|
42
|
+
})),
|
|
43
|
+
...exec.agent !== void 0 ? { agent: exec.agent } : {},
|
|
44
|
+
signal: exec.signal
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Copy service-owned answer arrays into the model-facing result.
|
|
49
|
+
* @param result - Answer returned by the user-question service.
|
|
50
|
+
* @returns A detached model-facing answer payload.
|
|
51
|
+
*/
|
|
52
|
+
function answerResult(result) {
|
|
53
|
+
return { answers: result.answers.map((answer) => ({
|
|
54
|
+
id: answer.id,
|
|
55
|
+
selected: [...answer.selected],
|
|
56
|
+
...answer.custom !== void 0 ? { custom: answer.custom } : {}
|
|
57
|
+
})) };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Register the opt-in timed tool definition.
|
|
61
|
+
* @param ctx - Agent-scoped context receiving the tool.
|
|
62
|
+
* @param timeout - Default foreground wait in seconds.
|
|
63
|
+
*/
|
|
64
|
+
function registerTimedAskUser(ctx, timeout = 120) {
|
|
65
|
+
const defaultTimeout = validateTimeout(timeout);
|
|
66
|
+
ctx.tools.register(defineTool({
|
|
67
|
+
name: "ask_user_question",
|
|
68
|
+
description: description$1,
|
|
69
|
+
parameters: {
|
|
70
|
+
questions: {
|
|
71
|
+
type: "array",
|
|
72
|
+
required: true,
|
|
73
|
+
description: "Questions to ask the user.",
|
|
74
|
+
items: {
|
|
75
|
+
type: "object",
|
|
76
|
+
additionalProperties: true,
|
|
77
|
+
properties: {
|
|
78
|
+
id: {
|
|
79
|
+
type: "string",
|
|
80
|
+
required: true,
|
|
81
|
+
description: "Stable id for this question; echoed in the answer."
|
|
82
|
+
},
|
|
83
|
+
question: {
|
|
84
|
+
type: "string",
|
|
85
|
+
required: true,
|
|
86
|
+
description: "The specific question to ask the user."
|
|
87
|
+
},
|
|
88
|
+
header: {
|
|
89
|
+
type: "string",
|
|
90
|
+
description: "Optional short heading for the question, such as \"Confirm\" or \"Choose Mode\"."
|
|
91
|
+
},
|
|
92
|
+
options: {
|
|
93
|
+
type: "array",
|
|
94
|
+
description: "Optional choices to show the user. If you recommend one, put it first and append \"(Recommended)\" to that label.",
|
|
95
|
+
items: {
|
|
96
|
+
type: "object",
|
|
97
|
+
additionalProperties: true,
|
|
98
|
+
properties: {
|
|
99
|
+
label: {
|
|
100
|
+
type: "string",
|
|
101
|
+
required: true,
|
|
102
|
+
description: "Short user-facing option label."
|
|
103
|
+
},
|
|
104
|
+
description: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "One sentence explaining the tradeoff or impact."
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
multi_select: {
|
|
112
|
+
type: "boolean",
|
|
113
|
+
description: "Whether the user may select more than one option. Defaults to false."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
[TIMED_WAIT_PARAMETER]: {
|
|
119
|
+
type: "integer",
|
|
120
|
+
description: `Wait seconds for the entire batch (default ${defaultTimeout}); omit unless the user specifies a duration. Use -1 only when an answer is required before proceeding.`
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
output: {
|
|
124
|
+
schema: { oneOf: [{
|
|
125
|
+
type: "object",
|
|
126
|
+
additionalProperties: false,
|
|
127
|
+
properties: {
|
|
128
|
+
pending: {
|
|
129
|
+
type: "boolean",
|
|
130
|
+
enum: [true],
|
|
131
|
+
required: true,
|
|
132
|
+
description: "True when the foreground wait expired before the user submitted an answer batch. The questions remain answerable; this is not a skipped answer."
|
|
133
|
+
},
|
|
134
|
+
callId: {
|
|
135
|
+
type: "string",
|
|
136
|
+
required: true,
|
|
137
|
+
description: "Tool call whose unanswered questions remain pending."
|
|
138
|
+
},
|
|
139
|
+
message: {
|
|
140
|
+
type: "string",
|
|
141
|
+
required: true,
|
|
142
|
+
description: "How to continue while the questions stay answerable."
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}, {
|
|
146
|
+
type: "object",
|
|
147
|
+
additionalProperties: false,
|
|
148
|
+
properties: { answers: {
|
|
149
|
+
type: "array",
|
|
150
|
+
required: true,
|
|
151
|
+
description: "Submitted answer batch with one item per question. A skipped question has empty selected and no custom; unlike pending, the user has completed the batch.",
|
|
152
|
+
items: {
|
|
153
|
+
type: "object",
|
|
154
|
+
additionalProperties: false,
|
|
155
|
+
properties: {
|
|
156
|
+
id: {
|
|
157
|
+
type: "string",
|
|
158
|
+
required: true,
|
|
159
|
+
description: "Stable question id echoed from the request."
|
|
160
|
+
},
|
|
161
|
+
selected: {
|
|
162
|
+
type: "array",
|
|
163
|
+
required: true,
|
|
164
|
+
items: { type: "string" },
|
|
165
|
+
description: "Selected option labels. Empty with no custom means the user explicitly skipped this question."
|
|
166
|
+
},
|
|
167
|
+
custom: {
|
|
168
|
+
type: "string",
|
|
169
|
+
description: "Optional free-form answer; omitted for a skipped question."
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} }
|
|
174
|
+
}] },
|
|
175
|
+
render: (_args, value) => [{
|
|
176
|
+
type: "text",
|
|
177
|
+
text: JSON.stringify(value)
|
|
178
|
+
}]
|
|
179
|
+
},
|
|
180
|
+
async execute(args, exec) {
|
|
181
|
+
const timeout = validateTimeout(args.timeout ?? defaultTimeout);
|
|
182
|
+
validateQuestionIds(args.questions);
|
|
183
|
+
const request = questionRequest(args.questions, exec);
|
|
184
|
+
if (timeout !== -1) {
|
|
185
|
+
if (exec.agent === void 0) throw new Error("timed questions require a live agent");
|
|
186
|
+
const result = await ctx.userQuestions.askTimed({
|
|
187
|
+
...request,
|
|
188
|
+
agent: exec.agent
|
|
189
|
+
}, exec.callId, timeout * 1e3);
|
|
190
|
+
return "pending" in result ? {
|
|
191
|
+
...result,
|
|
192
|
+
message: pendingNotice
|
|
193
|
+
} : answerResult(result);
|
|
194
|
+
}
|
|
195
|
+
return answerResult(await ctx.userQuestions.ask({
|
|
196
|
+
...request,
|
|
197
|
+
wait: { callId: exec.callId }
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
3
203
|
//#region lib/types/index.js
|
|
4
204
|
/**
|
|
5
205
|
* Model-facing Consumer of the `ctx.userQuestions` capability seam.
|
|
@@ -8,10 +208,18 @@ import "@deepseek-ai/dsh-user-questions";
|
|
|
8
208
|
*
|
|
9
209
|
* @module @deepseek-ai/dsh-tool-ask-user
|
|
10
210
|
*/
|
|
211
|
+
const Config = z.object({
|
|
212
|
+
mode: z.union(["legacy", "timed"]).default("legacy"),
|
|
213
|
+
timeout: z.union([-1, z.number().step(1).min(1).max(2147483)]).default(120)
|
|
214
|
+
});
|
|
11
215
|
const name = "tool-ask-user";
|
|
12
216
|
const inject = ["tools", "userQuestions"];
|
|
13
217
|
const description = "Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding.";
|
|
14
|
-
function apply(ctx) {
|
|
218
|
+
function apply(ctx, config = {}) {
|
|
219
|
+
if (config.mode === "timed") {
|
|
220
|
+
registerTimedAskUser(ctx, config.timeout);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
15
223
|
ctx.tools.register(defineTool({
|
|
16
224
|
name: "ask_user_question",
|
|
17
225
|
description,
|
|
@@ -113,4 +321,4 @@ function apply(ctx) {
|
|
|
113
321
|
}));
|
|
114
322
|
}
|
|
115
323
|
//#endregion
|
|
116
|
-
export { apply, inject, name };
|
|
324
|
+
export { Config, apply, inject, name };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -6,8 +6,17 @@
|
|
|
6
6
|
* @module @deepseek-ai/dsh-tool-ask-user
|
|
7
7
|
*/
|
|
8
8
|
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
+
import z from '@deepseek-ai/schemastery';
|
|
9
10
|
import '@deepseek-ai/dsh-user-questions';
|
|
11
|
+
/** Cordis row selecting the tool schema and its default foreground wait. */
|
|
12
|
+
export interface Config {
|
|
13
|
+
/** Tool definition selected by this Cordis row. Defaults to the blocking legacy tool. */
|
|
14
|
+
mode?: 'legacy' | 'timed';
|
|
15
|
+
/** Foreground wait before automatic continuation. Defaults to 120 seconds. */
|
|
16
|
+
timeout?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const Config: z<Config>;
|
|
10
19
|
export declare const name = "tool-ask-user";
|
|
11
20
|
export declare const inject: string[];
|
|
12
|
-
export declare function apply(ctx: Context): void;
|
|
21
|
+
export declare function apply(ctx: Context, config?: Config): void;
|
|
13
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in timed `ask_user_question` tool definition.
|
|
3
|
+
*/
|
|
4
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
5
|
+
/**
|
|
6
|
+
* Register the opt-in timed tool definition.
|
|
7
|
+
* @param ctx - Agent-scoped context receiving the tool.
|
|
8
|
+
* @param timeout - Default foreground wait in seconds.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerTimedAskUser(ctx: Context, timeout?: number): void;
|
|
11
|
+
//# sourceMappingURL=timed.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-tool-ask-user",
|
|
3
3
|
"description": "Model-facing ask_user_question tool over the ctx.userQuestions seam",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,17 +27,20 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@deepseek-ai/dsh-agent": "0.
|
|
31
|
-
"@deepseek-ai/dsh-tools": "0.
|
|
32
|
-
"@deepseek-ai/dsh-user-questions": "0.
|
|
30
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
31
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
32
|
+
"@deepseek-ai/dsh-user-questions": "0.2.0-rc.2",
|
|
33
33
|
"@deepseek-ai/cordis": "~4.0.4"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@deepseek-ai/dsh-
|
|
37
|
-
"@deepseek-ai/dsh-
|
|
38
|
-
"@deepseek-ai/dsh-system-prompt": "0.
|
|
39
|
-
"@deepseek-ai/dsh-tools": "0.
|
|
40
|
-
"@deepseek-ai/dsh-user-questions": "0.
|
|
36
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
37
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
38
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
|
|
39
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
40
|
+
"@deepseek-ai/dsh-user-questions": "0.2.0-rc.2",
|
|
41
41
|
"@deepseek-ai/cordis": "~4.0.4"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@deepseek-ai/schemastery": "~3.18.4"
|
|
42
45
|
}
|
|
43
46
|
}
|