dsh-agent-lines 0.3.8 → 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 CHANGED
@@ -2,6 +2,21 @@
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
+
13
+ ## 0.3.9 (2026-11-09)
14
+
15
+ - Added read-only line_observe: a manager feed of cross-line messages without prompts or state changes.
16
+ - Added CLI observe --json.
17
+ - README now documents version-by-version business scenarios; fixed line_handoff version (0.3.7).
18
+ - No schema change; line_send semantics unchanged.
19
+
5
20
  ## 0.3.8 (2026-11-09)
6
21
 
7
22
  - line_status / line_list expose activity: turns/steps/decodeTokens/level/lastActivityAt.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
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
+
13
+ ## 0.3.9 (2026-11-09)
14
+
15
+ - 新增只读 line_observe:管理线查看跨线消息 feed,不注入 prompt、不改消息状态。
16
+ - CLI 新增 observe --json。
17
+ - README 补“版本能力与业务场景”,修正 line_handoff 的版本标注(0.3.7)。
18
+ - 无 schema 变化,line_send 语义不变。
19
+
5
20
  ## 0.3.8 (2026-11-09)
6
21
 
7
22
  - line_status / line_list 增加 activity:turns/steps/decodeTokens/level/lastActivityAt。
package/README.en.md CHANGED
@@ -23,6 +23,44 @@ 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
+
38
+ ## What each version adds (business scenarios)
39
+
40
+ ### 0.3.6: replace a role when context is full
41
+
42
+ Scenario: a writer line has worked long enough that its context is saturated and quality drops.
43
+ Use: the manager calls line_create({ label: "writer", replace: true, force: true, reason: "context full" }).
44
+ Effect: the same label moves to a clean Session; the old Session is retired but kept; queued/claimed messages migrate; delivered/acked are never re-delivered; no manual routing.
45
+
46
+ ### 0.3.7: hand off before replacing
47
+
48
+ Scenario: the new writer does not know what the old line finished, what is next, or which traps to avoid.
49
+ Use: line_handoff({ line: "writer", format: "md" }).
50
+ Effect: a structured snapshot (progress, backlog, recent messages, lifecycle) without a model call; the new line reads it first.
51
+
52
+ ### 0.3.8: know when to replace
53
+
54
+ Scenario: the manager wants to see which line is close to saturation or has gone quiet.
55
+ Use: line_status / line_list expose activity.level, turns, steps, lastActivityAt.
56
+ Effect: a low/medium/high activity proxy, not a fake "78% context used"; exact context usage waits for DSH.
57
+
58
+ ### 0.4.0: the manager can see cross-line traffic
59
+
60
+ Scenario: lines message each other directly; the manager needs visibility without every message becoming a manager turn.
61
+ Use: line_observe({ line: "writer", limit: 20 }) or CLI observe --json.
62
+ Effect: a read-only feed with from/to/state/summary; no prompt injection and no state change; line_send semantics unchanged.
63
+
26
64
  ## Install
27
65
 
28
66
  One-command install (recommended; no need to find a profile):
@@ -36,7 +74,7 @@ The script picks the DSH Web profile, installs the plugin, and verifies agent-li
36
74
 
37
75
  Offline or when npm is unavailable, use the local tarball:
38
76
 
39
- bash install.sh ./dsh-agent-lines-0.3.8.tgz
77
+ bash install.sh ./dsh-agent-lines-0.4.0.tgz
40
78
 
41
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.
42
80
 
@@ -139,11 +177,13 @@ Tools:
139
177
  | `line_list` | List lines, status, unread counts, current line and role |
140
178
  | `line_create` | Manager only: create a line; same label is idempotent; replace:true swaps the label to a fresh Session |
141
179
  | `line_handoff` | Manager only: structured handoff snapshot (JSON/Markdown), no model call |
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 |
142
182
  | `line_retire` | Manager only: soft-retire; reject new sends, keep Session and history |
143
183
  | `line_rebind` | Manager only: atomically switch a line to a new live Session, optionally migrating backlog |
144
184
  | `line_rename` | Manager only: rename the sidebar title and persist it in lines.db |
145
185
  | `line_move_workspace` | Manager only: attach an existing line Session to a workspace whose path exactly matches the Session cwd |
