dsh-agent-lines 0.3.9 → 0.4.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.en.md +8 -0
- package/CHANGELOG.md +8 -0
- package/README.en.md +30 -4
- package/README.md +46 -4
- package/docs/en/index.html +102 -48
- package/docs/index.html +101 -48
- package/docs/market-index.json +2 -2
- package/examples/market-index.json +2 -2
- package/lib/store.js +39 -7
- package/lib/tools.js +35 -2
- package/package.json +1 -1
package/CHANGELOG.en.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
English | [中文](CHANGELOG.md)
|
|
4
4
|
|
|
5
|
+
## 0.4.0 (2026-11-09)
|
|
6
|
+
|
|
7
|
+
- Message governance: line_send adds kind (info/request/task/alert) and ccManager; a non-manager task becomes pending_approval and is delivered only after line_manager_ack.
|
|
8
|
+
- Schema v8: messages.kind/cc_manager/approval_state/approved_by/approved_at; additive migration.
|
|
9
|
+
- Added line_manager_ack (approve/reject).
|
|
10
|
+
- line_observe shows kind/ccManager/approvalState; pending tasks do not enter the target inbox.
|
|
11
|
+
- README is team-agnostic; GitHub Pages is rewritten for beginners (DSH-assisted install, novel/video/dev scenarios, restart handoff card).
|
|
12
|
+
|
|
5
13
|
## 0.3.9 (2026-11-09)
|
|
6
14
|
|
|
7
15
|
- Added read-only line_observe: a manager feed of cross-line messages without prompts or state changes.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
[English](CHANGELOG.en.md) | 中文
|
|
4
4
|
|
|
5
|
+
## 0.4.0 (2026-11-09)
|
|
6
|
+
|
|
7
|
+
- 消息治理:line_send 新增 kind(info/request/task/alert)、ccManager;非管理线发 task 进入 pending_approval,需 line_manager_ack 审批后才投递。
|
|
8
|
+
- schema v8:messages.kind/cc_manager/approval_state/approved_by/approved_at;additive 迁移。
|
|
9
|
+
- 新增 line_manager_ack(approve/reject)。
|
|
10
|
+
- line_observe 展示 kind/ccManager/approvalState;待审批任务不进目标线 inbox。
|
|
11
|
+
- README 团队类型通用化;GitHub Pages 面向小白改版(安装交给 DSH、小说/视频/开发场景、重启前交接卡)。
|
|
12
|
+
|
|
5
13
|
## 0.3.9 (2026-11-09)
|
|
6
14
|
|
|
7
15
|
- 新增只读 line_observe:管理线查看跨线消息 feed,不注入 prompt、不改消息状态。
|
package/README.en.md
CHANGED
|
@@ -23,6 +23,18 @@ Use it when:
|
|
|
23
23
|
- You want one lead agent to decompose work, create the team, track progress, collect results, and report back.
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
## Any team type, not just development
|
|
27
|
+
|
|
28
|
+
The plugin does not limit team types. It is "one manager + several long-lived members".
|
|
29
|
+
The examples below use a development team; novel, video, research, or operations teams only change roles and tasks.
|
|
30
|
+
|
|
31
|
+
- Novel: editor-in-chief, outline, writer, editor, proofreader, reviewer;
|
|
32
|
+
- Video: manager, planning, script, editing, cover, publishing;
|
|
33
|
+
- Development: manager, developer, tester, acceptance;
|
|
34
|
+
- Research: manager, research, analysis, writing, review.
|
|
35
|
+
|
|
36
|
+
You do not need to know the roles up front. Tell the manager "build a novel team and set up its workflow and mechanisms"; it proposes the team shape first.
|
|
37
|
+
|
|
26
38
|
## What each version adds (business scenarios)
|
|
27
39
|
|
|
28
40
|
### 0.3.6: replace a role when context is full
|
|
@@ -43,7 +55,7 @@ Scenario: the manager wants to see which line is close to saturation or has gone
|
|
|
43
55
|
Use: line_status / line_list expose activity.level, turns, steps, lastActivityAt.
|
|
44
56
|
Effect: a low/medium/high activity proxy, not a fake "78% context used"; exact context usage waits for DSH.
|
|
45
57
|
|
|
46
|
-
### 0.
|
|
58
|
+
### 0.4.0: the manager can see cross-line traffic
|
|
47
59
|
|
|
48
60
|
Scenario: lines message each other directly; the manager needs visibility without every message becoming a manager turn.
|
|
49
61
|
Use: line_observe({ line: "writer", limit: 20 }) or CLI observe --json.
|
|
@@ -62,7 +74,7 @@ The script picks the DSH Web profile, installs the plugin, and verifies agent-li
|
|
|
62
74
|
|
|
63
75
|
Offline or when npm is unavailable, use the local tarball:
|
|
64
76
|
|
|
65
|
-
bash install.sh ./dsh-agent-lines-0.
|
|
77
|
+
bash install.sh ./dsh-agent-lines-0.4.0.tgz
|
|
66
78
|
|
|
67
79
|
After install and restart, it works without a config. When you want a fixed manager/worker setup, paste the "Manager config" block below into the file printed by the installer.
|
|
68
80
|
|
|
@@ -165,12 +177,13 @@ Tools:
|
|
|
165
177
|
| `line_list` | List lines, status, unread counts, current line and role |
|
|
166
178
|
| `line_create` | Manager only: create a line; same label is idempotent; replace:true swaps the label to a fresh Session |
|
|
167
179
|
| `line_handoff` | Manager only: structured handoff snapshot (JSON/Markdown), no model call |
|
|
168
|
-
| `line_observe` | Manager only: read-only cross-line feed; no prompt injection, no state change |
|
|
180
|
+
| `line_observe` | Manager only: read-only cross-line feed with kind/approval state; no prompt injection, no state change |
|
|
181
|
+
| `line_manager_ack` | Manager only: approve/reject a pending_approval task |
|
|
169
182
|
| `line_retire` | Manager only: soft-retire; reject new sends, keep Session and history |
|
|
170
183
|
| `line_rebind` | Manager only: atomically switch a line to a new live Session, optionally migrating backlog |
|
|
171
184
|
| `line_rename` | Manager only: rename the sidebar title and persist it in lines.db |
|
|
172
185
|
| `line_move_workspace` | Manager only: attach an existing line Session to a workspace whose path exactly matches the Session cwd |
|
|
173
|
-
| `line_send` | Send to `line` or `agent#line`; optional `idempotencyKey`, `replyTo`, `noReply`, `forceDistinct`, `ttlMs` |
|
|
186
|
+
| `line_send` | Send to `line` or `agent#line`; optional `kind`, `ccManager`, `idempotencyKey`, `replyTo`, `noReply`, `forceDistinct`, `ttlMs` |
|
|
174
187
|
| `line_inbox` | Read delivered messages for the current line |
|
|
175
188
|
| `line_ack` | Acknowledge a delivered message |
|
|
176
189
|
|
|
@@ -263,6 +276,19 @@ dsh-agent-lines claim --agent agent-demo --line manager --session "$DSH_SESSION_
|
|
|
263
276
|
|
|
264
277
|
Claim is never automatic for every new Session: it needs an explicit user request or an explicit CLI call.
|
|
265
278
|
|
|
279
|
+
## Message kinds and task approval
|
|
280
|
+
|
|
281
|
+
`line_send` supports four kinds:
|
|
282
|
+
|
|
283
|
+
| kind | Meaning | Non-manager send |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| info | Informational sync | delivered directly |
|
|
286
|
+
| request | Request help | delivered directly |
|
|
287
|
+
| task | Assign work | becomes pending_approval; delivered only after manager approve |
|
|
288
|
+
| alert | Alert | delivered directly |
|
|
289
|
+
|
|
290
|
+
Manager tasks skip approval. Use `line_observe` to see pending tasks and `line_manager_ack({ messageId, decision: "approve" | "reject" })` to decide.
|
|
291
|
+
|
|
266
292
|
## Echo and duplicate protection
|
|
267
293
|
|
|
268
294
|
- Same `idempotencyKey` returns the existing message.
|
package/README.md
CHANGED
|
@@ -23,6 +23,18 @@ DSH 的 super agent 平替插件。
|
|
|
23
23
|
- 你希望一个负责人 agent 自己拆任务、建团队、追进度、收结果,最后向你汇报。
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
## 团队类型不限于开发
|
|
27
|
+
|
|
28
|
+
插件不限定团队类型。本质是“一个负责人 + 若干长期成员”的协作方式。
|
|
29
|
+
下面用开发团队做示例;小说、视频、研究、运营团队只换角色和任务,不需要改插件或代码。
|
|
30
|
+
|
|
31
|
+
- 小说:总编、大纲、写手、编辑、校对、审读;
|
|
32
|
+
- 视频:负责人、策划、脚本、剪辑、封面、发布;
|
|
33
|
+
- 开发:负责人、开发、测试、验收;
|
|
34
|
+
- 研究:负责人、资料、分析、写作、审阅。
|
|
35
|
+
|
|
36
|
+
第一次不需要自己想清楚角色:直接对负责人说“帮我建个小说创作团队,按小说创作的流程建立工作流程与机制”,它会先给团队结构和流程建议,你确认或补充即可。
|
|
37
|
+
|
|
26
38
|
## 版本能力与业务场景
|
|
27
39
|
|
|
28
40
|
### 0.3.6:上下文满了,角色可以换人
|
|
@@ -43,7 +55,7 @@ DSH 的 super agent 平替插件。
|
|
|
43
55
|
用法:line_status / line_list 看 activity.level、turns、steps、lastActivityAt。
|
|
44
56
|
效果:给出低/中/高活跃度,不伪造“上下文使用率 78%”;真实窗口等 DSH 上游提供。
|
|
45
57
|
|
|
46
|
-
### 0.
|
|
58
|
+
### 0.4.0:管理线看得到跨线消息
|
|
47
59
|
|
|
48
60
|
场景:成员线之间直接同步,管理线需要知道发生了什么,但不希望每条消息都打断自己。
|
|
49
61
|
用法:line_observe({ line: "writer", limit: 20 }) 或 CLI observe --json。
|
|
@@ -51,6 +63,22 @@ DSH 的 super agent 平替插件。
|
|
|
51
63
|
|
|
52
64
|
## 安装
|
|
53
65
|
|
|
66
|
+
### 让 DSH 帮你装(推荐给新手)
|
|
67
|
+
|
|
68
|
+
在 DSH 新会话里说:
|
|
69
|
+
|
|
70
|
+
~~~text
|
|
71
|
+
帮我检查并安装 GitHub 上 Lencen/dsh-agent-lines 插件。
|
|
72
|
+
只从官方仓库或官方 npm 安装,不要从其他 fork 安装。
|
|
73
|
+
安装前先告诉我准备执行什么命令,等我确认后再执行。
|
|
74
|
+
安装完成后按插件要求加载;如果需要重启 DSH Web,先告诉我重启后打开哪个地址、
|
|
75
|
+
怎么新开会话、第一句该说什么,并提醒当前会话会断开;等我确认后再重启。
|
|
76
|
+
~~~
|
|
77
|
+
|
|
78
|
+
重启后当前会话会断开,agent 无法再自动通知你;重启后的步骤会在重启前给你。
|
|
79
|
+
|
|
80
|
+
### 手动安装
|
|
81
|
+
|
|
54
82
|
一条命令安装(推荐,不需要自己找 profile):
|
|
55
83
|
|
|
56
84
|
curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh
|
|
@@ -62,7 +90,7 @@ DSH 的 super agent 平替插件。
|
|
|
62
90
|
|
|
63
91
|
离线或 npm 不可用时,用本地 tarball:
|
|
64
92
|
|
|
65
|
-
bash install.sh ./dsh-agent-lines-0.
|
|
93
|
+
bash install.sh ./dsh-agent-lines-0.4.0.tgz
|
|
66
94
|
|
|
67
95
|
安装并重启后,不写配置也可以直接使用;需要固定管理线/worker 时,再把下面“管理线配置”粘贴到脚本打印的配置文件。
|
|
68
96
|
|
|
@@ -170,12 +198,13 @@ level 是活跃度代理(low/medium/high),不是上下文使用率百分
|
|
|
170
198
|
| `line_list` | 列出线、状态、未读数、当前线和 role |
|
|
171
199
|
| `line_create` | 仅管理线:创建新线;同 label 幂等;传 replace:true 可给同 label 换新 Session |
|
|
172
200
|
| `line_handoff` | 仅管理线:生成结构化交接单(JSON/Markdown),不调模型 |
|
|
173
|
-
| `line_observe` | 仅管理线:只读跨线消息 feed
|
|
201
|
+
| `line_observe` | 仅管理线:只读跨线消息 feed;显示 kind/审批状态,不注入 prompt、不改状态 |
|
|
202
|
+
| `line_manager_ack` | 仅管理线:对 pending_approval 的 task 做 approve/reject |
|
|
174
203
|
| `line_retire` | 仅管理线:软下线;拒绝新发送,保留 Session/历史 |
|
|
175
204
|
| `line_rebind` | 仅管理线:把线原子切到新的 live Session,可迁移积压 |
|
|
176
205
|
| `line_rename` | 仅管理线:改侧栏标题,持久化到 lines.db |
|
|
177
206
|
| `line_move_workspace` | 仅管理线:把已有线的 Session 挂到路径完全匹配的工作区 |
|
|
178
|
-
| `line_send` | 发到 `line` 或 `agent#line`;可选 `idempotencyKey`、`replyTo`、`noReply`、`forceDistinct`、`ttlMs` |
|
|
207
|
+
| `line_send` | 发到 `line` 或 `agent#line`;可选 `kind`、`ccManager`、`idempotencyKey`、`replyTo`、`noReply`、`forceDistinct`、`ttlMs` |
|
|
179
208
|
| `line_inbox` | 读当前线已投递消息 |
|
|
180
209
|
| `line_ack` | 确认已处理 |
|
|
181
210
|
|
|
@@ -261,6 +290,19 @@ dsh-agent-lines claim --agent agent-demo --line manager --session "$DSH_SESSION_
|
|
|
261
290
|
|
|
262
291
|
claim 不会对每个新会话自动执行:需要用户明确要求,或运维显式调用。
|
|
263
292
|
|
|
293
|
+
## 消息类型与任务审批
|
|
294
|
+
|
|
295
|
+
`line_send` 支持四种 kind:
|
|
296
|
+
|
|
297
|
+
| kind | 含义 | 非管理线发送 |
|
|
298
|
+
|---|---|---|
|
|
299
|
+
| info | 信息同步 | 直接投递 |
|
|
300
|
+
| request | 请求协助 | 直接投递 |
|
|
301
|
+
| task | 派任务 | 进入 pending_approval,管理线 approve 后才投递 |
|
|
302
|
+
| alert | 告警 | 直接投递 |
|
|
303
|
+
|
|
304
|
+
管理线发 task 不进入审批。管理线用 `line_observe` 看 pending,再用 `line_manager_ack({ messageId, decision: "approve" | "reject" })` 决定。
|
|
305
|
+
|
|
264
306
|
## 回声与重复保护
|
|
265
307
|
|
|
266
308
|
- 同 `idempotencyKey` 返回已有消息。
|
package/docs/en/index.html
CHANGED
|
@@ -3,97 +3,151 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>dsh-agent-lines — let
|
|
7
|
-
<meta name="description" content="
|
|
6
|
+
<title>dsh-agent-lines — let an AI lead run your AI team</title>
|
|
7
|
+
<meta name="description" content="Tell the lead your goal. It splits work, builds a team, tracks progress and summarizes results. Members and messages survive a DSH restart.">
|
|
8
8
|
<meta property="og:image" content="https://lencen.github.io/dsh-agent-lines/assets/social-preview.png">
|
|
9
9
|
<style>
|
|
10
|
-
:root{--bg:#080b10;--panel:#0d1117;--line:#263241;--text:#e6edf3;--muted:#8b949e;--cyan:#58a6ff;--green:#7ee787}
|
|
10
|
+
:root{--bg:#080b10;--panel:#0d1117;--line:#263241;--text:#e6edf3;--muted:#8b949e;--cyan:#58a6ff;--green:#7ee787;--warn:#f0b72f}
|
|
11
11
|
*{box-sizing:border-box}
|
|
12
|
-
body{margin:0;background:radial-gradient(circle at 80% 0%,#12314a 0,#080b10 42%);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;line-height:1.
|
|
12
|
+
body{margin:0;background:radial-gradient(circle at 80% 0%,#12314a 0,#080b10 42%);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans CJK SC",sans-serif;line-height:1.75}
|
|
13
13
|
a{color:var(--cyan);text-decoration:none}
|
|
14
|
-
.wrap{max-width:
|
|
14
|
+
.wrap{max-width:1060px;margin:0 auto;padding:0 24px}
|
|
15
15
|
header{display:flex;align-items:center;justify-content:space-between;padding:22px 0;border-bottom:1px solid #1f2937}
|
|
16
16
|
.brand{display:flex;align-items:center;gap:10px;color:var(--text);font-weight:700}
|
|
17
17
|
.mark{width:24px;height:24px;border-radius:7px;background:linear-gradient(135deg,#58a6ff,#7ee787)}
|
|
18
18
|
nav a{color:var(--muted);margin-left:18px;font-size:14px}
|
|
19
|
-
.hero{padding:
|
|
20
|
-
.kicker{color:var(--green);font-size:12px;letter-spacing:.
|
|
21
|
-
h1{font-size:
|
|
19
|
+
.hero{padding:64px 0 32px;display:grid;grid-template-columns:1.1fr .9fr;gap:40px;align-items:center}
|
|
20
|
+
.kicker{color:var(--green);font-size:12px;letter-spacing:.14em;text-transform:uppercase}
|
|
21
|
+
h1{font-size:46px;line-height:1.15;margin:12px 0 16px;letter-spacing:-.03em}
|
|
22
22
|
h1 span{color:var(--cyan)}
|
|
23
|
-
.lead{color:#b7c3cf;font-size:17px;max-width:
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.btn{
|
|
27
|
-
.btn.primary{background:var(--cyan);color:#06111f;border-color:var(--cyan);font-weight:700}
|
|
23
|
+
.lead{color:#b7c3cf;font-size:17px;max-width:580px}
|
|
24
|
+
.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:22px}
|
|
25
|
+
.btn{display:inline-block;padding:11px 17px;border-radius:9px;border:1px solid var(--line);color:var(--text);font-size:14px}
|
|
26
|
+
.btn.primary{background:var(--green);color:#06111f;border-color:var(--green);font-weight:700}
|
|
28
27
|
.shot{border:1px solid var(--line);border-radius:14px;overflow:hidden;background:var(--panel);box-shadow:0 22px 60px rgba(0,0,0,.35)}
|
|
29
28
|
.shot img{display:block;width:100%;height:auto}
|
|
30
29
|
.caption{font-size:12px;color:var(--muted);padding:10px 14px;border-top:1px solid #1f2937}
|
|
31
|
-
section{padding:
|
|
32
|
-
h2{font-size:
|
|
30
|
+
section{padding:34px 0;border-top:1px solid #161d27}
|
|
31
|
+
h2{font-size:23px;margin:0 0 16px}
|
|
33
32
|
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
|
|
34
|
-
.card{background:rgba(13,17,23,.
|
|
33
|
+
.card{background:rgba(13,17,23,.82);border:1px solid var(--line);border-radius:12px;padding:18px}
|
|
35
34
|
.card b{display:block;margin-bottom:6px}
|
|
36
35
|
.card p{color:var(--muted);font-size:14px;margin:0}
|
|
37
|
-
pre{background:#0a0f16;border:1px solid var(--line);border-radius:10px;padding:16px;overflow:auto;color:#c9d1d9;font-size:13px}
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
pre{background:#0a0f16;border:1px solid var(--line);border-radius:10px;padding:16px;overflow:auto;color:#c9d1d9;font-size:13px;white-space:pre-wrap}
|
|
37
|
+
.note{color:var(--muted);font-size:13px}
|
|
38
|
+
.warn{border-left:3px solid var(--warn);padding:10px 14px;background:#141007;color:#e8d7a8;border-radius:6px;font-size:14px}
|
|
39
|
+
details{background:rgba(13,17,23,.7);border:1px solid var(--line);border-radius:10px;padding:14px 16px;margin-top:14px}
|
|
40
|
+
summary{cursor:pointer;color:var(--muted);font-size:14px}
|
|
41
|
+
footer{padding:30px 0 46px;color:var(--muted);font-size:12px;border-top:1px solid #161d27}
|
|
42
|
+
@media(max-width:820px){.hero{grid-template-columns:1fr;padding-top:40px}h1{font-size:36px}.cards{grid-template-columns:1fr}}
|
|
40
43
|
</style>
|
|
41
44
|
</head>
|
|
42
45
|
<body>
|
|
43
46
|
<div class="wrap">
|
|
44
47
|
<header>
|
|
45
48
|
<a class="brand" href="../"><span class="mark"></span>dsh-agent-lines</a>
|
|
46
|
-
<nav><a href="../">中文</a><a href="https://github.com/Lencen/dsh-agent-lines">GitHub</a><a href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.
|
|
49
|
+
<nav><a href="../">中文</a><a href="https://github.com/Lencen/dsh-agent-lines">GitHub</a><a href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.4.0">Release</a></nav>
|
|
47
50
|
</header>
|
|
48
51
|
<main>
|
|
49
|
-
<section class="hero" style="border:0;padding-bottom:
|
|
52
|
+
<section class="hero" style="border:0;padding-bottom:20px">
|
|
50
53
|
<div>
|
|
51
54
|
<div class="kicker">Community plugin · Apache-2.0</div>
|
|
52
|
-
<h1>Let
|
|
53
|
-
<p class="lead">
|
|
54
|
-
<div class="install">curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh && bash install.sh</div>
|
|
55
|
+
<h1>Let an AI lead<br><span>run your AI team</span></h1>
|
|
56
|
+
<p class="lead">Tell it the goal. The lead splits work, picks members, assigns tasks, tracks progress and summarizes the result. Close the window or restart DSH: members and messages stay.</p>
|
|
55
57
|
<div class="actions">
|
|
56
|
-
<a class="btn primary" href="
|
|
57
|
-
<a class="btn" href="
|
|
58
|
+
<a class="btn primary" href="#start">How to start</a>
|
|
59
|
+
<a class="btn" href="#install">Install</a>
|
|
58
60
|
</div>
|
|
59
61
|
</div>
|
|
60
62
|
<div class="shot">
|
|
61
|
-
<img src="../assets/first-use.gif" alt="
|
|
62
|
-
<div class="caption">
|
|
63
|
+
<img src="../assets/first-use.gif" alt="A lead creates member lines and gets acknowledgements">
|
|
64
|
+
<div class="caption">Demo profile · generic roles · no internal data</div>
|
|
63
65
|
</div>
|
|
64
66
|
</section>
|
|
67
|
+
|
|
65
68
|
<section>
|
|
66
|
-
<h2>What it
|
|
69
|
+
<h2>What it does for you</h2>
|
|
67
70
|
<div class="cards">
|
|
68
|
-
<div class="card"><b>No repeated
|
|
69
|
-
<div class="card"><b>No manual
|
|
70
|
-
<div class="card"><b>No
|
|
71
|
+
<div class="card"><b>No repeated background</b><p>Each member keeps its own context; new tasks go straight to the right role.</p></div>
|
|
72
|
+
<div class="card"><b>No manual scheduling</b><p>The lead splits work, coordinates, tracks progress; you read the summary.</p></div>
|
|
73
|
+
<div class="card"><b>No fear of closing</b><p>Members and messages are stored locally; restart DSH and continue.</p></div>
|
|
71
74
|
</div>
|
|
72
|
-
<p style="color:var(--muted);font-size:14px;margin-top:14px">Built for long-running projects and multi-role work.</p>
|
|
73
75
|
</section>
|
|
76
|
+
|
|
74
77
|
<section>
|
|
75
|
-
<h2>
|
|
78
|
+
<h2>Any team type</h2>
|
|
79
|
+
<p class="note">Not only development. Novel, video, research, operations — change roles and tasks, not the plugin.</p>
|
|
76
80
|
<div class="cards">
|
|
77
|
-
<div class="card"><b>
|
|
78
|
-
<div class="card"><b>
|
|
79
|
-
<div class="card"><b>
|
|
80
|
-
<div class="card"><b>4. You get the result</b><p>The lead reports conclusions, file paths, risks, and open questions. You do not chase every member yourself.</p></div>
|
|
81
|
+
<div class="card"><b>Novel team</b><p>Editor-in-chief, outline, writer, editor, proofreader, reviewer.</p></div>
|
|
82
|
+
<div class="card"><b>Video team</b><p>Lead, planning, script, editing, cover, publishing.</p></div>
|
|
83
|
+
<div class="card"><b>Development team</b><p>Lead, developer, tester, acceptance.</p></div>
|
|
81
84
|
</div>
|
|
85
|
+
<p class="note" style="margin-top:14px">You do not need to know the roles first. Say “build a novel team and set up its workflow and mechanisms”; the lead proposes the team shape first.</p>
|
|
82
86
|
</section>
|
|
83
|
-
<section>
|
|
84
|
-
<h2>First use: let the lead form the team</h2>
|
|
85
|
-
<pre>Use dsh-agent-lines. Make this Session the development team lead; bind it first if needed.
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
<section id="install">
|
|
89
|
+
<h2>Install: let DSH do it</h2>
|
|
90
|
+
<p>In a new DSH session, say:</p>
|
|
91
|
+
<pre>Check and install the GitHub plugin Lencen/dsh-agent-lines.
|
|
92
|
+
Use only the official repo or official npm package; do not install from forks.
|
|
93
|
+
Before running anything, show me the command and wait for my confirmation.
|
|
94
|
+
After install, load it as required; if DSH Web must restart, first tell me which URL to open,
|
|
95
|
+
how to start a new session, and what to say first, and warn me that this session will disconnect.
|
|
96
|
+
Wait for my confirmation before restarting.</pre>
|
|
97
|
+
<div class="warn">Restarting DSH Web disconnects this session; the agent cannot notify you after the restart. Put the next steps in the last message before restarting.</div>
|
|
98
|
+
<details>
|
|
99
|
+
<summary>Prefer manual install? Command</summary>
|
|
100
|
+
<pre>curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh && bash install.sh</pre>
|
|
101
|
+
<p class="note">Restart DSH Web after install so the plugin loads.</p>
|
|
102
|
+
</details>
|
|
103
|
+
</section>
|
|
104
|
+
|
|
105
|
+
<section id="start">
|
|
106
|
+
<h2>How to start</h2>
|
|
107
|
+
<div class="cards">
|
|
108
|
+
<div class="card"><b>1. Install and restart</b><p>Use the flow above; restart DSH Web when asked.</p></div>
|
|
109
|
+
<div class="card"><b>2. Open a new session</b><p>Tell the lead what kind of team you want.</p></div>
|
|
110
|
+
<div class="card"><b>3. Confirm the plan</b><p>The lead proposes team shape and workflow; confirm or add roles.</p></div>
|
|
111
|
+
</div>
|
|
112
|
+
<p class="note" style="margin-top:14px">Say:</p>
|
|
113
|
+
<pre>Build a novel team and set up its workflow and mechanisms.
|
|
114
|
+
or
|
|
115
|
+
Build a development team and set up its development workflow and mechanisms.</pre>
|
|
116
|
+
<p class="note">If you have specific roles, add:</p>
|
|
117
|
+
<pre>Also add a proofreader and a reviewer.</pre>
|
|
118
|
+
</section>
|
|
88
119
|
|
|
89
|
-
|
|
90
|
-
|
|
120
|
+
<section>
|
|
121
|
+
<h2>First prompts</h2>
|
|
122
|
+
<p class="note">Novel:</p>
|
|
123
|
+
<pre>Build a novel team and set up its workflow and mechanisms.
|
|
124
|
+
Do not write the novel yet; wait for my first chapter task.</pre>
|
|
125
|
+
<p class="note">Video:</p>
|
|
126
|
+
<pre>Build a video team and set up its workflow and mechanisms.
|
|
127
|
+
Do not start yet; wait for my first topic.</pre>
|
|
128
|
+
<p class="note">Development:</p>
|
|
129
|
+
<pre>Build a development team and set up its development workflow and mechanisms.
|
|
130
|
+
Do not start yet; wait for my first requirement.</pre>
|
|
131
|
+
</section>
|
|
91
132
|
|
|
92
|
-
|
|
93
|
-
<
|
|
133
|
+
<section>
|
|
134
|
+
<h2>FAQ</h2>
|
|
135
|
+
<div class="cards">
|
|
136
|
+
<div class="card"><b>Is it expensive?</b><p>You pay for actual usage; the lead proposes a minimal team first and expands later.</p></div>
|
|
137
|
+
<div class="card"><b>Do I lose it on restart?</b><p>No. Members and messages are stored locally; continue after restart.</p></div>
|
|
138
|
+
<div class="card"><b>Two teams at once?</b><p>One plugin instance supports one team. Use separate environments for novel and video.</p></div>
|
|
139
|
+
</div>
|
|
94
140
|
</section>
|
|
95
|
-
|
|
141
|
+
|
|
142
|
+
<footer>
|
|
143
|
+
<details>
|
|
144
|
+
<summary>Developers: protocol, limits, install command</summary>
|
|
145
|
+
<p><a href="https://github.com/Lencen/dsh-agent-lines">GitHub</a> · <a href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.4.0">Release</a> · <a href="https://github.com/Lencen/dsh-agent-lines/blob/main/docs/PROTOCOL.en.md">Protocol</a></p>
|
|
146
|
+
<p class="note">Community plugin, not an official DeepSeek product.</p>
|
|
147
|
+
</details>
|
|
148
|
+
<p style="margin-top:18px">dsh-agent-lines 0.4.0 · Apache-2.0 · Community plugin for DeepSeek Harness. Not an official DeepSeek product.</p>
|
|
149
|
+
</footer>
|
|
96
150
|
</main>
|
|
97
151
|
</div>
|
|
98
152
|
</body>
|
|
99
|
-
</html>
|
|
153
|
+
</html>
|
package/docs/index.html
CHANGED
|
@@ -3,97 +3,150 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>dsh-agent-lines — 让 AI
|
|
7
|
-
<meta name="description" content="你说目标,AI
|
|
6
|
+
<title>dsh-agent-lines — 让 AI 负责人带一支团队</title>
|
|
7
|
+
<meta name="description" content="你说目标,AI 负责人拆任务、组团队、追进度、收结果;关掉窗口、重启 DSH,成员和消息还在。">
|
|
8
8
|
<meta property="og:image" content="https://lencen.github.io/dsh-agent-lines/assets/social-preview.png">
|
|
9
9
|
<style>
|
|
10
|
-
:root{--bg:#080b10;--panel:#0d1117;--line:#263241;--text:#e6edf3;--muted:#8b949e;--cyan:#58a6ff;--green:#7ee787}
|
|
10
|
+
:root{--bg:#080b10;--panel:#0d1117;--line:#263241;--text:#e6edf3;--muted:#8b949e;--cyan:#58a6ff;--green:#7ee787;--warn:#f0b72f}
|
|
11
11
|
*{box-sizing:border-box}
|
|
12
|
-
body{margin:0;background:radial-gradient(circle at 80% 0%,#12314a 0,#080b10 42%);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans CJK SC",sans-serif;line-height:1.
|
|
12
|
+
body{margin:0;background:radial-gradient(circle at 80% 0%,#12314a 0,#080b10 42%);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans CJK SC",sans-serif;line-height:1.75}
|
|
13
13
|
a{color:var(--cyan);text-decoration:none}
|
|
14
|
-
.wrap{max-width:
|
|
14
|
+
.wrap{max-width:1060px;margin:0 auto;padding:0 24px}
|
|
15
15
|
header{display:flex;align-items:center;justify-content:space-between;padding:22px 0;border-bottom:1px solid #1f2937}
|
|
16
16
|
.brand{display:flex;align-items:center;gap:10px;color:var(--text);font-weight:700}
|
|
17
17
|
.mark{width:24px;height:24px;border-radius:7px;background:linear-gradient(135deg,#58a6ff,#7ee787)}
|
|
18
18
|
nav a{color:var(--muted);margin-left:18px;font-size:14px}
|
|
19
|
-
.hero{padding:
|
|
20
|
-
.kicker{color:var(--green);font-size:12px;letter-spacing:.
|
|
21
|
-
h1{font-size:
|
|
19
|
+
.hero{padding:64px 0 32px;display:grid;grid-template-columns:1.1fr .9fr;gap:40px;align-items:center}
|
|
20
|
+
.kicker{color:var(--green);font-size:12px;letter-spacing:.14em;text-transform:uppercase}
|
|
21
|
+
h1{font-size:46px;line-height:1.15;margin:12px 0 16px;letter-spacing:-.03em}
|
|
22
22
|
h1 span{color:var(--cyan)}
|
|
23
|
-
.lead{color:#b7c3cf;font-size:17px;max-width:
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.btn{
|
|
27
|
-
.btn.primary{background:var(--cyan);color:#06111f;border-color:var(--cyan);font-weight:700}
|
|
23
|
+
.lead{color:#b7c3cf;font-size:17px;max-width:580px}
|
|
24
|
+
.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:22px}
|
|
25
|
+
.btn{display:inline-block;padding:11px 17px;border-radius:9px;border:1px solid var(--line);color:var(--text);font-size:14px}
|
|
26
|
+
.btn.primary{background:var(--green);color:#06111f;border-color:var(--green);font-weight:700}
|
|
28
27
|
.shot{border:1px solid var(--line);border-radius:14px;overflow:hidden;background:var(--panel);box-shadow:0 22px 60px rgba(0,0,0,.35)}
|
|
29
28
|
.shot img{display:block;width:100%;height:auto}
|
|
30
29
|
.caption{font-size:12px;color:var(--muted);padding:10px 14px;border-top:1px solid #1f2937}
|
|
31
|
-
section{padding:
|
|
32
|
-
h2{font-size:
|
|
30
|
+
section{padding:34px 0;border-top:1px solid #161d27}
|
|
31
|
+
h2{font-size:23px;margin:0 0 16px}
|
|
33
32
|
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
|
|
34
|
-
.card{background:rgba(13,17,23,.
|
|
33
|
+
.card{background:rgba(13,17,23,.82);border:1px solid var(--line);border-radius:12px;padding:18px}
|
|
35
34
|
.card b{display:block;margin-bottom:6px}
|
|
36
35
|
.card p{color:var(--muted);font-size:14px;margin:0}
|
|
37
|
-
pre{background:#0a0f16;border:1px solid var(--line);border-radius:10px;padding:16px;overflow:auto;color:#c9d1d9;font-size:13px}
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
pre{background:#0a0f16;border:1px solid var(--line);border-radius:10px;padding:16px;overflow:auto;color:#c9d1d9;font-size:13px;white-space:pre-wrap}
|
|
37
|
+
.note{color:var(--muted);font-size:13px}
|
|
38
|
+
.warn{border-left:3px solid var(--warn);padding:10px 14px;background:#141007;color:#e8d7a8;border-radius:6px;font-size:14px}
|
|
39
|
+
details{background:rgba(13,17,23,.7);border:1px solid var(--line);border-radius:10px;padding:14px 16px;margin-top:14px}
|
|
40
|
+
summary{cursor:pointer;color:var(--muted);font-size:14px}
|
|
41
|
+
footer{padding:30px 0 46px;color:var(--muted);font-size:12px;border-top:1px solid #161d27}
|
|
42
|
+
@media(max-width:820px){.hero{grid-template-columns:1fr;padding-top:40px}h1{font-size:36px}.cards{grid-template-columns:1fr}}
|
|
40
43
|
</style>
|
|
41
44
|
</head>
|
|
42
45
|
<body>
|
|
43
46
|
<div class="wrap">
|
|
44
47
|
<header>
|
|
45
48
|
<a class="brand" href="./"><span class="mark"></span>dsh-agent-lines</a>
|
|
46
|
-
<nav><a href="en/index.html">English</a><a href="https://github.com/Lencen/dsh-agent-lines">GitHub</a><a href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.
|
|
49
|
+
<nav><a href="en/index.html">English</a><a href="https://github.com/Lencen/dsh-agent-lines">GitHub</a><a href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.4.0">Release</a></nav>
|
|
47
50
|
</header>
|
|
48
51
|
<main>
|
|
49
|
-
<section class="hero" style="border:0;padding-bottom:
|
|
52
|
+
<section class="hero" style="border:0;padding-bottom:20px">
|
|
50
53
|
<div>
|
|
51
54
|
<div class="kicker">Community plugin · Apache-2.0</div>
|
|
52
|
-
<h1
|
|
53
|
-
<p class="lead"
|
|
54
|
-
<div class="install">curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh && bash install.sh</div>
|
|
55
|
+
<h1>让 AI 负责人<br><span>替你带一支 AI 团队</span></h1>
|
|
56
|
+
<p class="lead">你只说目标。负责人会拆任务、找合适的成员、安排工作、追进度,最后把结果汇总给你。关掉窗口、重启 DSH,成员和消息都还在。</p>
|
|
55
57
|
<div class="actions">
|
|
56
|
-
<a class="btn primary" href="
|
|
57
|
-
<a class="btn" href="
|
|
58
|
+
<a class="btn primary" href="#start">怎么开始</a>
|
|
59
|
+
<a class="btn" href="#install">安装插件</a>
|
|
58
60
|
</div>
|
|
59
61
|
</div>
|
|
60
62
|
<div class="shot">
|
|
61
|
-
<img src="assets/first-use.gif" alt="
|
|
62
|
-
<div class="caption"
|
|
63
|
+
<img src="assets/first-use.gif" alt="负责人创建成员线并收到确认">
|
|
64
|
+
<div class="caption">演示环境 · 通用角色 · 不含内部数据</div>
|
|
63
65
|
</div>
|
|
64
66
|
</section>
|
|
67
|
+
|
|
65
68
|
<section>
|
|
66
|
-
<h2
|
|
69
|
+
<h2>它能帮你做什么</h2>
|
|
67
70
|
<div class="cards">
|
|
68
|
-
<div class="card"><b>不用反复讲背景</b><p
|
|
69
|
-
<div class="card"><b>不用自己当调度</b><p
|
|
70
|
-
<div class="card"><b>不用怕关窗口</b><p
|
|
71
|
+
<div class="card"><b>不用反复讲背景</b><p>每个成员长期保留自己的上下文;新任务直接派给对应角色。</p></div>
|
|
72
|
+
<div class="card"><b>不用自己当调度</b><p>负责人拆任务、分工、协调、追进度,你只看最后的汇总。</p></div>
|
|
73
|
+
<div class="card"><b>不用怕关窗口</b><p>成员和消息保存在本地;重启 DSH 后还能接着做。</p></div>
|
|
71
74
|
</div>
|
|
72
|
-
<p style="color:var(--muted);font-size:14px;margin-top:14px">适合长期项目、多角色协作。</p>
|
|
73
75
|
</section>
|
|
76
|
+
|
|
74
77
|
<section>
|
|
75
|
-
<h2
|
|
78
|
+
<h2>适合什么团队</h2>
|
|
79
|
+
<p class="note">不限于开发。小说、视频、研究、运营都可以;只换角色和任务,不需要改插件。</p>
|
|
76
80
|
<div class="cards">
|
|
77
|
-
<div class="card"><b
|
|
78
|
-
<div class="card"><b
|
|
79
|
-
<div class="card"><b
|
|
80
|
-
<div class="card"><b>4. 你收结果</b><p>负责人汇总结论、文件路径、风险和遗留问题;不用自己挨个问。</p></div>
|
|
81
|
+
<div class="card"><b>小说团队</b><p>总编、大纲、写手、编辑、校对、审读。</p></div>
|
|
82
|
+
<div class="card"><b>视频团队</b><p>负责人、策划、脚本、剪辑、封面、发布。</p></div>
|
|
83
|
+
<div class="card"><b>开发团队</b><p>负责人、开发、测试、验收。</p></div>
|
|
81
84
|
</div>
|
|
85
|
+
<p class="note" style="margin-top:14px">第一次不需要自己想清楚角色。你只说“帮我建个小说创作团队,按小说创作的流程建立工作流程与机制”,负责人会先给团队结构和流程建议。</p>
|
|
82
86
|
</section>
|
|
83
|
-
<section>
|
|
84
|
-
<h2>首次使用:让负责人自己组队</h2>
|
|
85
|
-
<pre>使用 dsh-agent-lines。先把这个会话设为开发团队的管理线;如果还没绑定,就先完成绑定。
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
<section id="install">
|
|
89
|
+
<h2>安装:可以让 DSH 帮你装</h2>
|
|
90
|
+
<p>在 DSH 新会话里说:</p>
|
|
91
|
+
<pre>帮我检查并安装 GitHub 上 Lencen/dsh-agent-lines 插件。
|
|
92
|
+
只从官方仓库或官方 npm 安装,不要从其他 fork 安装。
|
|
93
|
+
安装前先告诉我准备执行什么命令,等我确认后再执行。
|
|
94
|
+
安装完成后按插件要求加载;如果需要重启 DSH Web,先告诉我重启后打开哪个地址、
|
|
95
|
+
怎么新开会话、第一句该说什么,并提醒当前会话会断开;等我确认后再重启。</pre>
|
|
96
|
+
<div class="warn">重启 DSH Web 会让当前会话断开,重启后 agent 无法再自动通知你;重启后的步骤必须写在重启前。</div>
|
|
97
|
+
<details>
|
|
98
|
+
<summary>不想让 DSH 装?手动安装命令</summary>
|
|
99
|
+
<pre>curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh && bash install.sh</pre>
|
|
100
|
+
<p class="note">安装后必须重启 DSH Web,插件才会生效。</p>
|
|
101
|
+
</details>
|
|
102
|
+
</section>
|
|
103
|
+
|
|
104
|
+
<section id="start">
|
|
105
|
+
<h2>怎么开始(白话)</h2>
|
|
106
|
+
<div class="cards">
|
|
107
|
+
<div class="card"><b>1. 装好并重启</b><p>按上面的方式安装;需要时重启 DSH Web。</p></div>
|
|
108
|
+
<div class="card"><b>2. 新开一个会话</b><p>重启后新开会话,对负责人说要建什么团队。</p></div>
|
|
109
|
+
<div class="card"><b>3. 等它给方案</b><p>负责人先提团队结构和流程;你确认或补充角色。</p></div>
|
|
110
|
+
</div>
|
|
111
|
+
<p class="note" style="margin-top:14px">直接说:</p>
|
|
112
|
+
<pre>帮我建个小说创作团队,按小说创作的流程建立工作流程与机制。
|
|
113
|
+
或
|
|
114
|
+
帮我建个开发团队,按开发流程建立工作流程与机制。</pre>
|
|
115
|
+
<p class="note">如果你有明确要求,再补一句:</p>
|
|
116
|
+
<pre>另外加一个校对和一个审读角色。</pre>
|
|
117
|
+
</section>
|
|
88
118
|
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
<section>
|
|
120
|
+
<h2>第一次使用提示词</h2>
|
|
121
|
+
<p class="note">小说:</p>
|
|
122
|
+
<pre>帮我建个小说创作团队,按小说创作的流程建立工作流程与机制。
|
|
123
|
+
先不要写正文,建好后等我发第一章任务。</pre>
|
|
124
|
+
<p class="note">视频:</p>
|
|
125
|
+
<pre>帮我建个视频创作团队,按视频创作的流程建立工作流程与机制。
|
|
126
|
+
先不要开工,建好后等我发第一个选题。</pre>
|
|
127
|
+
<p class="note">开发:</p>
|
|
128
|
+
<pre>帮我建个开发团队,按开发流程建立工作流程与机制。
|
|
129
|
+
先不要开工,建好后等我发第一个需求。</pre>
|
|
130
|
+
</section>
|
|
91
131
|
|
|
92
|
-
|
|
93
|
-
<
|
|
132
|
+
<section>
|
|
133
|
+
<h2>常见问题</h2>
|
|
134
|
+
<div class="cards">
|
|
135
|
+
<div class="card"><b>会不会很贵?</b><p>按实际使用计费;负责人会先提最小可用团队,后续再扩。</p></div>
|
|
136
|
+
<div class="card"><b>关掉会丢吗?</b><p>不会,成员和消息保存在本地,重启后还能接着做。</p></div>
|
|
137
|
+
<div class="card"><b>能同时做两个团队吗?</b><p>当前一个插件实例只支持一个团队;小说和视频建议分开环境。</p></div>
|
|
138
|
+
</div>
|
|
94
139
|
</section>
|
|
95
|
-
|
|
140
|
+
|
|
141
|
+
<footer>
|
|
142
|
+
<details>
|
|
143
|
+
<summary>给开发者:协议、限制、安装命令</summary>
|
|
144
|
+
<p><a href="https://github.com/Lencen/dsh-agent-lines">GitHub 仓库</a> · <a href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.4.0">Release</a> · <a href="https://github.com/Lencen/dsh-agent-lines/blob/main/docs/PROTOCOL.md">协议</a></p>
|
|
145
|
+
<p class="note">社区插件,不是 DeepSeek 官方产品。技术细节以 README / 协议为准。</p>
|
|
146
|
+
</details>
|
|
147
|
+
<p style="margin-top:18px">dsh-agent-lines 0.4.0 · Apache-2.0 · Community plugin for DeepSeek Harness. Not an official DeepSeek product.</p>
|
|
148
|
+
</footer>
|
|
96
149
|
</main>
|
|
97
150
|
</div>
|
|
98
151
|
</body>
|
|
99
|
-
</html>
|
|
152
|
+
</html>
|
package/docs/market-index.json
CHANGED
package/lib/store.js
CHANGED
|
@@ -62,6 +62,11 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
62
62
|
root_message_id TEXT,
|
|
63
63
|
hop INTEGER NOT NULL DEFAULT 0,
|
|
64
64
|
no_reply INTEGER NOT NULL DEFAULT 0,
|
|
65
|
+
kind TEXT NOT NULL DEFAULT 'info',
|
|
66
|
+
cc_manager INTEGER NOT NULL DEFAULT 0,
|
|
67
|
+
approval_state TEXT,
|
|
68
|
+
approved_by TEXT,
|
|
69
|
+
approved_at INTEGER,
|
|
65
70
|
state TEXT NOT NULL,
|
|
66
71
|
claimed_by TEXT,
|
|
67
72
|
claimed_at INTEGER,
|
|
@@ -115,7 +120,7 @@ CREATE TABLE IF NOT EXISTS line_events (
|
|
|
115
120
|
);
|
|
116
121
|
CREATE INDEX IF NOT EXISTS line_events_line ON line_events(line_id, created_at);
|
|
117
122
|
|
|
118
|
-
PRAGMA user_version =
|
|
123
|
+
PRAGMA user_version = 8;
|
|
119
124
|
`
|
|
120
125
|
|
|
121
126
|
export function defaultDbPath() {
|
|
@@ -168,9 +173,14 @@ export class LineStore {
|
|
|
168
173
|
if (!messageColumns.includes('root_message_id')) this.db.exec('ALTER TABLE messages ADD COLUMN root_message_id TEXT')
|
|
169
174
|
if (!messageColumns.includes('hop')) this.db.exec('ALTER TABLE messages ADD COLUMN hop INTEGER NOT NULL DEFAULT 0')
|
|
170
175
|
if (!messageColumns.includes('no_reply')) this.db.exec('ALTER TABLE messages ADD COLUMN no_reply INTEGER NOT NULL DEFAULT 0')
|
|
176
|
+
if (!messageColumns.includes('kind')) this.db.exec("ALTER TABLE messages ADD COLUMN kind TEXT NOT NULL DEFAULT 'info'")
|
|
177
|
+
if (!messageColumns.includes('cc_manager')) this.db.exec('ALTER TABLE messages ADD COLUMN cc_manager INTEGER NOT NULL DEFAULT 0')
|
|
178
|
+
if (!messageColumns.includes('approval_state')) this.db.exec('ALTER TABLE messages ADD COLUMN approval_state TEXT')
|
|
179
|
+
if (!messageColumns.includes('approved_by')) this.db.exec('ALTER TABLE messages ADD COLUMN approved_by TEXT')
|
|
180
|
+
if (!messageColumns.includes('approved_at')) this.db.exec('ALTER TABLE messages ADD COLUMN approved_at INTEGER')
|
|
171
181
|
this.db.exec('CREATE TABLE IF NOT EXISTS line_events (id TEXT PRIMARY KEY, line_id TEXT NOT NULL, action TEXT NOT NULL, old_session_id TEXT, new_session_id TEXT, actor TEXT, reason TEXT, detail TEXT, created_at INTEGER NOT NULL)')
|
|
172
182
|
this.db.exec('CREATE INDEX IF NOT EXISTS line_events_line ON line_events(line_id, created_at)')
|
|
173
|
-
this.db.exec('PRAGMA user_version =
|
|
183
|
+
this.db.exec('PRAGMA user_version = 8')
|
|
174
184
|
this.prepared = new Map()
|
|
175
185
|
this.closed = false
|
|
176
186
|
this.fresh = fresh
|
|
@@ -265,7 +275,10 @@ export class LineStore {
|
|
|
265
275
|
}
|
|
266
276
|
|
|
267
277
|
/** Enqueue a message from one registered line to another. */
|
|
268
|
-
send({ agentId, fromLine, to, text, idempotencyKey, replyTo, noReply = false, maxReplyDepth = 8, maxMessageBytes = 65536, ttlMs, now = Date.now() }) {
|
|
278
|
+
send({ agentId, fromLine, to, text, idempotencyKey, replyTo, noReply = false, kind = 'info', ccManager = false, requireApproval = false, maxReplyDepth = 8, maxMessageBytes = 65536, ttlMs, now = Date.now() }) {
|
|
279
|
+
if (!['info', 'request', 'task', 'alert'].includes(String(kind))) {
|
|
280
|
+
throw new LineError('LINE_INVALID_KIND', `kind must be info/request/task/alert: ${kind}`)
|
|
281
|
+
}
|
|
269
282
|
const from = this.#stmt('SELECT * FROM lines WHERE agent_id = ? AND label = ?').get(agentId, fromLine)
|
|
270
283
|
if (from === undefined) throw new LineError('LINE_NOT_FOUND', `Sender line is not registered: ${agentId}#${fromLine}`)
|
|
271
284
|
const target = this.resolveTarget(agentId, to)
|
|
@@ -290,9 +303,9 @@ export class LineStore {
|
|
|
290
303
|
const expiresAt = ttlMs === undefined || ttlMs === null || ttlMs <= 0 ? null : now + ttlMs
|
|
291
304
|
this.#stmt(`INSERT INTO messages(
|
|
292
305
|
id, idempotency_key, from_agent, from_line, from_session, to_agent, to_line, body,
|
|
293
|
-
created_at, expires_at, reply_to, root_message_id, hop, no_reply, state, attempts, next_attempt_at
|
|
294
|
-
) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
295
|
-
.run(id, key, from.agent_id, from.label, from.session_id, target.agentId, target.line, body, now, expiresAt, replyTo ?? null, rootMessageId, hop, noReply === true ? 1 : 0, now)
|
|
306
|
+
created_at, expires_at, reply_to, root_message_id, hop, no_reply, kind, cc_manager, approval_state, state, attempts, next_attempt_at
|
|
307
|
+
) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?)`)
|
|
308
|
+
.run(id, key, from.agent_id, from.label, from.session_id, target.agentId, target.line, body, now, expiresAt, replyTo ?? null, rootMessageId, hop, noReply === true ? 1 : 0, String(kind), ccManager === true ? 1 : 0, requireApproval ? 'pending' : null, requireApproval ? 'pending_approval' : 'queued', now)
|
|
296
309
|
return { ...this.#stmt('SELECT * FROM messages WHERE id = ?').get(id), deduplicated: false }
|
|
297
310
|
}
|
|
298
311
|
|
|
@@ -542,12 +555,28 @@ export class LineStore {
|
|
|
542
555
|
return this.#stmt('SELECT * FROM line_events WHERE line_id = ? AND action = ? ORDER BY created_at DESC LIMIT 1').get(id, action)
|
|
543
556
|
}
|
|
544
557
|
|
|
558
|
+
/** Manager decision on a pending task message: approve queues it, reject closes it. */
|
|
559
|
+
approveMessage({ messageId, decision = 'approve', actor = null, now = Date.now() }) {
|
|
560
|
+
if (!['approve', 'reject'].includes(String(decision))) throw new LineError('LINE_INVALID_ARGUMENT', 'decision must be approve or reject')
|
|
561
|
+
return this.#tx(() => {
|
|
562
|
+
const row = this.#stmt('SELECT * FROM messages WHERE id = ?').get(String(messageId))
|
|
563
|
+
if (row === undefined) throw new LineError('LINE_MESSAGE_NOT_FOUND', `No message: ${messageId}`)
|
|
564
|
+
if (row.state !== 'pending_approval') throw new LineError('LINE_NOT_PENDING_APPROVAL', `Message ${messageId} is ${row.state}`)
|
|
565
|
+
const approved = String(decision) === 'approve'
|
|
566
|
+
this.#stmt(`UPDATE messages SET state = ?, approval_state = ?, approved_by = ?, approved_at = ?
|
|
567
|
+
WHERE id = ? AND state = 'pending_approval'`).run(
|
|
568
|
+
approved ? 'queued' : 'rejected', approved ? 'approved' : 'rejected', actor, now, row.id,
|
|
569
|
+
)
|
|
570
|
+
return { ...this.#stmt('SELECT * FROM messages WHERE id = ?').get(row.id), approved }
|
|
571
|
+
})
|
|
572
|
+
}
|
|
573
|
+
|
|
545
574
|
/** Read-only cross-line feed for the manager; no state changes. */
|
|
546
575
|
observe({ agentId, line = null, since = 0, limit = 100 } = {}) {
|
|
547
576
|
const n = Math.min(200, Math.max(1, Number(limit) || 100))
|
|
548
577
|
const from = Math.max(0, Number(since) || 0)
|
|
549
578
|
const params = [agentId, agentId]
|
|
550
|
-
let sql = `SELECT id, from_agent, from_line, to_agent, to_line, body, state, reply_to, root_message_id,
|
|
579
|
+
let sql = `SELECT id, from_agent, from_line, to_agent, to_line, body, state, kind, cc_manager, approval_state, reply_to, root_message_id,
|
|
551
580
|
hop, no_reply, created_at, delivered_at, acked_at
|
|
552
581
|
FROM messages WHERE (from_agent = ? OR to_agent = ?) AND from_line != to_line AND created_at > ?`
|
|
553
582
|
params.push(from)
|
|
@@ -565,6 +594,9 @@ export class LineStore {
|
|
|
565
594
|
from: row.from_agent + '#' + row.from_line,
|
|
566
595
|
to: row.to_agent + '#' + row.to_line,
|
|
567
596
|
state: row.state,
|
|
597
|
+
kind: row.kind,
|
|
598
|
+
ccManager: Number(row.cc_manager) === 1,
|
|
599
|
+
approvalState: row.approval_state,
|
|
568
600
|
noReply: Number(row.no_reply) === 1,
|
|
569
601
|
replyTo: row.reply_to,
|
|
570
602
|
hop: row.hop,
|
package/lib/tools.js
CHANGED
|
@@ -20,11 +20,12 @@ function currentLine(exec, lineDefs) {
|
|
|
20
20
|
return def
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function windowKey({ from, to, text, replyTo, windowMs, now = Date.now() }) {
|
|
23
|
+
function windowKey({ from, to, text, replyTo, kind = 'info', ccManager = false, windowMs, now = Date.now() }) {
|
|
24
24
|
if (windowMs <= 0) return undefined
|
|
25
25
|
const bucket = Math.floor(now / windowMs)
|
|
26
26
|
const digest = createHash('sha256')
|
|
27
27
|
.update(from).update('\0').update(to).update('\0').update(text).update('\0').update(String(replyTo ?? ''))
|
|
28
|
+
.update('\0').update(String(kind)).update('\0').update(ccManager ? 'cc' : '')
|
|
28
29
|
.digest('hex')
|
|
29
30
|
return `window:${bucket}:${digest}`
|
|
30
31
|
}
|
|
@@ -208,6 +209,8 @@ export function registerToolsForAgent(agent, def, { config, store, lineDefs, cre
|
|
|
208
209
|
idempotencyKey: { type: 'string', description: 'Optional stable key for retry-safe sends' },
|
|
209
210
|
replyTo: { type: 'string', description: 'Optional message id this message replies to; blocked if that message is noReply and checked against maxReplyDepth' },
|
|
210
211
|
noReply: { type: 'boolean', description: 'Mark this message as a notification; replies are rejected' },
|
|
212
|
+
kind: { type: 'string', enum: ['info', 'request', 'task', 'alert'], description: 'Message intent; non-manager task messages wait for line_manager_ack approval' },
|
|
213
|
+
ccManager: { type: 'boolean', description: 'Mark this message as manager-visible; defaults true for task/alert' },
|
|
211
214
|
forceDistinct: { type: 'boolean', description: 'Allow two identical messages inside dedupeWindowMs' },
|
|
212
215
|
ttlMs: { type: 'number', description: 'Optional time-to-live in milliseconds' },
|
|
213
216
|
},
|
|
@@ -222,9 +225,14 @@ export function registerToolsForAgent(agent, def, { config, store, lineDefs, cre
|
|
|
222
225
|
if (target.lineId === def.lineId && config.allowSelfSend !== true) {
|
|
223
226
|
throw new LineError('LINE_SELF_SEND_FORBIDDEN', `Self-send is disabled; set allowSelfSend to enable: ${def.lineId}`)
|
|
224
227
|
}
|
|
228
|
+
const kind = args.kind === undefined ? 'info' : String(args.kind)
|
|
229
|
+
if (!['info', 'request', 'task', 'alert'].includes(kind)) throw new LineError('LINE_INVALID_KIND', `kind must be info/request/task/alert: ${kind}`)
|
|
230
|
+
const isManager = def.label === config.createFrom
|
|
231
|
+
const ccManager = args.ccManager === undefined ? (kind === 'task' || kind === 'alert') : args.ccManager === true
|
|
232
|
+
const requireApproval = kind === 'task' && !isManager
|
|
225
233
|
const idempotencyKey = args.idempotencyKey ?? (args.forceDistinct === true
|
|
226
234
|
? undefined
|
|
227
|
-
: windowKey({ from: def.lineId, to: target.lineId, text, replyTo: args.replyTo, windowMs: config.dedupeWindowMs }))
|
|
235
|
+
: windowKey({ from: def.lineId, to: target.lineId, text, replyTo: args.replyTo, kind, ccManager, windowMs: config.dedupeWindowMs }))
|
|
228
236
|
const row = store.send({
|
|
229
237
|
agentId: def.agentId,
|
|
230
238
|
fromLine: def.label,
|
|
@@ -233,6 +241,7 @@ export function registerToolsForAgent(agent, def, { config, store, lineDefs, cre
|
|
|
233
241
|
idempotencyKey,
|
|
234
242
|
replyTo: args.replyTo,
|
|
235
243
|
noReply: args.noReply === true,
|
|
244
|
+
kind, ccManager, requireApproval,
|
|
236
245
|
maxReplyDepth: config.maxReplyDepth,
|
|
237
246
|
ttlMs: args.ttlMs,
|
|
238
247
|
maxMessageBytes: config.maxMessageBytes,
|
|
@@ -244,6 +253,10 @@ export function registerToolsForAgent(agent, def, { config, store, lineDefs, cre
|
|
|
244
253
|
hop: row.hop,
|
|
245
254
|
rootMessageId: row.root_message_id,
|
|
246
255
|
noReply: Number(row.no_reply) === 1,
|
|
256
|
+
kind: row.kind,
|
|
257
|
+
ccManager: Number(row.cc_manager) === 1,
|
|
258
|
+
approvalState: row.approval_state ?? null,
|
|
259
|
+
requiresApproval: row.state === 'pending_approval',
|
|
247
260
|
deduplicated: row.deduplicated === true,
|
|
248
261
|
}
|
|
249
262
|
},
|
|
@@ -398,6 +411,26 @@ export function registerToolsForAgent(agent, def, { config, store, lineDefs, cre
|
|
|
398
411
|
},
|
|
399
412
|
}))
|
|
400
413
|
|
|
414
|
+
disposers.push(agent.ctx.tools.register({
|
|
415
|
+
name: 'line_manager_ack',
|
|
416
|
+
description: 'Manager-only decision for a pending task message: approve queues it for delivery, reject closes it. Use line_observe to find pending approvals.',
|
|
417
|
+
parameters: {
|
|
418
|
+
type: 'object', additionalProperties: false,
|
|
419
|
+
properties: {
|
|
420
|
+
messageId: { type: 'string', description: 'Pending task message id' },
|
|
421
|
+
decision: { type: 'string', enum: ['approve', 'reject'], description: 'Approve queues the message; reject closes it' },
|
|
422
|
+
},
|
|
423
|
+
required: ['messageId', 'decision'],
|
|
424
|
+
},
|
|
425
|
+
output: OUTPUT,
|
|
426
|
+
execute: async (args, exec) => {
|
|
427
|
+
const caller = currentLine(exec, lineDefs)
|
|
428
|
+
if (caller.label !== config.createFrom) throw new LineError('LINE_CREATE_FORBIDDEN', `Only ${config.createFrom} may approve tasks`, { caller: caller.label })
|
|
429
|
+
const row = store.approveMessage({ messageId: String(args.messageId), decision: String(args.decision), actor: caller.label })
|
|
430
|
+
return { messageId: row.id, state: row.state, approvalState: row.approval_state, approved: row.approved }
|
|
431
|
+
},
|
|
432
|
+
}))
|
|
433
|
+
|
|
401
434
|
disposers.push(agent.ctx.tools.register({
|
|
402
435
|
name: 'line_observe',
|
|
403
436
|
description: 'Manager-only read-only feed of recent cross-line messages (from/to, state, noReply, summary). Does not inject prompts or change any message state. Use it to watch team traffic without turning every message into a manager turn.',
|