nx-kv 0.2.1 → 0.3.0
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/CHANGELOG.md +24 -0
- package/README.md +32 -4
- package/assets/kv-sessiontask/SKILL.md +106 -0
- package/assets/kv-sessiontask/references/sessiontask-workflow.md +127 -0
- package/package.json +5 -3
- package/src/index.js +1 -0
- package/src/modules/sessiontask/index.js +137 -0
- package/src/modules/sessiontask/popup.js +256 -0
- package/src/modules/sessiontask/service.js +476 -0
- package/src/modules/sessiontask/view.jsx +239 -0
- package/src/runtime/registry.js +2 -1
- package/src/web/frontend/registry.js +1 -0
- package/src/web/public/assets/{CliHints-CFhcOLE8.js → CliHints-CDKMmZbP.js} +1 -1
- package/src/web/public/assets/{index-BbN9ozEj.js → index-DW8t26MP.js} +2 -2
- package/src/web/public/assets/view-CLDx6X3W.js +1 -0
- package/src/web/public/assets/{view-CpSFKqN7.js → view-C_qjdm2x.js} +1 -1
- package/src/web/public/assets/{view-BTwd63ln.js → view-CdtYjY9W.js} +1 -1
- package/src/web/public/assets/{view-7P7JQz5n.js → view-CqGxh_0X.js} +1 -1
- package/src/web/public/index.html +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
本文件记录对外可见的变更。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。
|
|
4
4
|
|
|
5
|
+
## [0.3.0] - 2026-10-06
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **新功能域「实时任务输入」(sessiontask)**:在一个对话里通过工具调用实时向
|
|
10
|
+
agent 回填任务,替代在原始输入框打字。agent 完成当前任务、提交结果后,把
|
|
11
|
+
`nx-kv sessiontask wait --summary "<当前现状总结>" --json` 作为本轮**最后一个
|
|
12
|
+
动作**调用——命令阻塞,自动在本机临时起 http 服务器并打开浏览器弹窗;用户针对
|
|
13
|
+
该现状回填后解除阻塞,把新任务作为工具结果返回给 agent 继续,循环往复。
|
|
14
|
+
- **现状总结即等待点 key**:相同现状(逐字一致,忽略首尾/连续空白)复用同一
|
|
15
|
+
等待点,不同现状分散为不同等待点;用户在面板「等待点总览」看清各现状、点选
|
|
16
|
+
**分别回填(分散答复)**。
|
|
17
|
+
- **阻塞超时默认 120 秒(2 分钟)**:未回填则返回 `{status:"timeout"}`、
|
|
18
|
+
exit 0,agent 据此安静收尾结束本轮;超时是业务结果不是错误。
|
|
19
|
+
- **排队语义**:任务可提前投递;等待点非空时 wait 立即取走、不开窗。
|
|
20
|
+
- **纯本机协调**:数据存本地文件(`~/.nx-kv/sessiontasks.json`),
|
|
21
|
+
不依赖 KV 后端、不需登录;路径可用环境变量 `NX_KV_SESSIONTASK_STORE` 覆盖。
|
|
22
|
+
- CRUD 五操作 `sessiontask list / get / add / update / remove` 两端可达(均按
|
|
23
|
+
`--summary` 定位),面板新增「实时任务」页(等待点总览、点选回填、任务表、删除)。
|
|
24
|
+
- **新内置 skill `kv-sessiontask`**:教 agent 四条硬规则——wait 必须在最后一步、
|
|
25
|
+
工具调用超时设 120000ms、wait 必传现状总结作为 key(同现状同句、新现状新句)、
|
|
26
|
+
timeout 即收尾。含完整时序、分散答复与避坑(`references/sessiontask-workflow.md`)。
|
|
27
|
+
安装:`nx-kv skill install kv-sessiontask`;`skill list` 现列出两个 skill。
|
|
28
|
+
|
|
5
29
|
## [0.2.1] - 2026-09-27
|
|
6
30
|
|
|
7
31
|
### Added
|
package/README.md
CHANGED
|
@@ -66,6 +66,30 @@ nx-kv group members 24 # 成员
|
|
|
66
66
|
完整命令表由 action 声明自动生成:`nx-kv help`。
|
|
67
67
|
命令与 HTTP 端点互相可查:`nx-kv routes`,反查用 `nx-kv routes --http "POST /api/todo"`。
|
|
68
68
|
|
|
69
|
+
## 实时任务输入(对话内连续提交)
|
|
70
|
+
|
|
71
|
+
在一个对话里,agent 完成当前任务、提交结果、需要下一条输入时,可调用**阻塞式**
|
|
72
|
+
命令等用户在浏览器回填,替代在原始输入框打字:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# 本轮【最后一步】:阻塞并自动打开浏览器弹窗(工具调用超时需设为 120000ms)
|
|
76
|
+
nx-kv sessiontask wait --summary "<当前现状总结>" --json
|
|
77
|
+
# 成功 → {status:"ok", task:{text,...}};120 秒未回填 → {status:"timeout"}(exit 0)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**现状总结即等待点 key**:同现状用同一句总结复用同一等待点,新现状换新句分散为
|
|
81
|
+
新等待点。弹窗/面板顶部直接显示现状,用户不读对话也能快速了解、点选等待点
|
|
82
|
+
**分别回填(分散答复)**,也可提前排队;等待点非空时 wait 立即取走、不开窗。
|
|
83
|
+
纯本机协调、存本地文件,**不需登录后端**。其余命令:
|
|
84
|
+
`sessiontask list / add / get / update / remove`(均按 `--summary` 定位),
|
|
85
|
+
面板上对应「实时任务」页。
|
|
86
|
+
|
|
87
|
+
配套 skill:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
nx-kv skill install kv-sessiontask # 教 agent 在最后一步阻塞等用户喂任务
|
|
91
|
+
```
|
|
92
|
+
|
|
69
93
|
## ⚠️ 定位一律按内容,不按 id
|
|
70
94
|
|
|
71
95
|
待办的 id **非常容易重复**,两条独立的原因:
|
|
@@ -127,10 +151,11 @@ nx-kv todo get --ref "旧记录" --topic fr # 或按主题收窄
|
|
|
127
151
|
|
|
128
152
|
```
|
|
129
153
|
src/core/ 零业务语义:kvapi(后端客户端) / config(本机配置) / errors / fstree
|
|
130
|
-
src/modules/ 功能域:system / auth / group / todo / bundled
|
|
154
|
+
src/modules/ 功能域:system / auth / group / sessiontask / todo / bundled
|
|
131
155
|
src/runtime/ 装配层:registry / spec / cli / api / server
|
|
132
156
|
src/web/frontend/ React 壳
|
|
133
|
-
assets/nx-kv/
|
|
157
|
+
assets/nx-kv/ 内置 skill:KV 清单操作手册
|
|
158
|
+
assets/kv-sessiontask/ 内置 skill:对话内实时任务接力
|
|
134
159
|
```
|
|
135
160
|
|
|
136
161
|
分层由 `eslint.config.js` 的 `no-restricted-imports` 强制;
|
|
@@ -139,10 +164,13 @@ assets/nx-kv/ 内置 skill(agent 操作手册)
|
|
|
139
164
|
## 给 agent 用
|
|
140
165
|
|
|
141
166
|
```bash
|
|
142
|
-
nx-kv skill install # 装到 ~/.claude/skills/nx-kv
|
|
167
|
+
nx-kv skill install # 装到 ~/.claude/skills/nx-kv(KV 清单操作手册)
|
|
168
|
+
nx-kv skill install kv-sessiontask # 实时任务接力:最后一步阻塞等用户喂任务
|
|
169
|
+
nx-kv skill list # 列出 nx-kv / kv-sessiontask
|
|
143
170
|
```
|
|
144
171
|
|
|
145
|
-
装好后 agent 就能按 skill 里的 SOP
|
|
172
|
+
装好后 agent 就能按 skill 里的 SOP 领任务、读主题上下文、完成后回填结果;
|
|
173
|
+
或在一个对话里靠 `sessiontask wait` 阻塞弹窗、连续接收用户任务。
|
|
146
174
|
|
|
147
175
|
## License
|
|
148
176
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kv-sessiontask
|
|
3
|
+
description: 在一个对话里通过工具调用实时向 agent 回填任务(本机人机接力),替代在原始输入框打字——agent 完成当前任务、提交结果后,把 nx-kv sessiontask wait --summary "<当前现状总结>" 作为本轮最后一步强制调用;现状总结作为等待点 key,命令阻塞并自动打开浏览器弹窗,用户在面板看清现状、针对该现状回填后解除阻塞、把新任务作为工具结果返回,agent 继续;不同现状分散为不同等待点、可分别回填;120 秒未回填则超时收尾。触发词:实时任务、下一条任务、继续给任务、连续提交任务、补充任务、回填任务、现状总结、在网页/弹窗里给任务、任务表提交、不想用输入框、用工具调用实现用户输入、阻塞等待、挂起等用户、人机接力、分散答复、sessiontask、session wait。凡是「agent 不结束本轮、靠一个阻塞工具调用等用户在浏览器里继续喂任务」的场景,都走本 skill。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# kv-sessiontask — 对话内实时任务接力
|
|
7
|
+
|
|
8
|
+
本质是一个循环:**agent 提交结果 → CLI 阻塞 → 用户在 web 看清现状、回填新任务 →
|
|
9
|
+
agent 接收并继续**。在一个对话里连续给任务,不靠原始输入框。
|
|
10
|
+
|
|
11
|
+
agent 完成当前任务、需要下一条输入时,调用一个**阻塞式**工具
|
|
12
|
+
`nx-kv sessiontask wait`,并带上一句**当前现状总结**;它自动打开浏览器弹窗。
|
|
13
|
+
用户在面板/弹窗一眼看到现状,针对该现状回填,阻塞解除、新任务作为工具结果返回,
|
|
14
|
+
agent 接着干。如此循环。
|
|
15
|
+
|
|
16
|
+
> 纯本机协调:弹窗由 wait 进程临时起的 http 服务器直出,数据存本机文件,
|
|
17
|
+
> **不需要登录 KV 后端**。
|
|
18
|
+
|
|
19
|
+
## 四条硬规则(最重要)
|
|
20
|
+
|
|
21
|
+
1. **wait 必须是本轮最后一个动作。** 把它安排在所有分析与工具调用之后(阶段
|
|
22
|
+
结果已经通过正常输出提交);调用之后不要再安排任何别的工具、输出或推理——
|
|
23
|
+
它会阻塞,直到拿到新任务或超时。
|
|
24
|
+
2. **这次工具调用的超时设为 `120000` ms(2 分钟)。** 与 CLI 的阻塞上限一致,
|
|
25
|
+
避免宿主在等待中途把调用杀掉。
|
|
26
|
+
3. **wait 必须带 `--summary "<当前现状总结>"`,现状总结就是等待点的 key。**
|
|
27
|
+
- 回到**同一个现状**就用**同一句**总结(逐字一致,忽略首尾/连续空白差异),
|
|
28
|
+
从而复用同一个等待点;
|
|
29
|
+
- 进入**新的现状**就换一句新总结,自然分散成新的等待点。
|
|
30
|
+
- 总结写**当前做到哪、卡在哪、等用户决定什么**,让用户不读对话也能在面板
|
|
31
|
+
快速了解现状。
|
|
32
|
+
4. **返回 `status:"timeout"` 时直接收尾。** 用户 2 分钟内没回填,就不要再追问、
|
|
33
|
+
不要再次调用 wait,把当前结论交代清楚、结束本轮即可。
|
|
34
|
+
|
|
35
|
+
## 核心命令
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 阻塞等待用户针对该现状回填(agent 本轮最后一步;务必加 --json)
|
|
39
|
+
nx-kv sessiontask wait --summary "<当前现状总结>" --json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
返回形状:
|
|
43
|
+
|
|
44
|
+
| 返回 | 含义 | agent 怎么做 |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `{status:"ok", queued:true, task}` | 取到用户**提前排队**的回填(弹窗未开) | 直接执行 task |
|
|
47
|
+
| `{status:"ok", delivered:true, task}` | 用户在弹窗/面板**实时回填** | 执行 task |
|
|
48
|
+
| `{status:"timeout", key, summary, timeoutMs}` | 120 秒未收到回填 | 收尾、结束本轮 |
|
|
49
|
+
|
|
50
|
+
成功结果里带 `key` 与 `summary`;`task` 形如
|
|
51
|
+
`{id, text, status, createdAt, consumedAt}`,**任务内容在 `task.text`**。
|
|
52
|
+
|
|
53
|
+
## 一次循环长什么样
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
agent:干活 → 提交阶段结果
|
|
57
|
+
→ 最后一步:nx-kv sessiontask wait --summary "已完成登录,等待选择工作空间" --json
|
|
58
|
+
(阻塞,弹窗/面板顶部显示这句现状)
|
|
59
|
+
用户:看清现状 → 回填「选择工作空间 A」→ 提交
|
|
60
|
+
agent:工具结果拿到 task.text → 继续干活、提交结果
|
|
61
|
+
→ 新现状再 wait(同现状用同一句,新现状换新句)……(循环)
|
|
62
|
+
若 120 秒没回填:wait 返回 timeout → agent 结束本轮
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 分散答复:多个等待点并存
|
|
66
|
+
|
|
67
|
+
- 每个不同的现状总结 = 一个等待点;面板「等待点总览」把它们并排展示,每张卡
|
|
68
|
+
显示现状、等待状态/倒计时、待领取数。
|
|
69
|
+
- 用户点选任一等待点,即可**分别回填**(分散答复);也可趁 agent 在忙,给几个
|
|
70
|
+
等待点提前排队。
|
|
71
|
+
- agent 用对应现状调用 wait 时取走该等待点的任务,互不串。
|
|
72
|
+
|
|
73
|
+
## 配套命令(弹窗背后同一套,agent 一般不直接用)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
nx-kv sessiontask list # 等待点总览
|
|
77
|
+
nx-kv sessiontask add "<回填内容>" --summary "<现状>" # 针对现状排队/投递
|
|
78
|
+
nx-kv sessiontask get --id <n> --summary "<现状>" # 查看单条
|
|
79
|
+
nx-kv sessiontask update --id <n> --text "<新内容>" --summary "<现状>"
|
|
80
|
+
nx-kv sessiontask remove --id <n> --summary "<现状>" # 删除
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
常驻面板(`nx-kv serve`)的「实时任务」页就是等待点总览,可直接点选回填;
|
|
84
|
+
wait 同样能取走(跨进程由轮询兜底)。
|
|
85
|
+
|
|
86
|
+
## 先判断你要做哪件事
|
|
87
|
+
|
|
88
|
+
| 你要做的事 | 读哪份 |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| 要在对话里阻塞等用户回填,需要**完整时序、现状 key、排队语义、避坑** | [[sessiontask-workflow]] |
|
|
91
|
+
| 只是查命令与返回形状 | 本页即可 |
|
|
92
|
+
|
|
93
|
+
## 与 nx-kv skill 的分工
|
|
94
|
+
|
|
95
|
+
- **nx-kv**:操作 KV **后端**上的清单(todo,需登录、按 groupId 隔离)。
|
|
96
|
+
- **kv-sessiontask(本 skill)**:**本机**实时人机接力,把用户在浏览器的回填
|
|
97
|
+
变成阻塞工具的返回,现状即 key,不碰后端、不需登录。
|
|
98
|
+
|
|
99
|
+
两者可同时安装,各管一段,不冲突。
|
|
100
|
+
|
|
101
|
+
## 安装
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
nx-kv skill install kv-sessiontask # 装到 ~/.claude/skills/kv-sessiontask
|
|
105
|
+
nx-kv skill list # 列出 nx-kv / kv-sessiontask
|
|
106
|
+
```
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# sessiontask-workflow — 阻塞等待用户回填任务的完整 SOP
|
|
2
|
+
|
|
3
|
+
> 归属 [[kv-sessiontask]]。当你要在一个对话里**不结束本轮**、靠一个阻塞工具
|
|
4
|
+
> 调用等用户在浏览器里继续回填任务时,按本 SOP 走。
|
|
5
|
+
|
|
6
|
+
## 一、端到端时序
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
1. agent 正常干活(分析、读写文件、调用其它工具)。
|
|
10
|
+
2. 当前任务做完、提交阶段结果,需要用户给下一条时:
|
|
11
|
+
→ 把「nx-kv sessiontask wait --summary "<当前现状总结>" --json」
|
|
12
|
+
作为本轮【最后一个动作】调用;工具调用超时设为 120000 ms。
|
|
13
|
+
3. wait 的行为:
|
|
14
|
+
a. 若该现状的等待点已有用户提前排队的 pending → 立即取走、不开窗、不阻塞;
|
|
15
|
+
b. 否则在本机临时起一个 http 服务器(127.0.0.1,随机端口),
|
|
16
|
+
自动打开浏览器弹窗,然后阻塞。
|
|
17
|
+
4. 用户在弹窗/面板看清现状,回填新任务 → 提交(Enter 或按钮)。
|
|
18
|
+
5. wait 解除阻塞,stdout(--json) 返回 {status:"ok", task},进程退出。
|
|
19
|
+
6. agent 从工具结果读 task.text,继续干活、提交结果;做完回到第 2 步——循环。
|
|
20
|
+
7. 若 120 秒内没有回填:wait 返回 {status:"timeout"}(exit 0),
|
|
21
|
+
agent 不再等待,收尾并结束本轮。
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
关键点:**第 2 步之后、工具结果回来之前,agent 不产生任何其它动作。** 用户的
|
|
25
|
+
回填正是通过这个工具结果进入对话的,等于用工具调用替代了在原始输入框打字,
|
|
26
|
+
agent 这一轮不会因为「等输入」而提前结束。
|
|
27
|
+
|
|
28
|
+
## 二、现状总结就是 key(怎么写)
|
|
29
|
+
|
|
30
|
+
wait 必带一句当前现状总结,它被归一化(折叠首尾/连续空白)后哈希成等待点 key:
|
|
31
|
+
|
|
32
|
+
- **回到同一个现状 → 用同一句总结(逐字一致)**,复用同一个等待点;首尾空格、
|
|
33
|
+
连续空格的差异会被忽略,不影响 key。
|
|
34
|
+
- **进入新的现状 → 换一句新总结**,自然形成新的等待点。
|
|
35
|
+
- 总结要让用户**不读整段对话也能快速了解现状**,建议包含三要素:
|
|
36
|
+
1. 已经做到哪一步 / 产出了什么;
|
|
37
|
+
2. 当前卡在哪、有哪些可选项;
|
|
38
|
+
3. 具体等用户决定或补充什么。
|
|
39
|
+
- 一句话为宜,≤200 字。示例:
|
|
40
|
+
- `已完成登录,等待选择工作空间`
|
|
41
|
+
- `已生成 3 个方案(A/B/C),等待你选一个再细化`
|
|
42
|
+
- `面板已能提交,但删除还没接,等待确认是否现在补`
|
|
43
|
+
|
|
44
|
+
展示给用户的始终是现状原文;key 只是内部标识,agent 不需要自己算 key。
|
|
45
|
+
|
|
46
|
+
## 三、排队语义(用户可以提前投递)
|
|
47
|
+
|
|
48
|
+
- 用户在弹窗、常驻面板「实时任务」页或用 `sessiontask add` 提前提交的回填,会
|
|
49
|
+
以 `pending` 进入对应现状的等待点。
|
|
50
|
+
- wait 总是取走该等待点**最早的一条 pending**,标记为已领取(consumed)后返回。
|
|
51
|
+
- 所以:
|
|
52
|
+
- 等待点非空 → wait 立刻返回(`queued:true`),连弹窗都不打开;
|
|
53
|
+
- 等待点为空 → 才开窗阻塞(`delivered:true`)。
|
|
54
|
+
- 已领取的任务保留为历史(状态「已领取」),不会被二次取走。
|
|
55
|
+
|
|
56
|
+
这让「用户趁 agent 还在忙,先把后面几条排好」成为可能:agent 每次 wait 都能
|
|
57
|
+
立刻拿到下一条,不打断节奏。
|
|
58
|
+
|
|
59
|
+
## 四、返回形状与退出码
|
|
60
|
+
|
|
61
|
+
成功(exit 0):
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"status": "ok",
|
|
66
|
+
"key": "3f9a2c1d7e8b4a10",
|
|
67
|
+
"summary": "已完成登录,等待选择工作空间",
|
|
68
|
+
"task": { "id": 3, "text": "选择工作空间 A", "status": "consumed",
|
|
69
|
+
"createdAt": "2026-10-06 10:00:00", "consumedAt": "2026-10-06 10:00:04" },
|
|
70
|
+
"delivered": true,
|
|
71
|
+
"queued": false
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- `delivered:true` = 实时回填;`queued:true` = 取到提前排队的任务(二者互斥)。
|
|
76
|
+
- **任务内容永远读 `task.text`**,多行换行原样保留。
|
|
77
|
+
|
|
78
|
+
超时(exit 0,业务结果而非错误):
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{ "status": "timeout", "key": "3f9a2c1d7e8b4a10",
|
|
82
|
+
"summary": "已完成登录,等待选择工作空间", "timeoutMs": 120000 }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- 超时**不是失败**:退出码 0,按「用户没继续」收尾,不要按错误处理。
|
|
86
|
+
- 参数非法(如缺 `--summary`、缺 `--id`)才是错误(exit 1,文本带「用法」),
|
|
87
|
+
按宿主失败流程处理。
|
|
88
|
+
|
|
89
|
+
## 五、弹窗任务表怎么用(可转述给用户)
|
|
90
|
+
|
|
91
|
+
- 顶部「当前现状」直接显示 agent 传入的现状总结,先看这句再决定回填什么。
|
|
92
|
+
- 大输入框:回填下一条任务;**Enter 提交,Shift+Enter 换行**。
|
|
93
|
+
- 状态栏倒计时显示还会等多久;归零未回填则本轮自动结束。
|
|
94
|
+
- 下方是该现状的任务表,状态分「待领取 / 已领取」,可删除。
|
|
95
|
+
- 提交后显示「已回填给 agent,正在返回」即说明 wait 已取走、窗口可关闭;agent
|
|
96
|
+
做完会再开下一个弹窗。
|
|
97
|
+
|
|
98
|
+
## 六、分散答复:多个等待点并存
|
|
99
|
+
|
|
100
|
+
- 不同现状总结 → 不同等待点。常驻面板「等待点总览」把它们并排展示,每张卡显示
|
|
101
|
+
现状、等待状态/倒计时、待领取数。
|
|
102
|
+
- 用户点选任一等待点即可**分别回填**,也可给多个等待点提前排队——答复被分散到
|
|
103
|
+
各现状下,而不是挤在一个队列。
|
|
104
|
+
- agent 用对应现状调 wait 时只取该等待点的任务,互不串。
|
|
105
|
+
|
|
106
|
+
## 七、常见错误与避坑
|
|
107
|
+
|
|
108
|
+
| 错误做法 | 后果 | 正确做法 |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| wait 后又安排别的工具/推理 | wait 阻塞,后续动作被卡住、顺序全乱 | wait 严格放在最后一步,之后只处理返回 |
|
|
111
|
+
| 工具超时没设/设太短(如 60s) | 宿主在回填前杀掉调用,任务中断 | 工具超时设 `120000` ms |
|
|
112
|
+
| wait 不带 / 空着 --summary | 无法定位等待点,命令报错退出 | 每次 wait 都写一句现状总结 |
|
|
113
|
+
| 同一现状却每次换措辞 | 被当成多个等待点,排队任务取不到 | 同现状用同一句总结,新现状才换新句 |
|
|
114
|
+
| 把不同阶段都写成同一句 | 不同现状无法分散,回填容易给错点 | 进入新现状就换新总结 |
|
|
115
|
+
| 把 timeout 当报错、反复重试 | 用户想停却被反复弹窗 | timeout 即收尾结束本轮 |
|
|
116
|
+
| 读 `task.id` 当任务内容 | id 只是编号,内容在 `task.text` | 一律读 `task.text` |
|
|
117
|
+
| 同时开多个 wait(同一等待点) | 只有一个收到回填,另一个等到超时 | 同一等待点同一时刻只调一个 wait |
|
|
118
|
+
| 没用 --json | 输出人读文本,宿主难以可靠解析 | agent 调用一律加 `--json` |
|
|
119
|
+
|
|
120
|
+
## 八、与常驻面板配合 / 排障
|
|
121
|
+
|
|
122
|
+
- 已 `nx-kv serve` 打开面板时,直接在「等待点总览」点选现状回填;正在阻塞的
|
|
123
|
+
wait 通过轮询(约每 0.5 秒)取走,不依赖弹窗本身。
|
|
124
|
+
- 弹窗没自动打开时:终端输出里有本次地址(`http://127.0.0.1:<端口>/`),手动
|
|
125
|
+
复制到浏览器打开;该窗口只在本次 wait 期间有效。
|
|
126
|
+
- wait 结束(拿到任务或超时)后弹窗服务器随即关闭,窗口里再提交会失败——下一
|
|
127
|
+
条等 agent 下一次 wait 打开新窗口再给。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nx-kv",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "nx-kv — KV
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "nx-kv — KV 清单管理中枢 + 对话内实时任务接力:serve 驱动 CLI 与 Web 面板,同源同构,附带可安装 skill 供 agent 领取任务与阻塞接收用户输入",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nx-kv": "bin/cli.mjs"
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
".": "./src/index.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "vite build",
|
|
14
|
+
"build": "vite build && node scripts/link-local.mjs --auto",
|
|
15
15
|
"dev": "vite",
|
|
16
16
|
"dev:serve": "node bin/cli.mjs serve --no-open",
|
|
17
17
|
"start": "pnpm run build && node bin/cli.mjs serve",
|
|
18
|
+
"link:local": "node scripts/link-local.mjs",
|
|
19
|
+
"unlink:local": "node scripts/link-local.mjs --unlink",
|
|
18
20
|
"lint": "eslint .",
|
|
19
21
|
"test": "pnpm run lint && pnpm run build && pnpm run test:smoke && pnpm run test:unit",
|
|
20
22
|
"test:smoke": "node tests/smoke.mjs",
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export * as config from './core/config.js';
|
|
|
4
4
|
export * as kvapi from './core/kvapi.js';
|
|
5
5
|
export * as errors from './core/errors.js';
|
|
6
6
|
export * as todo from './modules/todo/service.js';
|
|
7
|
+
export * as sessiontask from './modules/sessiontask/service.js';
|
|
7
8
|
|
|
8
9
|
export { ACTIONS, MODULES } from './runtime/registry.js';
|
|
9
10
|
export { startServer } from './runtime/server.js';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// 实时任务输入模块:在一个对话里通过工具调用向 agent 实时回填任务,替代原始输入框。
|
|
2
|
+
//
|
|
3
|
+
// 声明了 `resource: 'sessiontask'`,故 CRUD 五操作齐备且两端可调用
|
|
4
|
+
// (tests/unit/registry.test.mjs 据此断言)。核心动作是纯 CLI 的
|
|
5
|
+
// `sessiontask wait --summary "<现状总结>"`——agent 把它放在本轮**最后一步**
|
|
6
|
+
// 调用:阻塞、自动打开浏览器弹窗,用户针对该现状回填后解除阻塞,把新任务返回给
|
|
7
|
+
// agent。现状总结即等待点 key:相同现状复用、不同现状分散答复。
|
|
8
|
+
//
|
|
9
|
+
// 与 todo 的区别:todo 管 KV 后端上的清单(需登录);sessiontask 是**本机**
|
|
10
|
+
// 实时人机接力,数据存本地文件,不依赖后端登录态。
|
|
11
|
+
import * as service from './service.js';
|
|
12
|
+
|
|
13
|
+
const SUMMARY = { type: 'string', required: true, hint: '当前现状总结(作为等待点 key)' };
|
|
14
|
+
|
|
15
|
+
const STATUS_TAG = { pending: '待领取', consumed: '已领取' };
|
|
16
|
+
|
|
17
|
+
// 总览:每个等待点一行,先看现状再决定回填哪条
|
|
18
|
+
function renderOverview(d) {
|
|
19
|
+
const lines = [`等待点 ${d.slots.length} 个(等待中 ${d.waiting})`];
|
|
20
|
+
if (!d.slots.length) {
|
|
21
|
+
lines.push('(暂无等待点)');
|
|
22
|
+
return lines.join('\n');
|
|
23
|
+
}
|
|
24
|
+
for (const s of d.slots) {
|
|
25
|
+
const tag = s.waiting ? '[等待中]' : '[已结束]';
|
|
26
|
+
lines.push(` ${tag} ${s.summary} · 待领取 ${s.pending}/${s.total}`);
|
|
27
|
+
}
|
|
28
|
+
return lines.join('\n');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const renderTask = (t) =>
|
|
32
|
+
[
|
|
33
|
+
`#${t.id} [${STATUS_TAG[t.status] || t.status}] ${t.text}`,
|
|
34
|
+
` 创建: ${t.createdAt}`,
|
|
35
|
+
t.consumedAt ? ` 领取: ${t.consumedAt}` : '',
|
|
36
|
+
]
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.join('\n');
|
|
39
|
+
|
|
40
|
+
function renderAdd(d) {
|
|
41
|
+
const head = `#${d.task.id} [${d.task.status}] ${d.task.text}`;
|
|
42
|
+
return d.delivered
|
|
43
|
+
? `已实时回填给等待中的 agent: ${head}`
|
|
44
|
+
: `已排队(agent 下一次 wait 取走): ${head}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// wait 的人读输出:成功给任务,超时明确提示「本轮结束」
|
|
48
|
+
function renderWait(r) {
|
|
49
|
+
if (r.status === 'timeout') {
|
|
50
|
+
return `等待超时(${Math.round(r.timeoutMs / 1000)} 秒未回填):现状「${r.summary}」结束本轮。`;
|
|
51
|
+
}
|
|
52
|
+
const origin = r.queued ? '排队任务' : '实时回填';
|
|
53
|
+
return `已取得下一条任务(${origin} · 现状「${r.summary}」): #${r.task.id}\n ${r.task.text.replace(/\n/g, '\n ')}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default {
|
|
57
|
+
id: 'sessiontask',
|
|
58
|
+
title: '实时任务(= Web「实时任务」页)',
|
|
59
|
+
order: 20,
|
|
60
|
+
view: () => import('./view.jsx'),
|
|
61
|
+
|
|
62
|
+
// CRUD 资源声明:等待点任务的增删查改齐备,CLI 与面板都能调用
|
|
63
|
+
resource: 'sessiontask',
|
|
64
|
+
|
|
65
|
+
actions: [
|
|
66
|
+
// ─── CRUD 五操作 ───────────────────────────────────────────
|
|
67
|
+
{
|
|
68
|
+
id: 'sessiontask.list',
|
|
69
|
+
cli: ['sessiontask', 'list'],
|
|
70
|
+
http: ['GET', '/api/sessiontasks'],
|
|
71
|
+
summary: '总览全部等待点(现状、是否等待中、待领取数)',
|
|
72
|
+
run: () => service.listTasks(),
|
|
73
|
+
render: renderOverview,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'sessiontask.get',
|
|
77
|
+
cli: ['sessiontask', 'get'],
|
|
78
|
+
http: ['GET', '/api/sessiontasks/item'],
|
|
79
|
+
summary: '查看单条任务(按 --id,定位到 --summary 现状)',
|
|
80
|
+
flags: { id: { type: 'number', required: true, hint: '任务编号' }, summary: SUMMARY },
|
|
81
|
+
run: (ctx) => service.getTask({ id: ctx.id, summary: ctx.summary }),
|
|
82
|
+
render: renderTask,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'sessiontask.add',
|
|
86
|
+
cli: ['sessiontask', 'add'],
|
|
87
|
+
http: ['POST', '/api/sessiontasks'],
|
|
88
|
+
summary: '针对某现状回填一条任务(有 wait 在等则即时投递,否则排队)',
|
|
89
|
+
args: ['text'],
|
|
90
|
+
flags: { summary: SUMMARY },
|
|
91
|
+
run: (ctx) => service.addTask({ text: ctx.text, summary: ctx.summary }),
|
|
92
|
+
render: renderAdd,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: 'sessiontask.update',
|
|
96
|
+
cli: ['sessiontask', 'update'],
|
|
97
|
+
http: ['PATCH', '/api/sessiontasks/item'],
|
|
98
|
+
summary: '编辑任务内容(按 --id,定位到 --summary 现状)',
|
|
99
|
+
flags: {
|
|
100
|
+
id: { type: 'number', required: true, hint: '任务编号' },
|
|
101
|
+
text: { type: 'string', required: true, hint: '改成这个内容' },
|
|
102
|
+
summary: SUMMARY,
|
|
103
|
+
},
|
|
104
|
+
run: (ctx) => service.updateTask({ id: ctx.id, text: ctx.text, summary: ctx.summary }),
|
|
105
|
+
render: (d) => `已更新 #${d.task.id}: ${d.task.text}`,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'sessiontask.remove',
|
|
109
|
+
cli: ['sessiontask', 'remove'],
|
|
110
|
+
http: ['DELETE', '/api/sessiontasks/item'],
|
|
111
|
+
summary: '删除一条任务(按 --id,定位到 --summary 现状)',
|
|
112
|
+
flags: { id: { type: 'number', required: true, hint: '任务编号' }, summary: SUMMARY },
|
|
113
|
+
run: (ctx) => service.removeTask({ id: ctx.id, summary: ctx.summary }),
|
|
114
|
+
render: (d) => `已删除 #${d.removed.id}: ${d.removed.text}`,
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// ─── 阻塞等待(纯 CLI,agent 本轮最后一步)────────────────
|
|
118
|
+
{
|
|
119
|
+
id: 'sessiontask.wait',
|
|
120
|
+
cli: ['sessiontask', 'wait'],
|
|
121
|
+
http: null,
|
|
122
|
+
summary: '针对当前现状阻塞等待用户回填(默认 120 秒,超时结束本轮)',
|
|
123
|
+
flags: {
|
|
124
|
+
summary: SUMMARY,
|
|
125
|
+
timeout: { type: 'number', default: 120, hint: '阻塞上限(秒),默认 120' },
|
|
126
|
+
'no-open': { type: 'boolean', hint: '不自动打开浏览器(测试用)' },
|
|
127
|
+
},
|
|
128
|
+
run: (ctx) =>
|
|
129
|
+
service.waitForTask({
|
|
130
|
+
summary: ctx.summary,
|
|
131
|
+
timeoutMs: ctx.timeout * 1000,
|
|
132
|
+
open: !ctx['no-open'],
|
|
133
|
+
}),
|
|
134
|
+
render: renderWait,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|