146
- | `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` |
147
187
  | `line_inbox` | Read delivered messages for the current line |
148
188
  | `line_ack` | Acknowledge a delivered message |
149
189
 
@@ -155,7 +195,7 @@ Line tools other than `line_status` and `line_claim_manager` are registered only
155
195
 
156
196
  A line does not inherit the old Session context. Recommended flow:
157
197
 
158
- 1. Have the old line write a handoff file (line_handoff is available from 0.3.8);
198
+ 1. Have the old line write a handoff file (line_handoff is available from 0.3.7);
159
199
  2. The manager calls `line_create({ label, replace: true, force: true, reason: "context full" })`;
160
200
  3. The plugin creates a clean Session, atomically swaps the same label, and marks the old Session retired but keeps it;
161
201
  4. queued/claimed messages migrate to the new line; delivered/acked stay on the old Session and are never re-delivered.
@@ -171,6 +211,7 @@ dsh-agent-lines list
171
211
  dsh-agent-lines send --from frontend-line --to backend-line --text "hi"
172
212
  dsh-agent-lines inbox --line backend-line
173
213
  dsh-agent-lines ack --line backend-line --id msg_...
214
+ dsh-agent-lines --json observe --line writer --limit 20
174
215
  dsh-agent-lines claim --agent agent-demo --line manager --session "$DSH_SESSION_ID"
175
216
  dsh-agent-lines doctor
176
217
  ```
@@ -235,6 +276,19 @@ dsh-agent-lines claim --agent agent-demo --line manager --session "$DSH_SESSION_
235
276
 
236
277
  Claim is never automatic for every new Session: it needs an explicit user request or an explicit CLI call.
237
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
+
238
292
  ## Echo and duplicate protection
239
293
 
240
294
  - Same `idempotencyKey` returns the existing message.
package/README.md CHANGED
@@ -23,8 +23,62 @@ DSH 的 super agent 平替插件。
23
23
  - 你希望一个负责人 agent 自己拆任务、建团队、追进度、收结果,最后向你汇报。
24
24
 
25
25
 
26
+ ## 团队类型不限于开发
27
+
28
+ 插件不限定团队类型。本质是“一个负责人 + 若干长期成员”的协作方式。
29
+ 下面用开发团队做示例;小说、视频、研究、运营团队只换角色和任务,不需要改插件或代码。
30
+
31
+ - 小说:总编、大纲、写手、编辑、校对、审读;
32
+ - 视频:负责人、策划、脚本、剪辑、封面、发布;
33
+ - 开发:负责人、开发、测试、验收;
34
+ - 研究:负责人、资料、分析、写作、审阅。
35
+
36
+ 第一次不需要自己想清楚角色:直接对负责人说“帮我建个小说创作团队,按小说创作的流程建立工作流程与机制”,它会先给团队结构和流程建议,你确认或补充即可。
37
+
38
+ ## 版本能力与业务场景
39
+
40
+ ### 0.3.6:上下文满了,角色可以换人
41
+
42
+ 场景:写手线连续工作很久,上下文配额耗尽;继续写容易出现前后矛盾。
43
+ 用法:管理线调用 line_create({ label: "writer", replace: true, force: true, reason: "context full" })。
44
+ 效果:同一个 label 切到干净 Session,旧 Session 只 retired、不删除;queued/claimed 消息迁移,delivered/acked 不重投;管理线不需要手工改路由。
45
+
46
+ ### 0.3.7:换人之前先交接
47
+
48
+ 场景:新写手不知道旧线做到哪、下一步是什么、哪些坑不能踩。
49
+ 用法:line_handoff({ line: "writer", format: "md" })。
50
+ 效果:生成结构化交接单(进度、积压、最近消息、生命周期),不调用模型;新线先读交接单再继续。
51
+
52
+ ### 0.3.8:什么时候该换线
53
+
54
+ 场景:管理线想知道哪条线快满了、哪条线最近没动静。
55
+ 用法:line_status / line_list 看 activity.level、turns、steps、lastActivityAt。
56
+ 效果:给出低/中/高活跃度,不伪造“上下文使用率 78%”;真实窗口等 DSH 上游提供。
57
+
58
+ ### 0.4.0:管理线看得到跨线消息
59
+
60
+ 场景:成员线之间直接同步,管理线需要知道发生了什么,但不希望每条消息都打断自己。
61
+ 用法:line_observe({ line: "writer", limit: 20 }) 或 CLI observe --json。
62
+ 效果:只读 feed,显示 from/to/state/摘要;不注入 prompt、不改消息状态;line_send 语义不变。
63
+
26
64
  ## 安装
27
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
+
28
82
  一条命令安装(推荐,不需要自己找 profile):
29
83
 
30
84
  curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh
@@ -36,7 +90,7 @@ DSH 的 super agent 平替插件。
36
90
 
37
91
  离线或 npm 不可用时,用本地 tarball:
38
92
 
39
- bash install.sh ./dsh-agent-lines-0.3.8.tgz
93
+ bash install.sh ./dsh-agent-lines-0.4.0.tgz
40
94
 
41
95
  安装并重启后,不写配置也可以直接使用;需要固定管理线/worker 时,再把下面“管理线配置”粘贴到脚本打印的配置文件。
42
96
 
@@ -144,11 +198,13 @@ level 是活跃度代理(low/medium/high),不是上下文使用率百分
144
198
  | `line_list` | 列出线、状态、未读数、当前线和 role |
145
199
  | `line_create` | 仅管理线:创建新线;同 label 幂等;传 replace:true 可给同 label 换新 Session |
146
200
  | `line_handoff` | 仅管理线:生成结构化交接单(JSON/Markdown),不调模型 |
201
+ | `line_observe` | 仅管理线:只读跨线消息 feed;显示 kind/审批状态,不注入 prompt、不改状态 |
202
+ | `line_manager_ack` | 仅管理线:对 pending_approval 的 task 做 approve/reject |
147
203
  | `line_retire` | 仅管理线:软下线;拒绝新发送,保留 Session/历史 |
148
204
  | `line_rebind` | 仅管理线:把线原子切到新的 live Session,可迁移积压 |
149
205
  | `line_rename` | 仅管理线:改侧栏标题,持久化到 lines.db |
150
206
  | `line_move_workspace` | 仅管理线:把已有线的 Session 挂到路径完全匹配的工作区 |
151
- | `line_send` | 发到 `line` 或 `agent#line`;可选 `idempotencyKey`、`replyTo`、`noReply`、`forceDistinct`、`ttlMs` |
207
+ | `line_send` | 发到 `line` 或 `agent#line`;可选 `kind`、`ccManager`、`idempotencyKey`、`replyTo`、`noReply`、`forceDistinct`、`ttlMs` |
152
208
  | `line_inbox` | 读当前线已投递消息 |
