@lemoncat7/dsh-partner 1.8.3 → 1.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -1
- package/docs/architecture.md +101 -1
- package/lib/agent-runtime.d.ts +11 -2
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +91 -66
- package/lib/agent-runtime.js.map +1 -1
- package/lib/api/features/workspace-api.d.ts +2 -0
- package/lib/api/features/workspace-api.d.ts.map +1 -1
- package/lib/api/features/workspace-api.js +49 -2
- package/lib/api/features/workspace-api.js.map +1 -1
- package/lib/api.d.ts +2 -0
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +13 -31
- package/lib/api.js.map +1 -1
- package/lib/channels/delivery-policy.d.ts +14 -0
- package/lib/channels/delivery-policy.d.ts.map +1 -0
- package/lib/channels/delivery-policy.js +82 -0
- package/lib/channels/delivery-policy.js.map +1 -0
- package/lib/channels/manager.d.ts +3 -2
- package/lib/channels/manager.d.ts.map +1 -1
- package/lib/channels/manager.js +40 -17
- package/lib/channels/manager.js.map +1 -1
- package/lib/channels/outbound-media.d.ts +6 -0
- package/lib/channels/outbound-media.d.ts.map +1 -0
- package/lib/channels/outbound-media.js +118 -0
- package/lib/channels/outbound-media.js.map +1 -0
- package/lib/client-api.d.ts +4 -0
- package/lib/client-api.d.ts.map +1 -1
- package/lib/client-api.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +1572 -1109
- package/lib/client.js.map +4 -4
- package/lib/collaboration/composition.d.ts +3 -1
- package/lib/collaboration/composition.d.ts.map +1 -1
- package/lib/collaboration/composition.js +47 -14
- package/lib/collaboration/composition.js.map +1 -1
- package/lib/collaboration/service.d.ts +7 -0
- package/lib/collaboration/service.d.ts.map +1 -1
- package/lib/collaboration/service.js +154 -44
- package/lib/collaboration/service.js.map +1 -1
- package/lib/collaboration/task-dispatch.d.ts +18 -0
- package/lib/collaboration/task-dispatch.d.ts.map +1 -0
- package/lib/collaboration/task-dispatch.js +44 -0
- package/lib/collaboration/task-dispatch.js.map +1 -0
- package/lib/collaboration/task-recovery.d.ts +8 -0
- package/lib/collaboration/task-recovery.d.ts.map +1 -0
- package/lib/collaboration/task-recovery.js +30 -0
- package/lib/collaboration/task-recovery.js.map +1 -0
- package/lib/companions/directory-cleanup.d.ts +4 -0
- package/lib/companions/directory-cleanup.d.ts.map +1 -0
- package/lib/companions/directory-cleanup.js +77 -0
- package/lib/companions/directory-cleanup.js.map +1 -0
- package/lib/companions/removal.d.ts +20 -0
- package/lib/companions/removal.d.ts.map +1 -0
- package/lib/companions/removal.js +84 -0
- package/lib/companions/removal.js.map +1 -0
- package/lib/companions/service.d.ts +3 -0
- package/lib/companions/service.d.ts.map +1 -1
- package/lib/companions/service.js +4 -0
- package/lib/companions/service.js.map +1 -1
- package/lib/companions/session-configuration.js +1 -1
- package/lib/companions/session-configuration.js.map +1 -1
- package/lib/companions/workspace-cleanup.d.ts +4 -0
- package/lib/companions/workspace-cleanup.d.ts.map +1 -0
- package/lib/companions/workspace-cleanup.js +37 -0
- package/lib/companions/workspace-cleanup.js.map +1 -0
- package/lib/daily-review.d.ts +1 -0
- package/lib/daily-review.d.ts.map +1 -1
- package/lib/daily-review.js +3 -0
- package/lib/daily-review.js.map +1 -1
- package/lib/domain.d.ts +2 -0
- package/lib/domain.d.ts.map +1 -1
- package/lib/domain.js.map +1 -1
- package/lib/execution/agent-support.d.ts.map +1 -1
- package/lib/execution/agent-support.js +27 -3
- package/lib/execution/agent-support.js.map +1 -1
- package/lib/execution/service.d.ts +1 -0
- package/lib/execution/service.d.ts.map +1 -1
- package/lib/execution/service.js +10 -0
- package/lib/execution/service.js.map +1 -1
- package/lib/heartbeat.d.ts +1 -0
- package/lib/heartbeat.d.ts.map +1 -1
- package/lib/heartbeat.js +3 -0
- package/lib/heartbeat.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +22 -11
- package/lib/index.js.map +1 -1
- package/lib/pendant/renderer.d.ts +1 -0
- package/lib/pendant/renderer.d.ts.map +1 -1
- package/lib/pendant/renderer.js +4 -0
- package/lib/pendant/renderer.js.map +1 -1
- package/lib/pendant/widget.js +3 -1
- package/lib/pendant/widget.js.map +1 -1
- package/lib/pendant-renderer.js +6 -6
- package/lib/requirements/archive.d.ts +4 -0
- package/lib/requirements/archive.d.ts.map +1 -0
- package/lib/requirements/archive.js +14 -0
- package/lib/requirements/archive.js.map +1 -0
- package/lib/requirements/consolidation.d.ts +11 -0
- package/lib/requirements/consolidation.d.ts.map +1 -0
- package/lib/requirements/consolidation.js +50 -0
- package/lib/requirements/consolidation.js.map +1 -0
- package/lib/requirements/domain.d.ts +41 -0
- package/lib/requirements/domain.d.ts.map +1 -0
- package/lib/requirements/domain.js +2 -0
- package/lib/requirements/domain.js.map +1 -0
- package/lib/requirements/progress.d.ts +7 -0
- package/lib/requirements/progress.d.ts.map +1 -0
- package/lib/requirements/progress.js +17 -0
- package/lib/requirements/progress.js.map +1 -0
- package/lib/requirements/service.d.ts +23 -0
- package/lib/requirements/service.d.ts.map +1 -0
- package/lib/requirements/service.js +163 -0
- package/lib/requirements/service.js.map +1 -0
- package/lib/requirements/tool.d.ts +5 -0
- package/lib/requirements/tool.d.ts.map +1 -0
- package/lib/requirements/tool.js +49 -0
- package/lib/requirements/tool.js.map +1 -0
- package/lib/requirements/worker.d.ts +30 -0
- package/lib/requirements/worker.d.ts.map +1 -0
- package/lib/requirements/worker.js +164 -0
- package/lib/requirements/worker.js.map +1 -0
- package/lib/skills/builtin.d.ts.map +1 -1
- package/lib/skills/builtin.js +3 -2
- package/lib/skills/builtin.js.map +1 -1
- package/lib/skills/service.js +1 -1
- package/lib/skills/service.js.map +1 -1
- package/lib/skills/task-planning.d.ts +5 -0
- package/lib/skills/task-planning.d.ts.map +1 -0
- package/lib/skills/task-planning.js +47 -0
- package/lib/skills/task-planning.js.map +1 -0
- package/lib/store.d.ts +3 -0
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +10 -0
- package/lib/store.js.map +1 -1
- package/lib/tasks/context.d.ts +8 -0
- package/lib/tasks/context.d.ts.map +1 -0
- package/lib/tasks/context.js +48 -0
- package/lib/tasks/context.js.map +1 -0
- package/lib/tasks/domain.d.ts +12 -0
- package/lib/tasks/domain.d.ts.map +1 -1
- package/lib/tasks/progress-notifier.d.ts +4 -0
- package/lib/tasks/progress-notifier.d.ts.map +1 -0
- package/lib/tasks/progress-notifier.js +20 -0
- package/lib/tasks/progress-notifier.js.map +1 -0
- package/lib/tasks/removal.d.ts +5 -0
- package/lib/tasks/removal.d.ts.map +1 -0
- package/lib/tasks/removal.js +44 -0
- package/lib/tasks/removal.js.map +1 -0
- package/lib/tasks/requirement-link.d.ts +4 -0
- package/lib/tasks/requirement-link.d.ts.map +1 -0
- package/lib/tasks/requirement-link.js +11 -0
- package/lib/tasks/requirement-link.js.map +1 -0
- package/lib/tasks/service.d.ts +13 -5
- package/lib/tasks/service.d.ts.map +1 -1
- package/lib/tasks/service.js +113 -29
- package/lib/tasks/service.js.map +1 -1
- package/lib/ui/board-refresh.d.ts +3 -0
- package/lib/ui/board-refresh.d.ts.map +1 -0
- package/lib/ui/board-refresh.js +42 -0
- package/lib/ui/board-refresh.js.map +1 -0
- package/lib/ui/identity-editor.d.ts +11 -0
- package/lib/ui/identity-editor.d.ts.map +1 -0
- package/lib/ui/identity-editor.js +78 -0
- package/lib/ui/identity-editor.js.map +1 -0
- package/lib/ui/requirement-board.d.ts +5 -0
- package/lib/ui/requirement-board.d.ts.map +1 -0
- package/lib/ui/requirement-board.js +47 -0
- package/lib/ui/requirement-board.js.map +1 -0
- package/lib/ui/requirement-dialog.d.ts +15 -0
- package/lib/ui/requirement-dialog.d.ts.map +1 -0
- package/lib/ui/requirement-dialog.js +53 -0
- package/lib/ui/requirement-dialog.js.map +1 -0
- package/lib/ui/requirement-tasks-panel.d.ts +8 -0
- package/lib/ui/requirement-tasks-panel.d.ts.map +1 -0
- package/lib/ui/requirement-tasks-panel.js +269 -0
- package/lib/ui/requirement-tasks-panel.js.map +1 -0
- package/lib/ui/task-board-panel.d.ts +2 -4
- package/lib/ui/task-board-panel.d.ts.map +1 -1
- package/lib/ui/task-board-panel.js +2 -283
- package/lib/ui/task-board-panel.js.map +1 -1
- package/lib/ui/task-board-stage.d.ts +12 -0
- package/lib/ui/task-board-stage.d.ts.map +1 -0
- package/lib/ui/task-board-stage.js +13 -0
- package/lib/ui/task-board-stage.js.map +1 -0
- package/lib/ui/workspace-components.js +1 -1
- package/lib/ui/workspace-components.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,42 @@
|
|
|
6
6
|
|
|
7
7
|
## 兼容性
|
|
8
8
|
|
|
9
|
-
正式版 `1.
|
|
9
|
+
正式版 `1.10.0` 适配 DeepSeek Harness `0.1.2-rc.1`,需要 Node.js `22.19+` 或 `24+`。DSH 官方 Agent、Session、Settings、Tools 与客户端 UI 依赖均锁定到 `0.1.2-rc.1` 接口族。
|
|
10
|
+
|
|
11
|
+
### 1.10.0 更新:需求看板与统一交付
|
|
12
|
+
|
|
13
|
+
- 看板以需求组织子任务,区分进行中和已归档;完成分工后提交规划,全部子任务验收通过后由负责人汇总、归档并向需求来源渠道统一通知,不逐任务播报。
|
|
14
|
+
- 需求详情集中展示目标、进度、最终结论和交付记录;支持人工收尾、调整负责人及重试。任务阶段按需展开,空阶段不占位,复用现有字体、材质与弹窗,适配手机及平板横屏。
|
|
15
|
+
- 删除任务幂等处理,取消关联执行并忽略迟到结果;依赖者暂停,重新确认需求范围。归档结果保留交付快照,旧任务不自动猜测归属或清空。
|
|
16
|
+
- 汇总及投递失败持久化退避重试,重启继续;通知失败不会重新生成已保存的总结。人工从看板创建的需求不误发到其他渠道。
|
|
17
|
+
- 内置任务规划 Skill 升级至 `1.4.0`,明确需求归属及整体收尾;强调专业分工,区分伙伴技能与原生技能加载,不增加前置 Skill 或执行路由门禁。
|
|
18
|
+
- 包含渠道只提取最终答复、生成文件真实路径解析及挂卡数字位置优化。220 项测试和 60 个任务的六组响应式/主题交互验证通过。
|
|
19
|
+
|
|
20
|
+
操作说明及兼容边界见 [1.10.0 发布说明](docs/releases/1.10.0.md)。
|
|
21
|
+
|
|
22
|
+
### 1.9.2 修复:中断后的待验收任务恢复
|
|
23
|
+
|
|
24
|
+
- 执行伙伴提前标记待验收、但尚未提交结果时,服务重启或暂时网络故障后可继续恢复交付,不再误取消执行。
|
|
25
|
+
- 对已被旧调度器误取消的记录,校验停止记录、负责人、当前授权和是否有更新的执行后定向恢复;有结果、已验收、正常取消或已结束的任务不会因此重跑。
|
|
26
|
+
- 启动时先注册结果通知再启动恢复队列,避免快速完成的恢复任务漏发通知;恢复前仍检查依赖和并发限制。
|
|
27
|
+
- 渠道只保留创建后的分工确认与验收通过后的最终交付;核验、重试、accept 等内部回调静默,自动目标续轮也不能绕过过滤。真正受阻时仍可请求用户介入,用户主动询问进度仍正常回答。
|
|
28
|
+
- 已保存的最终结果先投递渠道,再创建内部进度回调,二者错误独立记录。搭配知识库 `2.6.0` 的持久化回写队列,会话结束只等待快照入队,不等待知识提取或写入完成;回写失败在原回答下单独重试,不重发渠道消息。
|
|
29
|
+
|
|
30
|
+
### 1.9.1 更新:伙伴删除与目录清理
|
|
31
|
+
|
|
32
|
+
- 删除确认绑定当前伙伴,切换伙伴时重置;删除中防止重复操作,成功返回总览,不再把下一位伙伴置于删除确认状态。
|
|
33
|
+
- 页面明确确认后,移除伙伴专属 `partners/<id>` 目录及文件、工作区注册和关联配置;共享目录、链接根目录与挂载点拒绝自动清理,不扫描历史遗留目录。
|
|
34
|
+
- 删除期间阻止新会话、后台任务与执行队列重新占用目录;运行中的伙伴、已绑定渠道与最后一个伙伴受到保护,清理失败保留身份供重试。
|
|
35
|
+
- DSH 当前公开接口不支持永久清理全局原始会话日志,因此这些日志仍保留;目录删除不是跨存储事务,文件删除失败时可能已有部分文件被移除。
|
|
36
|
+
- 180 项自动化测试及手机、横屏、桌面深浅色共 6 组隔离交互验证通过。
|
|
37
|
+
|
|
38
|
+
### 1.9.0 更新:主动任务拆解与执行
|
|
39
|
+
|
|
40
|
+
- 匹配已启用的拆解 Skill 和授权伙伴专长时,主动拆分真实交付物、安排负责人及依赖,不要求用户反复说“使用 Skill”或逐个 @;简单问答和明确只规划的请求不自动执行。
|
|
41
|
+
- 工具创建任务并指定负责人后默认提交执行;前置任务验收通过后,后续任务自动接续。支持 `autoRun=false` 仅规划;已有历史任务不会因为升级突然启动。
|
|
42
|
+
- 执行队列持久化、重复提交去重、启动前复核授权,最多三个任务执行并发;提交后先返回分工摘要,不阻塞等待所有结果。
|
|
43
|
+
- 看板改为阶段内横向卡片排列、按需展开与整页滚动,移动端使用紧凑筛选栏,保留现有配色。
|
|
44
|
+
- 169 项自动化测试通过;60 条任务的隔离浏览器检查覆盖手机、横屏与桌面、深浅色共 6 组场景,包括筛选、展开、详情和提交模式。
|
|
10
45
|
|
|
11
46
|
### 1.8.3 更新
|
|
12
47
|
|
package/docs/architecture.md
CHANGED
|
@@ -12,6 +12,13 @@ client -> HTTP feature routers -> application services -> repositories/store
|
|
|
12
12
|
|
|
13
13
|
## Module boundaries
|
|
14
14
|
|
|
15
|
+
`tasks/progress-notifier.ts` orders persisted final-result delivery before internal
|
|
16
|
+
agent follow-ups, with separate error boundaries. Knowledge 2.6.0 owns durable
|
|
17
|
+
writeback snapshots, plans, retries and status; Partner does not copy its queue or
|
|
18
|
+
patch the DSH loop. Channel results do not wait for extraction. Retrying writeback
|
|
19
|
+
does not rerun the partner's turn or resend its channel message. This does not
|
|
20
|
+
make channel delivery and knowledge writes one atomic transaction.
|
|
21
|
+
|
|
15
22
|
Configuration saves only commit state. The awaited `agent/pre-step` hook compares
|
|
16
23
|
the saved configuration with the installed revision before each new turn, covering
|
|
17
24
|
native history resume as well as local, channel and task-board conversations.
|
|
@@ -135,8 +142,101 @@ form and save feedback, using the shared companion draft and existing UI tokens.
|
|
|
135
142
|
receives only A's granted directory and the service checks the same edge
|
|
136
143
|
again at execution time. User-initiated board delegation is a separate actor
|
|
137
144
|
and may target any created companion without impersonating another partner.
|
|
138
|
-
-
|
|
145
|
+
- A companion may submit its own assigned stage without a cross-companion
|
|
146
|
+
grant. Other-companion execution always requires the directed grant above;
|
|
147
|
+
assigned-stage instructions forbid recreating the same work recursively.
|
|
139
148
|
- Scheduled jobs default to non-overlapping execution and a disposable session.
|
|
149
|
+
|
|
150
|
+
## Companion deletion safety
|
|
151
|
+
|
|
152
|
+
- `companions/removal.ts` serializes removals, binds each operation to one ID,
|
|
153
|
+
blocks active work/last-companion deletion, and keeps the identity retryable
|
|
154
|
+
if directory cleanup fails. An in-memory removal guard prevents new partner
|
|
155
|
+
sessions, task claims, heartbeat and daily-review work during deletion.
|
|
156
|
+
- `companions/directory-cleanup.ts` derives only `<defaultCwd>/partners/<id>`;
|
|
157
|
+
it rejects traversal, shared/nested workspace paths, symlinked roots and
|
|
158
|
+
mounted volumes. Nested symlinks are removed without following their targets.
|
|
159
|
+
Filesystem cleanup finishes before deleting identity. This is not a cross-store
|
|
160
|
+
transaction: a failed filesystem deletion can have removed some files, and a
|
|
161
|
+
later state-write failure leaves the identity available for retry.
|
|
162
|
+
- The identity editor sends `DELETE ...?removeFiles=1` only after a named
|
|
163
|
+
confirmation describing permanent deletion. Older callers without this flag
|
|
164
|
+
retain file-preserving behavior. Neither path scans historical orphan folders
|
|
165
|
+
nor purges DSH's global session persistence; the current host has no supported
|
|
166
|
+
permanent session-log deletion API. `workspace-cleanup.ts` removes only the
|
|
167
|
+
dedicated workspace registration through the public host API.
|
|
168
|
+
- `ui/identity-editor.tsx` isolates confirmation and async feedback per companion,
|
|
169
|
+
prevents duplicate clicks and returns the selected deletion to the overview.
|
|
170
|
+
`test/companion-removal.test.mjs` and `scripts/verify-companion-removal.mjs`
|
|
171
|
+
use disposable fixtures; physical deletion never runs against live user data
|
|
172
|
+
as part of these regression tests.
|
|
173
|
+
|
|
174
|
+
## Proactive planning and durable task dispatch
|
|
175
|
+
|
|
176
|
+
- `collaboration/task-recovery.ts` distinguishes an early review status from a
|
|
177
|
+
committed deliverable. Previously started task delegations may resume an empty
|
|
178
|
+
review after interruption, subject to assignment, access, dependency and
|
|
179
|
+
concurrency checks. Saved results/reviews are never rerun through this path.
|
|
180
|
+
Startup repairs only known coordinator cancellations, with shutdown evidence
|
|
181
|
+
for the status-mismatch bug and no superseding task delegation. Arbitrary user
|
|
182
|
+
cancellations are not revived. Result notification is wired before recovery
|
|
183
|
+
starts claiming work.
|
|
184
|
+
- `channels/delivery-policy.ts` keeps internal task/review continuations out of
|
|
185
|
+
channel replies, including nested goal completion notices. User-origin turns
|
|
186
|
+
retain creation acknowledgments and explicit progress answers. Accepted task
|
|
187
|
+
results use the separate deterministic delivery path and persistent receipts;
|
|
188
|
+
intermediate execution/review notices are not additional channel messages.
|
|
189
|
+
- `skills/task-planning.ts` owns the built-in planning instructions (1.2.0).
|
|
190
|
+
Matching multi-deliverable requests proactively use enabled Skills and the
|
|
191
|
+
authorized directory's stable IDs, roles, descriptions and Skill names.
|
|
192
|
+
Inline Skills are already injected and do not require a redundant load call.
|
|
193
|
+
Simple answers and explicit discussion/planning-only requests do not launch
|
|
194
|
+
work. This is a model instruction contract, not a guarantee of model choices.
|
|
195
|
+
- `collaboration/composition.ts` maps assigned tool-created tasks to explicit
|
|
196
|
+
`autoRun` intent by default; `autoRun=false` or explicit backlog preserves
|
|
197
|
+
planning-only behavior. The board creation form exposes the same choice.
|
|
198
|
+
The storage service itself defaults to planning-only, so legacy records and
|
|
199
|
+
other callers do not silently acquire execution permission after an upgrade.
|
|
200
|
+
- `tasks/service.ts` persists intent and validates task state/dependencies.
|
|
201
|
+
`collaboration/task-dispatch.ts` owns pure eligibility, authorization and
|
|
202
|
+
bounded-queue helpers. `collaboration/service.ts` uses its existing recovery
|
|
203
|
+
timer to enroll intent and claim work atomically, with at most three running
|
|
204
|
+
delegations. No additional polling loop or external queue is introduced.
|
|
205
|
+
- Assignment plus explicit delegation commits atomically. Repeat submissions
|
|
206
|
+
of the same task to the same pending executor return that job, not another
|
|
207
|
+
execution. Missing or unaccepted dependencies prevent execution; all
|
|
208
|
+
prerequisites must be `done` after acceptance before downstream work starts.
|
|
209
|
+
Accepted prerequisite summaries enter the executor prompt with bounded size.
|
|
210
|
+
- The scheduler rechecks directed grants, executor identity and current task
|
|
211
|
+
state before claiming and again checks authorization before execution.
|
|
212
|
+
Queued/running jobs are never evicted to retain terminal history. Switching
|
|
213
|
+
to planning-only cancels waiting task jobs, not already-running work or review.
|
|
214
|
+
Restart recovery retains submission intent; old tasks without `autoRun=true`
|
|
215
|
+
are not enrolled automatically. Submission returns without awaiting results.
|
|
216
|
+
- `ui/task-board-stage.tsx` owns stage collapse and progressive disclosure:
|
|
217
|
+
six cards initially per stage (twelve when focused), then twelve per expansion.
|
|
218
|
+
Responsive horizontal card grids share the page scroll instead of fixed-height
|
|
219
|
+
nested scrollboxes. Mobile uses compact stage/assignee filters. Existing
|
|
220
|
+
semantic colors and dialogs are retained without new card animations.
|
|
221
|
+
- Regression coverage: `test/task-dispatch.test.mjs` exercises real storage,
|
|
222
|
+
composition tools, dependency chains, restart intent, idempotency, concurrency
|
|
223
|
+
and permission changes. `scripts/verify-task-board.mjs` tests the actual panel
|
|
224
|
+
with isolated fixtures across phone, landscape and desktop in both themes.
|
|
225
|
+
|
|
226
|
+
## 需求看板与整体收尾
|
|
227
|
+
|
|
228
|
+
- `requirements/domain.ts` 与 `service.ts` 定义需求归属及规划、提交、汇总、归档生命周期。伙伴工具创建必须显式传入 requirementId,缺失时不产生任何记录;`tasks/requirement-link.ts` 给出依赖所属需求的核对线索,但不猜测归属。仅旧内部调用/人工接口保留单任务需求兼容逻辑。规划提交后不能直接增加任务,续做先重开规划。
|
|
229
|
+
- 同一目标续做可以明确 reopen 已归档需求,`requirements/archive.ts` 保留旧总结、交付快照和投递时间(最多 20 次,达到上限明确拒绝,不静默丢弃)。reopen 的 title/description 用于扩展新阶段范围,不失效已验收任务;update 才用于改写旧验收要求。仅重开不触发旧成果再通知,新增任务后按批次继续汇报。
|
|
230
|
+
- `requirements/consolidation.ts` 是显式维护用的纯状态操作,不注册伙伴工具、不按标题在启动时迁移。只允许同负责人、同来源、子任务全部完成且来源归档已通知的两个需求合并;保留原任务 ID、依赖、成果与归档历史,不重新执行或重复投递,版本变化立即拒绝。
|
|
231
|
+
- `requirements/worker.ts` 单并发汇总已提交且全部子任务验收通过的需求。总结及交付快照先持久化,再向原始创建渠道投递;失败指数退避,重启继续,已保存的总结不会因通知失败重新生成。投递复用持久化 receipt,已确认发送不重复发送;网络发送与本地 receipt 写入不是跨系统事务,不承诺绝对 exactly-once。
|
|
232
|
+
- `requirements/progress.ts` 独立判定阶段通知:非空需求只剩 done/blocked 且没有 queued/running 委派才可汇报;backlog/ready/doing/review 任一存在都静默。稳定 15 秒且负责人空闲后汇总一次;阶段内容与指纹先持久化,失败重试复用内容,追加任务会中止过时汇总。阶段通知不需要归档,明确确认整个需求完成后可以 finish 规划态需求;未完成或受阻任务禁止被伪装为完成归档。
|
|
233
|
+
- `tasks/context.ts` 为执行、核验和创建者回调提供同一份最新需求、任务补充、打回理由及上次交付。修改实际需求会原子失效旧执行,工作版本阻止迟到结果覆盖;核验 accept/reject 使用实际读取的任务版本,防止旧意见打回新版。普通讨论保留当前执行,但会使旧验收决定失效。已验收事件不再携带历史返工指令。
|
|
234
|
+
- 子任务完成、验收、受阻只反馈给内部创建伙伴;渠道不逐任务播报。人工从看板创建的需求不会自动投递到负责人的其他聊天。汇总输出沿用长结果文档及真实附件投递能力。
|
|
235
|
+
- `tasks/removal.ts` 原子移除任务、活动、委派,取消关联执行;未完成的依赖者暂停,并重新打开需求范围确认。重复删除成功,执行器迟到结果不能重建记录。归档保留不可变交付快照,删除子任务不会抹去最终结论。
|
|
236
|
+
- `ui/requirement-board.tsx` 负责需求总览与归档入口;`requirement-dialog.tsx` 负责需求表单和详情;`requirement-tasks-panel.tsx` 负责需求内的任务阶段和任务详情,`task-board-panel.tsx` 仅保留稳定入口。统一复用工作区表单、字体、色彩与弹窗,次要信息通过折叠区展示,无新增模糊层或卡片动画。
|
|
237
|
+
- `ui/board-refresh.ts` 合并刷新并用版本号丢弃旧响应,页面隐藏暂停轮询;进入需求后停止后台总览轮询。需求列表分批展示,任务阶段只展示非空阶段,避免堆叠空列。
|
|
238
|
+
- `test/requirements.test.mjs` 覆盖范围确认、归档、权限、并发删除、执行取消、持久化重试;`test/channel-media.test.mjs` 验证子任务静默与需求最终通知。
|
|
239
|
+
|
|
140
240
|
## 主界面挂饰与消息
|
|
141
241
|
|
|
142
242
|
- `pendant/frame-loop.ts` 独立负责可选 24/30/60 FPS 绘制调度(默认 30,旧配置自动兼容):定时器临近截止时间再进入 RAF,保留非整除刷新率的小数节奏,高频输入合并唤醒;运行时切换只重排唯一待执行回调,不重建 WebGL/物理场景,休眠中切换不启动循环。隐藏/卸载取消计时器和 RAF。物理仍以 120 Hz 固定小步推进,停顿后不追赶后台时间。握持状态不再强制常驻循环,绳子平稳后可休眠,保持弹簧位置和储能,移动/松手重新唤醒。
|
package/lib/agent-runtime.d.ts
CHANGED
|
@@ -14,8 +14,9 @@ import type { MemoryReflectionService } from './memory-reflection.js';
|
|
|
14
14
|
import { type ConcernObservation, type ConcernObservationCandidate, type PartnerConcern } from './concern-domain.js';
|
|
15
15
|
import type { PartnerConcernStore } from './concern-store.js';
|
|
16
16
|
import type { BoardTask } from './tasks/domain.js';
|
|
17
|
-
import type { PartnerInboundMessage,
|
|
17
|
+
import type { PartnerInboundMessage, PartnerReply } from './channel-message.js';
|
|
18
18
|
import { type ConcernSource } from './concern-sources.js';
|
|
19
|
+
export { extractOutboundAttachments } from './channels/outbound-media.js';
|
|
19
20
|
export { renderToolProtocol, resolvePartnerAgentOptions as resolveAgentOptions } from './execution/agent-support.js';
|
|
20
21
|
type RuntimeContext = Context & {
|
|
21
22
|
agents: Context['agents'];
|
|
@@ -70,6 +71,8 @@ export declare class PartnerAgentRuntime {
|
|
|
70
71
|
private readonly disposeConfigurationListener;
|
|
71
72
|
private readonly configurations;
|
|
72
73
|
private readonly preparedTurns;
|
|
74
|
+
private readonly observationJobs;
|
|
75
|
+
private readonly agentSetupJobs;
|
|
73
76
|
private closed;
|
|
74
77
|
private questionAnswerer;
|
|
75
78
|
constructor(ctx: RuntimeContext, store: PartnerStore, defaultCwd: string, memory?: PartnerMemoryStore | undefined, reflection?: MemoryReflectionService | undefined, concerns?: PartnerConcernStore | undefined, composer?: PartnerAgentComposer | undefined);
|
|
@@ -86,6 +89,7 @@ export declare class PartnerAgentRuntime {
|
|
|
86
89
|
companion: Companion;
|
|
87
90
|
prompt: string;
|
|
88
91
|
parentSessionId?: string;
|
|
92
|
+
signal?: AbortSignal;
|
|
89
93
|
}): Promise<{
|
|
90
94
|
run: {
|
|
91
95
|
id: string;
|
|
@@ -99,11 +103,16 @@ export declare class PartnerAgentRuntime {
|
|
|
99
103
|
concernSources(companion: Companion, query?: string): Promise<ConcernSource[]>;
|
|
100
104
|
private runHeartbeatWhenIdle;
|
|
101
105
|
observeSessionEvent(session: Session, event: SessionEvent): Promise<void>;
|
|
106
|
+
private processSessionEvent;
|
|
102
107
|
recordConcernCreatedNotice(companion: Companion, scopeId: string, concerns: PartnerConcern[]): Promise<boolean>;
|
|
103
108
|
private driveScopedHeartbeat;
|
|
104
109
|
private driveEphemeralHeartbeat;
|
|
105
110
|
recordHeartbeatActivity(companion: Companion, route: ChannelSession, execution: HeartbeatExecution, outcome: HeartbeatOutcome, deliveryError?: string): Promise<void>;
|
|
106
111
|
resetCompanion(companionId: string): Promise<void>;
|
|
112
|
+
removeCompanionWorkspace(companionId: string): Promise<void>;
|
|
113
|
+
private sharedDirectoryPaths;
|
|
114
|
+
validateCompanionDirectory(companionId: string): Promise<void>;
|
|
115
|
+
removeCompanionDirectory(companionId: string): Promise<void>;
|
|
107
116
|
reloadCompanion(companionId: string): Promise<void>;
|
|
108
117
|
/** Awaited by the host before prompt/tool assembly, never from a running tool. */
|
|
109
118
|
prepareAgentTurn(agent: Agent, turn: number, signal: AbortSignal): Promise<void>;
|
|
@@ -116,12 +125,12 @@ export declare class PartnerAgentRuntime {
|
|
|
116
125
|
private persistInbound;
|
|
117
126
|
private ensureSession;
|
|
118
127
|
private ensureAgent;
|
|
128
|
+
private ensureAgentReady;
|
|
119
129
|
private composeRestoredAgent;
|
|
120
130
|
private injectProfileUpdate;
|
|
121
131
|
private attachSession;
|
|
122
132
|
}
|
|
123
133
|
export declare function selectTaskNotificationRoute(routes: readonly ChannelSession[], creatorSessionId: string | undefined, archived: (route: ChannelSession) => boolean): ChannelSession | undefined;
|
|
124
|
-
export declare function extractOutboundAttachments(text: string, cwd: string): Promise<PartnerOutboundAttachment[]>;
|
|
125
134
|
export declare function completedTurnEvents(events: readonly SessionEvent[], end: Extract<SessionEvent, {
|
|
126
135
|
type: 'turn/end';
|
|
127
136
|
}>): readonly SessionEvent[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-runtime.d.ts","sourceRoot":"","sources":["../src/agent-runtime.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAA;AACnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAIlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACrE,OAAO,KAAK,EAAa,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"agent-runtime.d.ts","sourceRoot":"","sources":["../src/agent-runtime.ts"],"names":[],"mappings":"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"}
|
package/lib/agent-runtime.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { mkdir, realpath, stat, writeFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
4
4
|
import { BlockAssembler, createToolResultMessage, createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
5
5
|
import { createScope, scopeOf, scopeParentOf } from '@deepseek-ai/dsh-scope';
|
|
6
6
|
import { SessionConfigurationIndex } from './companions/session-configuration.js';
|
|
7
|
+
import { removeOwnedWorkspace } from './companions/workspace-cleanup.js';
|
|
8
|
+
import { assertOwnedDirectory, removeOwnedDirectory } from './companions/directory-cleanup.js';
|
|
7
9
|
import { concernObservationPrompt } from './autonomy.js';
|
|
8
10
|
import { boundedConcernCheckMinutes } from './concern-domain.js';
|
|
11
|
+
import { taskWorkContext } from './tasks/context.js';
|
|
9
12
|
import { PARTNER_MEDIA_MAX_BYTES, safeMediaName } from './channel-message.js';
|
|
10
13
|
import { listConcernFileSources } from './concern-sources.js';
|
|
11
14
|
import { CONCERN_CREATED_NOTICE, renderConcernCreatedNotice } from './concern-notification.js';
|
|
12
15
|
import { HEARTBEAT_LOCAL_COMMAND, heartbeatLocalCommandTool } from './heartbeat-command.js';
|
|
13
16
|
import { assistantTextAfter, renderPartnerPersona, renderToolProtocol, resolvePartnerAgentOptions as resolveAgentOptions } from './execution/agent-support.js';
|
|
17
|
+
import { channelReplyPartsAfter } from './channels/delivery-policy.js';
|
|
18
|
+
import { prepareChannelReply } from './channels/outbound-media.js';
|
|
19
|
+
export { extractOutboundAttachments } from './channels/outbound-media.js';
|
|
14
20
|
export { renderToolProtocol, resolvePartnerAgentOptions as resolveAgentOptions } from './execution/agent-support.js';
|
|
15
21
|
const HEARTBEAT_TIMEOUT_MS = 115_000;
|
|
16
22
|
const HEARTBEAT_DISCOVERY_BUDGET_MS = 72_000;
|
|
@@ -58,6 +64,8 @@ export class PartnerAgentRuntime {
|
|
|
58
64
|
disposeConfigurationListener;
|
|
59
65
|
configurations;
|
|
60
66
|
preparedTurns = new WeakMap();
|
|
67
|
+
observationJobs = new Map();
|
|
68
|
+
agentSetupJobs = new Map();
|
|
61
69
|
closed = false;
|
|
62
70
|
questionAnswerer;
|
|
63
71
|
constructor(ctx, store, defaultCwd, memory, reflection, concerns, composer) {
|
|
@@ -157,6 +165,8 @@ export class PartnerAgentRuntime {
|
|
|
157
165
|
return route;
|
|
158
166
|
}
|
|
159
167
|
async ensureLocalSessionRecord(companionId) {
|
|
168
|
+
if (this.store.isCompanionRemoving(companionId))
|
|
169
|
+
throw new Error('伙伴正在删除,不能创建会话');
|
|
160
170
|
const companion = this.store.snapshot().companions.find(item => item.id === companionId);
|
|
161
171
|
if (companion === undefined)
|
|
162
172
|
throw new Error('伙伴身份不存在');
|
|
@@ -176,6 +186,8 @@ export class PartnerAgentRuntime {
|
|
|
176
186
|
lastMessageAt: now,
|
|
177
187
|
};
|
|
178
188
|
await this.store.update(state => {
|
|
189
|
+
if (this.store.isCompanionRemoving(companion.id) || !state.companions.some(item => item.id === companion.id))
|
|
190
|
+
throw new Error('伙伴正在删除或已不存在');
|
|
179
191
|
state.sessions = state.sessions.filter(item => !(item.companionId === companion.id && item.kind === 'local'));
|
|
180
192
|
state.sessions.push(route);
|
|
181
193
|
});
|
|
@@ -201,18 +213,22 @@ export class PartnerAgentRuntime {
|
|
|
201
213
|
const route = selectTaskNotificationRoute(routes, task.creatorSessionId, item => this.isArchived(item))
|
|
202
214
|
?? await this.createLocalSession(companion.id);
|
|
203
215
|
const agent = await this.ensureAgent(companion, route);
|
|
216
|
+
const latestTask = this.store.snapshot().tasks.find(item => item.id === task.id);
|
|
217
|
+
if (!latestTask || latestTask.revision !== task.revision)
|
|
218
|
+
return;
|
|
204
219
|
const isReviewer = task.status === 'review' && task.reviewerCompanionId === companion.id;
|
|
205
220
|
const instruction = isReviewer
|
|
206
|
-
? '
|
|
221
|
+
? '这是伙伴内部验收,不是用户的新消息。执行伙伴已提交结果,现在默认由你验收。请根据任务要求真实核验已有产出;通过后必须调用 partner_task_board accept,不通过则调用 reject 并写明原因。不要重新执行该任务,不要向用户播报核验过程或 accept 操作;子任务不发送渠道通知,需求整体验收和汇总后由系统统一投递。'
|
|
207
222
|
: task.status === 'review'
|
|
208
|
-
? '
|
|
223
|
+
? '这是伙伴内部进度事件。执行结果已提交并等待指定伙伴验收;不要重新执行,也不要向用户播报中间进度。只在看板中推进必要的协作,子任务不发送渠道通知,需求整体验收和汇总后由系统统一投递。'
|
|
209
224
|
: task.status === 'done'
|
|
210
|
-
? '
|
|
225
|
+
? '这是你创建并分配的看板任务终态事件。不要重新执行已完成的工作。请检查依赖它的后续任务是否解锁,需要时继续在看板上分配。不要逐项向渠道通知;全部子任务验收完成后,系统会单独请求需求负责人汇总整个需求。'
|
|
211
226
|
: '这是你创建并分配的看板任务受阻事件。请保留已有产出、检查是否能调整分工或解锁依赖;需要用户介入时,明确说明阻塞原因与所需动作。';
|
|
212
227
|
agent.followup(createUserMessage({
|
|
213
228
|
content: [{ type: 'text', text: [
|
|
214
229
|
instruction,
|
|
215
230
|
`任务:${task.title}`,
|
|
231
|
+
taskWorkContext(this.store.snapshot(), task),
|
|
216
232
|
`状态:${previousStatus} → ${task.status}`,
|
|
217
233
|
task.resultSummary ? `结果:\n${task.resultSummary.slice(0, 3000)}` : '',
|
|
218
234
|
task.status === 'review' && task.reviewHandoff ? `验收交接:\n${task.reviewHandoff.slice(0, 2000)}` : '',
|
|
@@ -223,18 +239,21 @@ export class PartnerAgentRuntime {
|
|
|
223
239
|
}));
|
|
224
240
|
}
|
|
225
241
|
async executeTask(input) {
|
|
242
|
+
input.signal?.throwIfAborted();
|
|
226
243
|
const route = await this.createLocalSession(input.companion.id);
|
|
227
244
|
const key = route.sessionId;
|
|
228
245
|
const previous = this.taskQueues.get(key) ?? Promise.resolve();
|
|
229
246
|
let output = '';
|
|
230
247
|
const current = previous.catch(() => { }).then(async () => {
|
|
248
|
+
input.signal?.throwIfAborted();
|
|
231
249
|
const agent = await this.ensureAgent(input.companion, route);
|
|
232
250
|
if (agent.status !== 'idle')
|
|
233
251
|
await agent.whenIdle();
|
|
252
|
+
input.signal?.throwIfAborted();
|
|
234
253
|
const startSeq = agent.session.seq;
|
|
235
254
|
agent.followup(createUserMessage({
|
|
236
255
|
content: [{ type: 'text', text: input.prompt }],
|
|
237
|
-
source: { kind: 'plugin', plugin: '@lemoncat7/dsh-partner', form: 'notice', summary: input.sourceId.startsWith('review:') ? '伙伴核验看板任务' : '伙伴执行看板任务' },
|
|
256
|
+
source: { kind: 'plugin', plugin: '@lemoncat7/dsh-partner', form: 'notice', summary: input.sourceId.startsWith('requirement:') ? '伙伴汇总需求' : input.sourceId.startsWith('review:') ? '伙伴核验看板任务' : '伙伴执行看板任务' },
|
|
238
257
|
}));
|
|
239
258
|
let timedOut = false;
|
|
240
259
|
const timer = setTimeout(() => {
|
|
@@ -242,11 +261,17 @@ export class PartnerAgentRuntime {
|
|
|
242
261
|
agent.cancel({ kind: 'hook', reason: 'partner task execution timed out' });
|
|
243
262
|
}, PARTNER_TASK_TIMEOUT_MS);
|
|
244
263
|
timer.unref?.();
|
|
264
|
+
const cancel = () => { agent.cancel({ kind: 'hook', reason: 'partner task removed' }); };
|
|
265
|
+
input.signal?.addEventListener('abort', cancel, { once: true });
|
|
266
|
+
if (input.signal?.aborted)
|
|
267
|
+
cancel();
|
|
245
268
|
try {
|
|
246
269
|
await agent.whenIdle();
|
|
270
|
+
input.signal?.throwIfAborted();
|
|
247
271
|
}
|
|
248
272
|
finally {
|
|
249
273
|
clearTimeout(timer);
|
|
274
|
+
input.signal?.removeEventListener('abort', cancel);
|
|
250
275
|
}
|
|
251
276
|
if (timedOut)
|
|
252
277
|
throw new Error('伙伴看板任务执行超时');
|
|
@@ -376,6 +401,25 @@ export class PartnerAgentRuntime {
|
|
|
376
401
|
}
|
|
377
402
|
}
|
|
378
403
|
async observeSessionEvent(session, event) {
|
|
404
|
+
if (event.type !== 'turn/end' || event.data.reason.kind !== 'completed')
|
|
405
|
+
return;
|
|
406
|
+
const route = this.store.snapshot().sessions.find(item => item.sessionId === session.id);
|
|
407
|
+
if (!route || this.store.isCompanionRemoving(route.companionId))
|
|
408
|
+
return;
|
|
409
|
+
const id = route.companionId;
|
|
410
|
+
this.observationJobs.set(id, (this.observationJobs.get(id) ?? 0) + 1);
|
|
411
|
+
try {
|
|
412
|
+
await this.processSessionEvent(session, event);
|
|
413
|
+
}
|
|
414
|
+
finally {
|
|
415
|
+
const left = (this.observationJobs.get(id) ?? 1) - 1;
|
|
416
|
+
if (left)
|
|
417
|
+
this.observationJobs.set(id, left);
|
|
418
|
+
else
|
|
419
|
+
this.observationJobs.delete(id);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async processSessionEvent(session, event) {
|
|
379
423
|
if (event.type !== 'turn/end' || event.data.reason.kind !== 'completed')
|
|
380
424
|
return;
|
|
381
425
|
const route = this.store.snapshot().sessions.find(item => item.sessionId === session.id);
|
|
@@ -488,6 +532,27 @@ export class PartnerAgentRuntime {
|
|
|
488
532
|
await this.releaseCompanion(companionId);
|
|
489
533
|
await this.store.update(state => { state.sessions = state.sessions.filter(item => item.companionId !== companionId); });
|
|
490
534
|
}
|
|
535
|
+
async removeCompanionWorkspace(companionId) {
|
|
536
|
+
const cwd = partnerCwd(this.defaultCwd, companionId);
|
|
537
|
+
const sharedPaths = this.store.snapshot().sessions.filter(route => route.companionId !== companionId).map(route => route.cwd ?? this.defaultCwd);
|
|
538
|
+
await removeOwnedWorkspace(this.ctx.workspaceRegistry, cwd, companionId, sharedPaths);
|
|
539
|
+
this.workspaces.delete(cwd);
|
|
540
|
+
}
|
|
541
|
+
sharedDirectoryPaths(companionId) {
|
|
542
|
+
const state = this.store.snapshot();
|
|
543
|
+
const cwd = resolve(partnerCwd(this.defaultCwd, companionId));
|
|
544
|
+
return [
|
|
545
|
+
...state.companions.filter(item => item.id !== companionId).map(item => partnerCwd(this.defaultCwd, item.id)),
|
|
546
|
+
...state.sessions.filter(route => route.companionId !== companionId).map(route => route.cwd ?? this.defaultCwd),
|
|
547
|
+
...this.ctx.workspaceRegistry.list().filter(item => resolve(item.path) !== cwd).map(item => item.path),
|
|
548
|
+
];
|
|
549
|
+
}
|
|
550
|
+
async validateCompanionDirectory(companionId) {
|
|
551
|
+
await assertOwnedDirectory(this.defaultCwd, companionId, this.sharedDirectoryPaths(companionId));
|
|
552
|
+
}
|
|
553
|
+
async removeCompanionDirectory(companionId) {
|
|
554
|
+
await removeOwnedDirectory(this.defaultCwd, companionId, this.sharedDirectoryPaths(companionId));
|
|
555
|
+
}
|
|
491
556
|
async reloadCompanion(companionId) {
|
|
492
557
|
// Compatibility for callers that saved a configuration. The index already
|
|
493
558
|
// observes committed writes; do not tear down or wake any Agent here.
|
|
@@ -503,6 +568,8 @@ export class PartnerAgentRuntime {
|
|
|
503
568
|
const configuration = this.configurations.forSession(agent.session.id);
|
|
504
569
|
if (!configuration)
|
|
505
570
|
return;
|
|
571
|
+
if (this.store.isCompanionRemoving(configuration.companion.id))
|
|
572
|
+
throw new Error('伙伴正在删除,不能启动新轮次');
|
|
506
573
|
signal.throwIfAborted();
|
|
507
574
|
const installed = this.restoredCompositions.get(agent.session.id);
|
|
508
575
|
if (installed?.agent !== agent || installed.revision !== configuration.revision) {
|
|
@@ -523,7 +590,7 @@ export class PartnerAgentRuntime {
|
|
|
523
590
|
this.preparedTurns.set(agent, turn);
|
|
524
591
|
}
|
|
525
592
|
isCompanionBusy(companionId) {
|
|
526
|
-
return this.store.snapshot().sessions.some(route => route.companionId === companionId
|
|
593
|
+
return this.observationJobs.has(companionId) || this.agentSetupJobs.has(companionId) || this.store.snapshot().sessions.some(route => route.companionId === companionId
|
|
527
594
|
&& (this.handles.get(route.sessionId)?.agent ?? this.ctx.agents.get(route.sessionId))?.status === 'running');
|
|
528
595
|
}
|
|
529
596
|
async releaseCompanion(companionId) {
|
|
@@ -599,17 +666,15 @@ export class PartnerAgentRuntime {
|
|
|
599
666
|
source: { kind: 'user' },
|
|
600
667
|
}));
|
|
601
668
|
await agent.whenIdle();
|
|
602
|
-
const response =
|
|
669
|
+
const response = channelReplyPartsAfter(agent.session.snapshotEvents(), startSeq);
|
|
603
670
|
await this.store.update(state => {
|
|
604
671
|
const target = state.sessions.find(item => item.id === session.id);
|
|
605
672
|
if (target)
|
|
606
673
|
target.lastMessageAt = Date.now();
|
|
607
674
|
});
|
|
608
|
-
if (!response)
|
|
609
|
-
throw new Error('伙伴没有生成可发送的文本回复');
|
|
610
675
|
if (deferred && deferred.length > 0)
|
|
611
676
|
await this.concerns?.markMentioned(companion.id, deferred.map(item => item.id)).catch(() => { });
|
|
612
|
-
return
|
|
677
|
+
return prepareChannelReply(response, session.cwd ?? this.defaultCwd);
|
|
613
678
|
}
|
|
614
679
|
async persistInbound(session, message) {
|
|
615
680
|
const cwd = session.cwd ?? this.defaultCwd;
|
|
@@ -659,6 +724,22 @@ export class PartnerAgentRuntime {
|
|
|
659
724
|
return session;
|
|
660
725
|
}
|
|
661
726
|
async ensureAgent(companion, route) {
|
|
727
|
+
if (this.store.isCompanionRemoving(companion.id) || !this.store.snapshot().companions.some(item => item.id === companion.id))
|
|
728
|
+
throw new Error('伙伴正在删除或已不存在');
|
|
729
|
+
const id = companion.id;
|
|
730
|
+
this.agentSetupJobs.set(id, (this.agentSetupJobs.get(id) ?? 0) + 1);
|
|
731
|
+
try {
|
|
732
|
+
return await this.ensureAgentReady(companion, route);
|
|
733
|
+
}
|
|
734
|
+
finally {
|
|
735
|
+
const left = (this.agentSetupJobs.get(id) ?? 1) - 1;
|
|
736
|
+
if (left)
|
|
737
|
+
this.agentSetupJobs.set(id, left);
|
|
738
|
+
else
|
|
739
|
+
this.agentSetupJobs.delete(id);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async ensureAgentReady(companion, route) {
|
|
662
743
|
const cwd = route.cwd ?? this.defaultCwd;
|
|
663
744
|
await mkdir(cwd, { recursive: true, mode: 0o700 });
|
|
664
745
|
const profile = await this.memory?.profileSnapshot(companion.id, memoryScope(route.channelId, route.userId)).catch(() => undefined);
|
|
@@ -798,62 +879,6 @@ export function selectTaskNotificationRoute(routes, creatorSessionId, archived)
|
|
|
798
879
|
function delay(milliseconds) {
|
|
799
880
|
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
800
881
|
}
|
|
801
|
-
export async function extractOutboundAttachments(text, cwd) {
|
|
802
|
-
const candidates = new Set();
|
|
803
|
-
for (const match of text.matchAll(/\[[^\]]*\]\((?:<)?(file:\/\/)?([^)>]+)(?:>)?\)/g)) {
|
|
804
|
-
const value = decodeURIComponent(match[2] ?? '').trim();
|
|
805
|
-
if (value)
|
|
806
|
-
candidates.add(value);
|
|
807
|
-
}
|
|
808
|
-
for (const match of text.matchAll(/`([^`\n]+)`/g)) {
|
|
809
|
-
const value = match[1]?.trim();
|
|
810
|
-
if (value && mediaTypeFor(value))
|
|
811
|
-
candidates.add(value);
|
|
812
|
-
}
|
|
813
|
-
for (const line of text.split('\n').map(item => item.trim())) {
|
|
814
|
-
const value = line.replace(/^`|`$/g, '');
|
|
815
|
-
if (value.startsWith('/') && !value.includes(' '))
|
|
816
|
-
candidates.add(value);
|
|
817
|
-
}
|
|
818
|
-
const root = await realpath(cwd);
|
|
819
|
-
const result = [];
|
|
820
|
-
const included = new Set();
|
|
821
|
-
for (const candidate of candidates) {
|
|
822
|
-
if (result.length >= 8)
|
|
823
|
-
break;
|
|
824
|
-
const target = resolve(root, candidate);
|
|
825
|
-
let actual;
|
|
826
|
-
try {
|
|
827
|
-
actual = await realpath(target);
|
|
828
|
-
}
|
|
829
|
-
catch {
|
|
830
|
-
continue;
|
|
831
|
-
}
|
|
832
|
-
if (included.has(actual))
|
|
833
|
-
continue;
|
|
834
|
-
const rel = relative(root, actual);
|
|
835
|
-
if (rel.startsWith('..') || rel === '..' || resolve(root, rel) !== actual)
|
|
836
|
-
continue;
|
|
837
|
-
const info = await stat(actual);
|
|
838
|
-
if (!info.isFile() || info.size > PARTNER_MEDIA_MAX_BYTES)
|
|
839
|
-
continue;
|
|
840
|
-
const mediaType = mediaTypeFor(actual);
|
|
841
|
-
if (!mediaType)
|
|
842
|
-
continue;
|
|
843
|
-
included.add(actual);
|
|
844
|
-
result.push({ path: actual, name: basename(actual), mediaType, kind: mediaType.startsWith('image/') ? 'image' : 'file' });
|
|
845
|
-
}
|
|
846
|
-
return result;
|
|
847
|
-
}
|
|
848
|
-
function mediaTypeFor(path) {
|
|
849
|
-
return {
|
|
850
|
-
'.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp',
|
|
851
|
-
'.pdf': 'application/pdf', '.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
852
|
-
'.xls': 'application/vnd.ms-excel', '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
853
|
-
'.ppt': 'application/vnd.ms-powerpoint', '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
854
|
-
'.txt': 'text/plain', '.md': 'text/markdown', '.csv': 'text/csv', '.json': 'application/json', '.zip': 'application/zip',
|
|
855
|
-
}[extname(path).toLowerCase()];
|
|
856
|
-
}
|
|
857
882
|
export function completedTurnEvents(events, end) {
|
|
858
883
|
let startSeq;
|
|
859
884
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|