@hasna-internal/kai-tool-ask-user 0.1.1-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE 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,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/interaction/tool-ask-user/README.md
5
+ README.md: bf2ba369fca36c462c6155ed2354ab3d0e930dd0
6
+ README.zh.md: 067235d84d70d2f3278fd39f5c01f130373d3ac2
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @hasna-internal/kai-tool-ask-user
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Model-facing `ask_user_question` tool over `ctx.userQuestions`. It lets the model ask the human a concise question when it needs confirmation, a choice, or missing information before continuing.
6
+
7
+ ## Tool
8
+
9
+ `ask_user_question` accepts:
10
+
11
+ - `questions` — required non-empty array of question objects.
12
+ - `id` — required stable id on each question, echoed in the answer.
13
+ - `question` — required question text for each question.
14
+ - `header` — optional short heading.
15
+ - `options` — optional choices with `label` and `description`. If recommending a choice, put it first and append `(Recommended)` to that label.
16
+ - `multi_select` — whether that question may return more than one selected option.
17
+
18
+ The tool calls `ctx.userQuestions.ask()` and returns canonical `{ answers: [{ id, selected, custom? }] }`. `selected` contains option labels; `custom` carries a free-form answer, supplementing `selected` for a multi-select question and overriding it for a single-select question. The Native renderer preserves the compact JSON text shape `{ "answers": [{ "id": "...", "selected": ["..."], "custom": "..." }] }`.
19
+
20
+ ## Role
21
+
22
+ This is the Consumer package for the user-questions seam. It does not render UI and does not know how input is collected; it only translates model arguments into `AskUserQuestionRequest` and returns the human answer to the agent loop.
23
+
24
+ ## Model Experience
25
+
26
+ ### Tool schema
27
+
28
+ #### What the model sees
29
+
30
+ The model sees the generated [`ask_user_question` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-ask-user), including question ids, prompts, headings, options, and multi-select flags.
31
+
32
+ #### Token effect
33
+
34
+ Fixed schema cost on every request where the tool is visible.
35
+
36
+ #### KV Cache effect
37
+
38
+ Prefix-stable while the definition and visibility are unchanged. Plugin lifecycle or scoped restrictions may invalidate reuse from this schema.
39
+
40
+ ### Tool-call history and result
41
+
42
+ #### What the model sees
43
+
44
+ The model's full questions remain in the assistant tool-call arguments. After the human answers, the next step sees compact JSON in the exact shape `{"answers":[{"id":"<id>","selected":["<label>"],"custom":"<text>"}]}`; `custom` is omitted when unused and `selected` can contain zero, one, or several labels. UI interaction while the call is pending is not model context.
45
+
46
+ #### Token effect
47
+
48
+ Arguments and answer JSON are data-dependent retained tokens; there is no token cost while waiting for the human.
49
+
50
+ #### KV Cache effect
51
+
52
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
53
+
54
+ ## Known Limitations and Deferred Work
55
+
56
+ - **A pending question blocks the tool call until the human answers** — the tool declares no `timeout-policy` budget; cancellation rides the turn's `exec.signal` only.
57
+ - **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.
58
+ - **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.
package/README.zh.md ADDED
@@ -0,0 +1,58 @@
1
+ # @hasna-internal/kai-tool-ask-user
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 模型侧 `ask_user_question` 工具,基于 `ctx.userQuestions` 实现。当模型需要确认、选择结果或缺失的信息才能继续时,它可以借此向用户提出简明问题。
6
+
7
+ ## 工具
8
+
9
+ `ask_user_question` 接受以下参数:
10
+
11
+ - `questions`:必填的非空问题对象数组。
12
+ - `id`:每个问题必填的稳定 id,会原样包含在回答中。
13
+ - `question`:每个问题必填的问题文本。
14
+ - `header`:可选的简短标题。
15
+ - `options`:可选选项,包含 `label` 和 `description`。如需推荐某个选项,请将其置于首位,并在该标签末尾追加 `(Recommended)`。
16
+ - `multi_select`:该问题是否可以返回多个选中的选项。
17
+
18
+ 工具调用 `ctx.userQuestions.ask()`,并返回规范的 `{ answers: [{ id, selected, custom? }] }`。`selected` 包含选项标签;`custom` 携带自由填写的回答,对于多选题会补充 `selected`,对于单选题则会覆盖它。Native 渲染器会保留紧凑的 JSON 文本形式 `{ "answers": [{ "id": "...", "selected": ["..."], "custom": "..." }] }`。
19
+
20
+ ## 职责
21
+
22
+ 此包是用户交互 seam 的Consumer 包。它不渲染 UI,也不了解输入的收集方式;它只将模型参数转换为 `AskUserQuestionRequest`,并把用户回答返回给 agent loop(智能体循环)。
23
+
24
+ ## 模型体验
25
+
26
+ ### 工具 schema
27
+
28
+ #### 模型看到的内容
29
+
30
+ 模型会看到生成的 [`ask_user_question` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-ask-user),其中包含问题 id、提示语、标题、选项和多选标志。
31
+
32
+ #### Token 影响
33
+
34
+ 工具可见时,每个请求都会产生固定的 schema token 开销。
35
+
36
+ #### KV Cache 影响
37
+
38
+ 只要定义和可见性保持不变,前缀即可稳定复用。插件生命周期变化或作用域限制可能会使从此 schema 起的缓存复用失效。
39
+
40
+ ### 工具调用历史与结果
41
+
42
+ #### 模型看到的内容
43
+
44
+ 模型提出的完整问题保留在 assistant 工具调用参数中。用户回答后,下一步会看到精确采用 `{"answers":[{"id":"<id>","selected":["<label>"],"custom":"<text>"}]}` 形式的紧凑 JSON;不使用 `custom` 时会省略该字段,`selected` 可以包含零个、一个或多个标签。调用等待期间的 UI 交互不属于模型上下文。
45
+
46
+ #### Token 影响
47
+
48
+ 参数和回答 JSON 是依数据而定的保留 token;等待用户时不会产生 token 开销。
49
+
50
+ #### KV Cache 影响
51
+
52
+ 仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
53
+
54
+ ## 已知限制与暂缓事项
55
+
56
+ - **待处理问题会阻塞工具调用,直至用户作答**:该工具未声明 `timeout-policy` 预算;取消仅沿用当前轮次的 `exec.signal`。
57
+ - **运行时中归属于其他 agent 的 subagent 不能向用户提问**:`ask_user_question` 会以 `DELEGATED_CALLER` 拒绝归属于另一个 agent 的存活子级;该子级必须在最终结果中包含尚未解决的问题或决策。持久谱系不能决定这一边界,因此带有谱系的会话恢复为运行时根后可以正常提问。
58
+ - **Native 回答渲染为 JSON 文本**:规范值仍为结构化数据,但模型侧结果使用紧凑 JSON,而非更丰富的内容块词汇。
package/lib/index.js ADDED
@@ -0,0 +1,116 @@
1
+ import { defineTool } from "@hasna-internal/kai-tools";
2
+ import "@hasna-internal/kai-user-questions";
3
+ //#region lib/types/index.js
4
+ /**
5
+ * Model-facing Consumer of the `ctx.userQuestions` capability seam.
6
+ * The tool pauses until a UI provider returns a human answer, then feeds that
7
+ * answer back into the agent loop as an ordinary tool result.
8
+ *
9
+ * @module @hasna-internal/kai-tool-ask-user
10
+ */
11
+ const name = "tool-ask-user";
12
+ const inject = ["tools", "userQuestions"];
13
+ const description = "Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding. Send one or more questions, each with a stable id that will be echoed in the answer.";
14
+ function apply(ctx) {
15
+ ctx.tools.register(defineTool({
16
+ name: "ask_user_question",
17
+ description,
18
+ parameters: { questions: {
19
+ type: "array",
20
+ required: true,
21
+ description: "Questions to ask the user before continuing.",
22
+ items: {
23
+ type: "object",
24
+ additionalProperties: true,
25
+ properties: {
26
+ id: {
27
+ type: "string",
28
+ required: true,
29
+ description: "Stable id for this question; echoed in the answer."
30
+ },
31
+ question: {
32
+ type: "string",
33
+ required: true,
34
+ description: "The specific question to ask the user."
35
+ },
36
+ header: {
37
+ type: "string",
38
+ description: "Optional short heading for the question, such as \"Confirm\" or \"Choose Mode\"."
39
+ },
40
+ options: {
41
+ type: "array",
42
+ description: "Optional choices to show the user. If you recommend one, put it first and append \"(Recommended)\" to that label.",
43
+ items: {
44
+ type: "object",
45
+ additionalProperties: true,
46
+ properties: {
47
+ label: {
48
+ type: "string",
49
+ required: true,
50
+ description: "Short user-facing option label."
51
+ },
52
+ description: {
53
+ type: "string",
54
+ description: "One sentence explaining the tradeoff or impact."
55
+ }
56
+ }
57
+ }
58
+ },
59
+ multi_select: {
60
+ type: "boolean",
61
+ description: "Whether the user may select more than one option. Defaults to false."
62
+ }
63
+ }
64
+ }
65
+ } },
66
+ output: {
67
+ schema: {
68
+ type: "object",
69
+ additionalProperties: false,
70
+ properties: { answers: {
71
+ type: "array",
72
+ required: true,
73
+ items: {
74
+ type: "object",
75
+ additionalProperties: false,
76
+ properties: {
77
+ id: {
78
+ type: "string",
79
+ required: true
80
+ },
81
+ selected: {
82
+ type: "array",
83
+ required: true,
84
+ items: { type: "string" }
85
+ },
86
+ custom: { type: "string" }
87
+ }
88
+ }
89
+ } }
90
+ },
91
+ render: (_args, value) => [{
92
+ type: "text",
93
+ text: JSON.stringify(value)
94
+ }]
95
+ },
96
+ async execute(args, exec) {
97
+ return { answers: (await ctx.userQuestions.ask({
98
+ questions: args.questions.map((question) => ({
99
+ id: question.id,
100
+ question: question.question,
101
+ ...question.header !== void 0 ? { header: question.header } : {},
102
+ ...question.options !== void 0 ? { options: question.options } : {},
103
+ ...question.multi_select !== void 0 ? { multiSelect: question.multi_select } : {}
104
+ })),
105
+ ...exec.agent !== void 0 ? { agent: exec.agent } : {},
106
+ signal: exec.signal
107
+ })).answers.map((answer) => ({
108
+ id: answer.id,
109
+ selected: [...answer.selected],
110
+ ...answer.custom !== void 0 ? { custom: answer.custom } : {}
111
+ })) };
112
+ }
113
+ }));
114
+ }
115
+ //#endregion
116
+ export { apply, inject, name };
@@ -0,0 +1,23 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@hasna-internal/kai-tool-ask-user`.
4
+ * @module @hasna-internal/kai-tool-ask-user/invariant
5
+ */
6
+ const PACKAGE_NAME = "@hasna-internal/kai-tool-ask-user";
7
+ /** Cordis companion plugin name. */
8
+ const name = "tool-ask-user-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: this model-facing adapter has no independent lifecycle stream; execution
13
+ * relations are owned by the capability seam it calls.
14
+ */
15
+ const install = () => {};
16
+ /**
17
+ * Register this package's invariant companion.
18
+ * @param ctx - Cordis context carrying the invariant service.
19
+ * @returns the installed registration's disposer after setup succeeds.
20
+ */
21
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
+ //#endregion
23
+ export { apply, inject, name };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Model-facing Consumer of the `ctx.userQuestions` capability seam.
3
+ * The tool pauses until a UI provider returns a human answer, then feeds that
4
+ * answer back into the agent loop as an ordinary tool result.
5
+ *
6
+ * @module @hasna-internal/kai-tool-ask-user
7
+ */
8
+ import type { Context } from '@deepseek-ai/cordis';
9
+ import '@hasna-internal/kai-user-questions';
10
+ export declare const name = "tool-ask-user";
11
+ export declare const inject: string[];
12
+ export declare function apply(ctx: Context): void;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@hasna-internal/kai-tool-ask-user`.
3
+ * @module @hasna-internal/kai-tool-ask-user/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "tool-ask-user-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@hasna-internal/kai-tool-ask-user",
3
+ "description": "Model-facing ask_user_question tool over the ctx.userQuestions seam",
4
+ "version": "0.1.1-rc.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/interaction/tool-ask-user"
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
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "lib/index.js",
30
+ "lib/invariant.js",
31
+ "lib/types/**/*.d.ts"
32
+ ],
33
+ "license": "MIT",
34
+ "peerDependencies": {
35
+ "@hasna-internal/kai-agent": "^0.1.1-rc.2",
36
+ "@hasna-internal/kai-tools": "^0.1.1-rc.2",
37
+ "@deepseek-ai/cordis": "^4.0.1",
38
+ "@hasna-internal/kai-invariants": "^0.1.1-rc.2",
39
+ "@hasna-internal/kai-user-questions": "^0.1.1-rc.2"
40
+ },
41
+ "devDependencies": {
42
+ "@hasna-internal/kai-system-prompt": "^0.1.1-rc.2",
43
+ "@hasna-internal/kai-llm": "^0.1.1-rc.2",
44
+ "@hasna-internal/kai-invariants": "^0.1.1-rc.2",
45
+ "@deepseek-ai/cordis": "^4.0.1",
46
+ "@hasna-internal/kai-user-questions": "^0.1.1-rc.2",
47
+ "@hasna-internal/kai-tools": "^0.1.1-rc.2",
48
+ "@hasna-internal/kai-agent": "^0.1.1-rc.2"
49
+ }
50
+ }