153
209
  | `line_ack` | 确认已处理 |
154
210
 
@@ -160,7 +216,7 @@ level 是活跃度代理(low/medium/high),不是上下文使用率百分
160
216
 
161
217
  线不会自动继承旧 Session 的上下文。推荐流程:
162
218
 
163
- 1. 让旧线写交接单(0.3.8 起可用 `line_handoff`);
219
+ 1. 让旧线写交接单(0.3.7 起可用 `line_handoff`);
164
220
  2. 管理线调用 `line_create({ label, replace: true, force: true, reason: "context full" })`;
165
221
  3. 插件创建干净 Session、同 label 原子切换,旧 Session 标记 retired 但保留;
166
222
  4. queued/claimed 消息迁移到新线;delivered/acked 留在旧 Session,不重投。
@@ -176,6 +232,7 @@ dsh-agent-lines list
176
232
  dsh-agent-lines send --from frontend-line --to backend-line --text "hi"
177
233
  dsh-agent-lines inbox --line backend-line
178
234
  dsh-agent-lines ack --line backend-line --id msg_...
235
+ dsh-agent-lines --json observe --line writer --limit 20
179
236
  dsh-agent-lines claim --agent agent-demo --line manager --session "$DSH_SESSION_ID"
180
237
  dsh-agent-lines doctor
181
238
  ```
@@ -233,6 +290,19 @@ dsh-agent-lines claim --agent agent-demo --line manager --session "$DSH_SESSION_
233
290
 
234
291
  claim 不会对每个新会话自动执行:需要用户明确要求,或运维显式调用。
235
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
+
236
306
  ## 回声与重复保护
237
307
 
238
308
  - 同 `idempotencyKey` 返回已有消息。
@@ -9,6 +9,7 @@ import { LineError, lineId } from '../lib/protocol.js'
9
9
 
10
10
  function usage() {
11
11
  console.error(`Usage:
12
+ dsh-agent-lines [--db <path>] [--json] observe [--line <line>] [--since <epochMs>] [--limit N] [--agent <id>]
12
13
  dsh-agent-lines [--db <path>] [--json] list
13
14
  dsh-agent-lines [--db <path>] [--json] send --from <line> --to <line|agent#line> --text <text> [--agent <id>] [--key <key>] [--reply-to <id>]
14
15
  dsh-agent-lines [--db <path>] [--json] inbox --line <line> [--agent <id>] [--limit N] [--all]
@@ -62,6 +63,16 @@ function main() {
62
63
  }
63
64
  const store = new LineStore(opts.db)
64
65
  try {
66
+ if (command === 'observe') {
67
+ const result = store.observe({
68
+ agentId: String(opts.agent ?? 'agent-deepseek'),
69
+ line: opts.line === undefined ? null : String(opts.line),
70
+ since: Number(opts.since ?? 0),
71
+ limit: Number(opts.limit ?? 100),
72
+ })
73
+ print({ db: store.dbPath, ...result }, opts.json)
74
+ return 0
75
+ }
65
76
  if (command === 'list') {
66
77
  print({ db: store.dbPath, lines: store.listLines(), counts: store.counts() }, opts.json)
67
78
  return 0
@@ -152,4 +163,4 @@ function main() {
152
163
  }
153
164
  }
154
165
 
155
- process.exitCode = main()
166
+ process.exitCode = main()
@@ -103,6 +103,7 @@ The new line is an ordinary DSH Session in the same DSH process; no OS process,
103
103
  line_retire({ line, reason? })
104
104
  line_rebind({ line, sessionId, migrate?: "queued" | "none", reason? })
105
105
  line_create({ label, replace: true, force?, migrate?, title?, reason? })
106
+ line_observe({ line?, since?, limit? })
106
107
  line_handoff({ line, format?: "md" | "json", limit?: number })
107
108
  line_rename({ line, title })
108
109
 
@@ -112,6 +113,7 @@ The new line is an ordinary DSH Session in the same DSH process; no OS process,
112
113
  - migrate=none: returns LINE_BACKLOG_NOT_EMPTY if the backlog is non-empty.
113
114
  - replace: creates a fresh Session for the same label and rebinds; an active line requires force + reason.
114
115
  - line_events records title/retire/rebind audit; titles survive restarts.
116
+ - line_observe is a manager-only read-only cross-line feed; it does not inject prompts or change state.
115
117
 
116
118
  ## Echo and reply depth
117
119
 
package/docs/PROTOCOL.md CHANGED
@@ -95,6 +95,7 @@ line_move_workspace({ line, workspace }) 把已有线 Session 挂到已有工作
95
95
  line_retire({ line, reason? })
96
96
  line_rebind({ line, sessionId, migrate?: "queued" | "none", reason? })
97
97
  line_create({ label, replace: true, force?, migrate?, title?, reason? })
98
+ line_observe({ line?, since?, limit? })
98
99
  line_handoff({ line, format?: "md" | "json", limit?: number })
99
100
  line_rename({ line, title })
100
101
 
@@ -104,6 +105,7 @@ line_move_workspace({ line, workspace }) 把已有线 Session 挂到已有工作
104
105
  - migrate=none:backlog 非空时返回 LINE_BACKLOG_NOT_EMPTY。
105
106
  - replace:同 label 创建新 Session 并 rebind;active 线需 force + reason。
106
107
  - line_events 记录 title/retire/rebind 审计;title 持久化,重启保留。
108
+ - line_observe 仅管理线、只读跨线 feed;不注入 prompt、不改消息状态。
107
109
 
108
110
  ## 回声与回复深度
109
111
 
@@ -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 one AI lead run a team</title>
7
- <meta name="description" content="State the goal. One AI lead splits the work, creates team lines, tracks progress, and reports back. Each line keeps its own context; work survives a restart.">
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.7}
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:1080px;margin:0 auto;padding:0 24px}
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:72px 0 48px;display:grid;grid-template-columns:1.05fr .95fr;gap:42px;align-items:center}
20
- .kicker{color:var(--green);font-size:12px;letter-spacing:.16em;text-transform:uppercase}
21
- h1{font-size:48px;line-height:1.15;margin:14px 0 18px;letter-spacing:-.03em}
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:560px}
24
- .install{margin:26px 0;background:#0a0f16;border:1px solid var(--line);border-radius:10px;padding:14px 16px;font-family:"DejaVu Sans Mono",monospace;font-size:13px;color:#c9d1d9;overflow:auto}
25
- .actions{display:flex;gap:12px;flex-wrap:wrap}
26
- .btn{display:inline-block;padding:10px 16px;border-radius:9px;border:1px solid var(--line);color:var(--text);font-size:14px}
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:36px 0;border-top:1px solid #161d27}
32
- h2{font-size:22px;margin:0 0 18px}
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,.8);border:1px solid var(--line);border-radius:12px;padding:18px}
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
- footer{padding:34px 0 48px;color:var(--muted);font-size:12px}
39
- @media(max-width:820px){.hero{grid-template-columns:1fr;padding-top:44px}h1{font-size:36px}.cards{grid-template-columns:1fr}}
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.3.8">Release</a></nav>
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:22px">
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 one AI lead<br><span>run a team for you</span></h1>
53
- <p class="lead">You state the goal. The lead splits it into research, architecture, UI, development, test, acceptance, release, and ops lines; assigns work, tracks progress, and reports back. Close the window or restart DSH; the work and messages remain.</p>
54
- <div class="install">curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh &amp;&amp; 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="https://github.com/Lencen/dsh-agent-lines">GitHub repository</a>
57
- <a class="btn" href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.3.8">Download 0.3.8</a>
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="First use: create a manager line and 8 member lines, then receive acks">
62
- <div class="caption">demo profile · generic labels · no internal data</div>
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 takes off your plate</h2>
69
+ <h2>What it does for you</h2>
67
70
  <div class="cards">
68
- <div class="card"><b>No repeated context</b><p>Each member line is a long-lived DSH Session with its own context. Send new work to the right role instead of re-explaining the project.</p></div>
69
- <div class="card"><b>No manual dispatch</b><p>The lead decomposes work, assigns it, coordinates, resolves disagreements, and collects results. You read the summary.</p></div>
70
- <div class="card"><b>No lost work</b><p>Tasks and messages are stored locally. Restart DSH and continue; member state and unread messages are still there.</p></div>
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>A real usage pattern</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>1. You state the goal</b><p>“Add CSV export to the reports page, with tests and a short runbook.”</p></div>
78
- <div class="card"><b>2. The lead forms the team</b><p>Split into research, architecture, UI/UX, development, test, acceptance, release, and ops; create each line.</p></div>
79
- <div class="card"><b>3. The team works in parallel</b><p>Members do their parts; the lead tracks progress, resolves interface disagreements, and reassigns when needed.</p></div>
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
- You own task decomposition, assignment, coordination, progress tracking, result collection, and reporting. You manage and coordinate only; do not execute development tasks.
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 &amp;&amp; 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
- Form the team. Use short English names and Chinese roles:
90
- research (调研), architecture (架构), uiux (UIUX), dev (开发), test (测试), acceptance (验收), release (发布), ops (运维).
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
- After the team is ready, wait for my first task. Do not start by yourself.</pre>
93
- <p style="color:var(--muted);font-size:14px">Restart DSH Web after install or update. Technical details, protocol, and limits: <a href="https://github.com/Lencen/dsh-agent-lines">README / architecture</a>. Community plugin, not an official DeepSeek product.</p>
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
- <footer>dsh-agent-lines 0.3.8 · Apache-2.0 · Community plugin for DeepSeek Harness. Not an official DeepSeek product.</footer>
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 负责人带一支 AI 团队</title>
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.7}
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:1080px;margin:0 auto;padding:0 24px}
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:72px 0 48px;display:grid;grid-template-columns:1.05fr .95fr;gap:42px;align-items:center}
20
- .kicker{color:var(--green);font-size:12px;letter-spacing:.16em;text-transform:uppercase}
21
- h1{font-size:48px;line-height:1.15;margin:14px 0 18px;letter-spacing:-.03em}
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:560px}
24
- .install{margin:26px 0;background:#0a0f16;border:1px solid var(--line);border-radius:10px;padding:14px 16px;font-family:"DejaVu Sans Mono",monospace;font-size:13px;color:#c9d1d9;overflow:auto}
25
- .actions{display:flex;gap:12px;flex-wrap:wrap}
26
- .btn{display:inline-block;padding:10px 16px;border-radius:9px;border:1px solid var(--line);color:var(--text);font-size:14px}
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:36px 0;border-top:1px solid #161d27}
32
- h2{font-size:22px;margin:0 0 18px}
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,.8);border:1px solid var(--line);border-radius:12px;padding:18px}
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
- footer{padding:34px 0 48px;color:var(--muted);font-size:12px}
39
- @media(max-width:820px){.hero{grid-template-columns:1fr;padding-top:44px}h1{font-size:36px}.cards{grid-template-columns:1fr}}
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.3.8">Release</a></nav>
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:22px">
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>让一个 AI 负责人<br><span>替你带一支 AI 团队</span></h1>
53
- <p class="lead">你只管说目标。负责人会拆任务、建调研 / 架构 / 开发 / 测试等成员线、分配工作、追进度、收结果,最后给你一份汇总。关掉窗口或重启 DSH,任务和消息还在。</p>
54
- <div class="install">curl -fsSL https://raw.githubusercontent.com/Lencen/dsh-agent-lines/main/install.sh -o install.sh &amp;&amp; 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="https://github.com/Lencen/dsh-agent-lines">GitHub 仓库</a>
57
- <a class="btn" href="https://github.com/Lencen/dsh-agent-lines/releases/tag/v0.3.8">下载 0.3.8</a>
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="首次使用:创建管理线、8 条成员线并收到 ack">
62
- <div class="caption">demo profile · 通用 label · 不含内部数据</div>
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>它替你省掉什么</h2>
69
+ <h2>它能帮你做什么</h2>
67
70
  <div class="cards">
68
- <div class="card"><b>不用反复讲背景</b><p>每条成员线是一个独立的 DSH 会话,长期保留自己的上下文;新任务直接派给对应角色。</p></div>
69
- <div class="card"><b>不用自己当调度</b><p>负责人拆任务、分配、协调、处理分歧、收结果;你只需要看最后的汇总。</p></div>
70
- <div class="card"><b>不用怕关窗口</b><p>任务和消息保存在本地;重启 DSH 后还能接着做,成员状态和未处理消息都在。</p></div>
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>一个真实用法</h2>
78
+ <h2>适合什么团队</h2>
79
+ <p class="note">不限于开发。小说、视频、研究、运营都可以;只换角色和任务,不需要改插件。</p>
76
80
  <div class="cards">
77
- <div class="card"><b>1. 你说目标</b><p>“给报表页加 CSV 导出,带测试和一页 runbook。”</p></div>
78
- <div class="card"><b>2. 负责人组队</b><p>拆成调研、架构、UIUX、开发、测试、验收、发布、运维,按角色建线。</p></div>
79
- <div class="card"><b>3. 团队并行</b><p>成员各自做事;负责人追进度、处理接口分歧,必要时重新分配。</p></div>
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 &amp;&amp; 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
- research(调研)、architecture(架构)、uiux(UIUX)、dev(开发)、test(测试)、acceptance(验收)、release(发布)、ops(运维)。
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
- 团队建好后先待命,不要自动开工,等我发第一个任务。</pre>
93
- <p style="color:var(--muted);font-size:14px">安装或更新后需要重启 DSH Web。技术细节、协议和限制见 <a href="https://github.com/Lencen/dsh-agent-lines">README / 架构文档</a>。社区插件,不是 DeepSeek 官方产品。</p>
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
- <footer>dsh-agent-lines 0.3.8 · Apache-2.0 · Community plugin for DeepSeek Harness. Not an official DeepSeek product.</footer>
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>
@@ -50,11 +50,11 @@
50
50
  ]
51
51
  },
52
52
  "dist-tags": {
53
- "latest": "0.3.8"
53
+ "latest": "0.4.0"
54
54
  },
55
55
  "versions": [
56
56
  {
57
- "version": "0.3.8",
57
+ "version": "0.4.0",
58
58
  "publishTimestamp": 1793771000,
59
59
  "source": {
60
60
  "type": "npm"
@@ -50,11 +50,11 @@
50
50
  ]
51
51
  },
52
52
  "dist-tags": {
53
- "latest": "0.3.8"
53
+ "latest": "0.4.0"
54
54
  },
55
55
  "versions": [
56
56
  {
57
- "version": "0.3.8",
57
+ "version": "0.4.0",
58
58
  "publishTimestamp": 1793771000,
59
59
  "source": {
60
60
  "type": "npm"
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 = 7;
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 = 7')
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(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'queued', 0, ?)`)
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,6 +555,57 @@ 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
+
574
+ /** Read-only cross-line feed for the manager; no state changes. */
575
+ observe({ agentId, line = null, since = 0, limit = 100 } = {}) {
576
+ const n = Math.min(200, Math.max(1, Number(limit) || 100))
577
+ const from = Math.max(0, Number(since) || 0)
578
+ const params = [agentId, agentId]
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,
580
+ hop, no_reply, created_at, delivered_at, acked_at
581
+ FROM messages WHERE (from_agent = ? OR to_agent = ?) AND from_line != to_line AND created_at > ?`
582
+ params.push(from)
583
+ if (line !== null && String(line).trim() !== '') {
584
+ sql += ' AND (from_line = ? OR to_line = ?)'
585
+ params.push(String(line), String(line))
586
+ }
587
+ sql += ' ORDER BY created_at DESC LIMIT ?'
588
+ params.push(n)
589
+ const rows = this.#stmt(sql).all(...params)
590
+ return {
591
+ agentId, line: line === null ? null : String(line), since: from, count: rows.length,
592
+ messages: rows.map(row => ({
593
+ id: row.id,
594
+ from: row.from_agent + '#' + row.from_line,
595
+ to: row.to_agent + '#' + row.to_line,
596
+ state: row.state,
597
+ kind: row.kind,
598
+ ccManager: Number(row.cc_manager) === 1,
599
+ approvalState: row.approval_state,
600
+ noReply: Number(row.no_reply) === 1,
601
+ replyTo: row.reply_to,
602
+ hop: row.hop,
603
+ createdAt: row.created_at,
604
+ summary: String(row.body || '').replace(/\s+/g, ' ').slice(0, 240),
605
+ })),
606
+ }
607
+ }
608
+
545
609
  /** Structured handoff snapshot: counts + recent messages + lifecycle events. */
546
610
  handoff({ agentId, line, limit = 20, now = Date.now() }) {
547
611
  const row = this.getLine(agentId, line, now)
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,50 @@ 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
+
434
+ disposers.push(agent.ctx.tools.register({
435
+ name: 'line_observe',
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.',
437
+ parameters: {
438
+ type: 'object', additionalProperties: false,
439
+ properties: {
440
+ line: { type: 'string', description: 'Optional line filter (matches from or to)' },
441
+ since: { type: 'number', description: 'Only messages created after this epoch ms' },
442
+ limit: { type: 'number', description: 'Maximum messages, 1-200 (default 100)' },
443
+ },
444
+ },
445
+ output: OUTPUT,
446
+ execute: async (args, exec) => {
447
+ const caller = currentLine(exec, lineDefs)
448
+ if (caller.label !== config.createFrom) throw new LineError('LINE_CREATE_FORBIDDEN', `Only ${config.createFrom} may observe lines`, { caller: caller.label })
449
+ return store.observe({
450
+ agentId: config.agentId,
451
+ line: args.line === undefined ? null : String(args.line),
452
+ since: Number(args.since) || 0,
453
+ limit: limitValue(args.limit, 100, 200),
454
+ })
455
+ },
456
+ }))
457
+
401
458
  disposers.push(agent.ctx.tools.register({
402
459
  name: 'line_handoff',
403
460
  description: 'Generate a structured handoff snapshot for a line: current metadata, backlog counts, recent messages, lifecycle events. Does not call a model; write the returned markdown to a file before retire/replace/rebind.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agent-lines",
3
- "version": "0.3.8",
3
+ "version": "0.4.0",
4
4
  "description": "DSH Agent 的持久、可寻址消息线(社区插件)。",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",