@lemoncat7/dsh-partner 1.8.2 → 1.8.3

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.
Files changed (55) hide show
  1. package/README.md +10 -2
  2. package/docs/architecture.md +17 -11
  3. package/lib/agent-runtime.d.ts +7 -2
  4. package/lib/agent-runtime.d.ts.map +1 -1
  5. package/lib/agent-runtime.js +93 -102
  6. package/lib/agent-runtime.js.map +1 -1
  7. package/lib/api/features/workspace-api.d.ts.map +1 -1
  8. package/lib/api/features/workspace-api.js +1 -10
  9. package/lib/api/features/workspace-api.js.map +1 -1
  10. package/lib/api.js +8 -13
  11. package/lib/api.js.map +1 -1
  12. package/lib/client.d.ts.map +1 -1
  13. package/lib/client.js +778 -729
  14. package/lib/client.js.map +4 -4
  15. package/lib/collaboration/composition.d.ts.map +1 -1
  16. package/lib/collaboration/composition.js +13 -5
  17. package/lib/collaboration/composition.js.map +1 -1
  18. package/lib/companions/management-tool.d.ts +1 -1
  19. package/lib/companions/management-tool.d.ts.map +1 -1
  20. package/lib/companions/management-tool.js +1 -1
  21. package/lib/companions/management-tool.js.map +1 -1
  22. package/lib/companions/management.d.ts +4 -2
  23. package/lib/companions/management.d.ts.map +1 -1
  24. package/lib/companions/management.js +2 -2
  25. package/lib/companions/management.js.map +1 -1
  26. package/lib/companions/session-configuration.d.ts +19 -0
  27. package/lib/companions/session-configuration.d.ts.map +1 -0
  28. package/lib/companions/session-configuration.js +38 -0
  29. package/lib/companions/session-configuration.js.map +1 -0
  30. package/lib/index.d.ts.map +1 -1
  31. package/lib/index.js +1 -3
  32. package/lib/index.js.map +1 -1
  33. package/lib/pendant/notice-motion.d.ts.map +1 -1
  34. package/lib/pendant/notice-motion.js +12 -6
  35. package/lib/pendant/notice-motion.js.map +1 -1
  36. package/lib/pendant/sheen.d.ts +1 -1
  37. package/lib/pendant/sheen.d.ts.map +1 -1
  38. package/lib/pendant/sheen.js +9 -8
  39. package/lib/pendant/sheen.js.map +1 -1
  40. package/lib/pendant/widget.js +1 -1
  41. package/lib/pendant/widget.js.map +1 -1
  42. package/lib/pendant-renderer.js +17 -17
  43. package/lib/store.d.ts +3 -0
  44. package/lib/store.d.ts.map +1 -1
  45. package/lib/store.js +4 -0
  46. package/lib/store.js.map +1 -1
  47. package/lib/ui/capability-editor.d.ts +7 -0
  48. package/lib/ui/capability-editor.d.ts.map +1 -0
  49. package/lib/ui/capability-editor.js +85 -0
  50. package/lib/ui/capability-editor.js.map +1 -0
  51. package/lib/ui/companion-draft.d.ts +12 -0
  52. package/lib/ui/companion-draft.d.ts.map +1 -0
  53. package/lib/ui/companion-draft.js +2 -0
  54. package/lib/ui/companion-draft.js.map +1 -0
  55. package/package.json +1 -1
package/README.md CHANGED
@@ -6,7 +6,15 @@
6
6
 
7
7
  ## 兼容性
8
8
 
9
- 正式版 `1.8.2` 适配 DeepSeek Harness `0.1.2-rc.1`,需要 Node.js `22.19+` 或 `24+`。DSH 官方 Agent、Session、Settings、Tools 与客户端 UI 依赖均锁定到 `0.1.2-rc.1` 接口族。
9
+ 正式版 `1.8.3` 适配 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.8.3 更新
12
+
13
+ - 伙伴身份、能力、Skill 和协作配置保存后,在下一轮执行前统一注册到会话;覆盖已有浏览器会话和历史会话恢复,不在正在执行的轮次中重组工具,也不唤醒闲置会话。高权限管理工具继续在调用时检查当前授权。
14
+ - 「应用能力组合」增加保存中、成功和失败反馈,成功显示「已保存,下一轮生效」;防止重复提交,失败保留草稿,区分保存失败与保存后刷新失败。
15
+ - 新消息轻摆转向文字面,仅在文字面扫光两次;结束及阅读后不自动恢复图案面,不锁定用户手动翻面。
16
+ - 挂点旁增加小数字未读标记,超过 99 显示 99+,全部已读后隐藏;支持深浅色与减少动态效果,不增加常驻动画或渲染循环。
17
+ - 保留自定义图案、现有卡片材质、24/30/60 FPS 与休眠策略,不改 DSH 源码或伙伴数据。162 项自动化测试及隔离浏览器交互回归通过。
10
18
 
11
19
  ### 1.8.2 更新
12
20
 
@@ -97,7 +105,7 @@
97
105
 
98
106
  知识库挂载要求同时安装包含 `dshKnowledgeMountManagement` 接口的知识库插件;旧版或未安装时会明确提示,身份、能力等管理仍可用。挂载通过知识库当前选中的本地或远程后端执行,远程只读令牌不能修改挂载,不会绕过认证或退回本地写入。
99
107
 
100
- 目标伙伴执行中拒绝修改,配置过期须重新读取。配置已保存但会话未能刷新时,工具会返回 `applied: true` 和 `runtime: reopen-required`,应重新打开目标伙伴会话后使用新配置,不要重复提交。此权限不自动开启任何伙伴的记忆、心跳或渠道。
108
+ 目标伙伴执行中拒绝管理工具修改,配置过期须重新读取。配置保存成功后,身份、能力、Skill 与协作关系在下一轮执行前注册,无需为了注入工具重开会话;Preset 和模型作为会话默认配置,不覆盖已有会话的选择。此权限不自动开启任何伙伴的记忆、心跳或渠道。
101
109
 
102
110
  ## 安装
103
111
 
@@ -12,13 +12,19 @@ client -> HTTP feature routers -> application services -> repositories/store
12
12
 
13
13
  ## Module boundaries
14
14
 
15
- Capability saves recompose already-live companion sessions immediately after
16
- releasing the old composition. Browser-owned agents retain their session and
17
- receive fresh scoped identity/tools; inactive sessions are not eagerly opened.
18
- Busy companions reject configuration changes before teardown. Revocation removes
19
- the tool and management services also recheck live authorization on every call.
20
- Browser-owned preset/model changes still report that reopening is required;
21
- this is separate from refreshing scoped companion tools.
15
+ Configuration saves only commit state. The awaited `agent/pre-step` hook compares
16
+ the saved configuration with the installed revision before each new turn, covering
17
+ native history resume as well as local, channel and task-board conversations.
18
+ Unchanged configurations do not re-register tools or read Skill bodies again;
19
+ mid-turn saves never dismantle a running composition. No polling or eager wakeup
20
+ of inactive sessions is needed. Plugin-owned tools recheck capability revocations
21
+ at execution even when an old schema remains in the current turn.
22
+ The UI reports saved/next-turn, separately from request failures and refresh
23
+ failures. Preset and model defaults remain subject to DSH session-selection rules;
24
+ the plugin does not bypass restrictions on changing an existing session's preset.
25
+ `companions/session-configuration.ts` indexes persisted routes and configuration
26
+ revisions without transcript copies. `ui/capability-editor.tsx` owns the capability
27
+ form and save feedback, using the shared companion draft and existing UI tokens.
22
28
 
23
29
  - `core/`: shared validation, identifiers and bounded collections. It contains
24
30
  no partner feature policy.
@@ -109,9 +115,9 @@ this is separate from refreshing scoped companion tools.
109
115
  Identity, enabled Skill IDs and outgoing grants commit together. Unknown
110
116
  fields, stale revisions, cancellation, missing resources and busy targets
111
117
  fail closed. Authority is checked again inside the serialized store write.
112
- A reload error after commit is reported as saved/reopen-required, not a failed
113
- write; browser-owned Agent handles are not forcibly destroyed or reported as
114
- hot-reloaded. Unspecified settings, including automation, remain unchanged.
118
+ A runtime confirmation error after commit is reported as saved/next-turn with
119
+ a warning, not a failed write. Browser-owned Agent handles are not destroyed.
120
+ Unspecified settings, including automation, remain unchanged.
115
121
  - Knowledge mount management is a separate provider-backed operation. The
116
122
  adapter derives the target's default project and existing session scopes on
117
123
  the server, never from model-supplied paths. It calls the optional
@@ -148,7 +154,7 @@ this is separate from refreshing scoped companion tools.
148
154
  - 点击层不绘制材质或装饰边框,鼠标拖动不触发键盘聚焦,键盘操作保留可见焦点。卸载时一并清理小画布、绳带节点和物理资源。
149
155
  - `pendant/use-placement.ts` 只负责挂点位置:拖动顶部挂点或用方向键移动整件挂饰,与卡牌拉伸手势分离。位置按可用视口比例保存在本地浏览器,刷新恢复,窄屏及旋转时约束到可见范围;逐帧移动不触发 React 状态更新,完成时才写存储,不查询宿主私有 DOM。
150
156
  - 卡牌正面只负责图案,消息更新不会重绘或覆盖正面纹理;内侧单独绘制未读数量、伙伴名和简短状态。图案与消息使用独立纹理,不自动弹出消息或额外角标。
151
- - `pendant/notice-motion.ts` 统一处理新消息提醒:等待被握持或快速甩动的卡牌空闲,轻摆并以短暂的物理角速度辅助转回正面,再由 `pendant/sheen.ts` 在正面轻扫光两次。提醒不修改卡牌的自由拖拽物理结构,多条到达合并一次动作,抓取可中断;后台、全部已读及减少动态效果时取消。效果共用卡面圆角几何,不使用全屏后处理、独立计时器或逐帧纹理重绘。
157
+ - `pendant/notice-motion.ts` 统一处理新消息提醒:等待被握持或快速甩动的卡牌空闲,轻摆并以短暂的物理角速度辅助转向文字面(背面,Y 轴半周),再由 `pendant/sheen.ts` 仅在文字面轻扫光两次。结束或阅读后不自动恢复图案面,也不持续锁定朝向;抓取可中断,用户仍能自由翻面。多条到达合并一次动作;后台、全部已读及减少动态效果时取消。效果共用卡面圆角几何,不使用全屏后处理、独立计时器或逐帧纹理重绘。挂点旁保留静态小数字未读标记(超过 99 显示 99+,全部已读后隐藏),翻面及减少动态效果时仍可见,不拦截拖动、不唤醒渲染循环;读屏复用消息按钮和现有单一播报区。
152
158
  - `pendant/reader.tsx` 点击打开最新未读正文(无未读时展示列表),保留可见、可玩的挂饰;桌面优先放在卡牌旁边,窄屏选择卡牌上方或下方的可用空间。正文独立滚动,来源操作留在底部;使用独立冷灰纸面材质,支持 Escape 关闭、键盘焦点返回、卡牌再次点击关闭和来源导航。静止挂卡即使在阅读时也会自然休眠。
153
159
  - `notifications/service.ts` 从已提交的任务/定时执行变化和完整伙伴回复生成终态通知;不改变任务执行,不暴露内部验收交接。普通回复按会话和轮次去重,任务按 ID/修订/终态去重。
154
160
  - `notifications/store.ts` 在主状态文件同目录保存 `partner-inbox.sqlite`,权限 0600,最多 200 条摘要(每条最多 2000 字符)及已读状态。它不是新的会话历史;完整内容仍在原任务/会话中。
@@ -1,4 +1,5 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
+ import type { Agent } from '@deepseek-ai/dsh-agent';
2
3
  import type { AgentDefaultModelConfig } from '@deepseek-ai/dsh-agent-default-model';
3
4
  import type { AgentPresets } from '@deepseek-ai/dsh-agent-presets';
4
5
  import type { SessionId } from '@deepseek-ai/dsh-session';
@@ -66,6 +67,10 @@ export declare class PartnerAgentRuntime {
66
67
  private readonly restoredCompositions;
67
68
  private readonly restoredCompositionJobs;
68
69
  private readonly disposeAgentListener;
70
+ private readonly disposeConfigurationListener;
71
+ private readonly configurations;
72
+ private readonly preparedTurns;
73
+ private closed;
69
74
  private questionAnswerer;
70
75
  constructor(ctx: RuntimeContext, store: PartnerStore, defaultCwd: string, memory?: PartnerMemoryStore | undefined, reflection?: MemoryReflectionService | undefined, concerns?: PartnerConcernStore | undefined, composer?: PartnerAgentComposer | undefined);
71
76
  setQuestionAnswerer(answerer: PartnerQuestionAnswerer): void;
@@ -100,7 +105,8 @@ export declare class PartnerAgentRuntime {
100
105
  recordHeartbeatActivity(companion: Companion, route: ChannelSession, execution: HeartbeatExecution, outcome: HeartbeatOutcome, deliveryError?: string): Promise<void>;
101
106
  resetCompanion(companionId: string): Promise<void>;
102
107
  reloadCompanion(companionId: string): Promise<void>;
103
- reloadManagedCompanion(companionId: string): Promise<void>;
108
+ /** Awaited by the host before prompt/tool assembly, never from a running tool. */
109
+ prepareAgentTurn(agent: Agent, turn: number, signal: AbortSignal): Promise<void>;
104
110
  isCompanionBusy(companionId: string): boolean;
105
111
  private releaseCompanion;
106
112
  resetChannel(channelId: string): Promise<void>;
@@ -110,7 +116,6 @@ export declare class PartnerAgentRuntime {
110
116
  private persistInbound;
111
117
  private ensureSession;
112
118
  private ensureAgent;
113
- private ensureRestoredComposition;
114
119
  private composeRestoredAgent;
115
120
  private injectProfileUpdate;
116
121
  private attachSession;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-runtime.d.ts","sourceRoot":"","sources":["../src/agent-runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-runtime.d.ts","sourceRoot":"","sources":["../src/agent-runtime.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import { mkdir, realpath, stat, writeFile } from 'node:fs/promises';
3
3
  import { basename, extname, 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
+ import { SessionConfigurationIndex } from './companions/session-configuration.js';
6
7
  import { concernObservationPrompt } from './autonomy.js';
7
8
  import { boundedConcernCheckMinutes } from './concern-domain.js';
8
9
  import { PARTNER_MEDIA_MAX_BYTES, safeMediaName } from './channel-message.js';
@@ -54,6 +55,10 @@ export class PartnerAgentRuntime {
54
55
  restoredCompositions = new Map();
55
56
  restoredCompositionJobs = new Map();
56
57
  disposeAgentListener;
58
+ disposeConfigurationListener;
59
+ configurations;
60
+ preparedTurns = new WeakMap();
61
+ closed = false;
57
62
  questionAnswerer;
58
63
  constructor(ctx, store, defaultCwd, memory, reflection, concerns, composer) {
59
64
  this.ctx = ctx;
@@ -63,8 +68,15 @@ export class PartnerAgentRuntime {
63
68
  this.reflection = reflection;
64
69
  this.concerns = concerns;
65
70
  this.composer = composer;
71
+ this.configurations = new SessionConfigurationIndex(store);
72
+ this.disposeConfigurationListener = typeof this.ctx.on === 'function'
73
+ ? this.ctx.on('agent/pre-step', async ({ agent, turn, signal }, next) => {
74
+ await this.prepareAgentTurn(agent, turn, signal);
75
+ return next();
76
+ }) : () => { };
66
77
  this.disposeAgentListener = typeof this.ctx.on === 'function'
67
78
  ? this.ctx.on('agent/disposed', ({ agent }) => {
79
+ this.preparedTurns.delete(agent);
68
80
  const restored = this.restoredCompositions.get(agent.session.id);
69
81
  if (restored?.agent !== agent)
70
82
  return;
@@ -477,31 +489,38 @@ export class PartnerAgentRuntime {
477
489
  await this.store.update(state => { state.sessions = state.sessions.filter(item => item.companionId !== companionId); });
478
490
  }
479
491
  async reloadCompanion(companionId) {
480
- if (this.isCompanionBusy(companionId))
481
- throw new Error('伙伴正在执行,请等待当前回复结束后再修改能力');
482
- const activeRoutes = this.store.snapshot().sessions.filter(route => route.companionId === companionId
483
- && (this.handles.has(route.sessionId) || this.ctx.agents.get(route.sessionId) !== undefined));
484
- await this.releaseCompanion(companionId);
485
- // Releasing a restored composition does not close the browser-owned Agent.
486
- // Recompose it now: staying in the same conversation must not leave it with
487
- // no partner tools until the user happens to click "open session" again.
488
- for (const route of activeRoutes) {
489
- const state = this.store.snapshot();
490
- const companion = state.companions.find(item => item.id === companionId);
491
- const currentRoute = state.sessions.find(item => item.id === route.id && item.companionId === companionId);
492
- if (companion && currentRoute)
493
- await this.ensureAgent(companion, currentRoute);
494
- }
492
+ // Compatibility for callers that saved a configuration. The index already
493
+ // observes committed writes; do not tear down or wake any Agent here.
494
+ if (!this.configurations.companion(companionId))
495
+ throw new Error('伙伴不存在');
495
496
  }
496
- async reloadManagedCompanion(companionId) {
497
- const externallyOwned = this.store.snapshot().sessions.some(route => route.companionId === companionId
498
- && !this.handles.has(route.sessionId) && this.ctx.agents.get(route.sessionId) !== undefined);
499
- await this.reloadCompanion(companionId);
500
- // Browser-owned handles cannot be disposed by this plugin. In particular,
501
- // their preset/model must not be reported as hot-reloaded successfully.
502
- if (externallyOwned) {
503
- throw new Error('目标伙伴还有由 DSH 管理的活动会话,请重新打开会话使配置生效');
497
+ /** Awaited by the host before prompt/tool assembly, never from a running tool. */
498
+ async prepareAgentTurn(agent, turn, signal) {
499
+ if (this.closed)
500
+ throw new Error('伙伴运行时已关闭');
501
+ if (this.preparedTurns.get(agent) === turn)
502
+ return;
503
+ const configuration = this.configurations.forSession(agent.session.id);
504
+ if (!configuration)
505
+ return;
506
+ signal.throwIfAborted();
507
+ const installed = this.restoredCompositions.get(agent.session.id);
508
+ if (installed?.agent !== agent || installed.revision !== configuration.revision) {
509
+ const profile = await this.memory?.profileSnapshot(configuration.companion.id, memoryScope(configuration.route.channelId, configuration.route.userId)).catch(() => undefined);
510
+ signal.throwIfAborted();
511
+ if (this.closed)
512
+ throw new Error('伙伴运行时已关闭');
513
+ const job = this.composeRestoredAgent(agent, configuration.companion, configuration.route, configuration.revision, profile, signal);
514
+ this.restoredCompositionJobs.set(agent.session.id, job);
515
+ try {
516
+ await job;
517
+ }
518
+ finally {
519
+ if (this.restoredCompositionJobs.get(agent.session.id) === job)
520
+ this.restoredCompositionJobs.delete(agent.session.id);
521
+ }
504
522
  }
523
+ this.preparedTurns.set(agent, turn);
505
524
  }
506
525
  isCompanionBusy(companionId) {
507
526
  return this.store.snapshot().sessions.some(route => route.companionId === companionId
@@ -529,7 +548,10 @@ export class PartnerAgentRuntime {
529
548
  await this.store.update(state => { state.sessions = state.sessions.filter(item => item.channelId !== channelId); });
530
549
  }
531
550
  async close() {
551
+ this.closed = true;
552
+ this.disposeConfigurationListener();
532
553
  this.disposeAgentListener();
554
+ this.configurations.close();
533
555
  await Promise.allSettled(this.restoredCompositionJobs.values());
534
556
  for (const restored of this.restoredCompositions.values())
535
557
  restored.dispose();
@@ -545,6 +567,8 @@ export class PartnerAgentRuntime {
545
567
  async releaseRestoredComposition(sessionId) {
546
568
  await this.restoredCompositionJobs.get(sessionId)?.catch(() => { });
547
569
  const restored = this.restoredCompositions.get(sessionId);
570
+ if (restored)
571
+ this.preparedTurns.delete(restored.agent);
548
572
  restored?.dispose();
549
573
  this.restoredCompositions.delete(sessionId);
550
574
  }
@@ -647,7 +671,6 @@ export class PartnerAgentRuntime {
647
671
  }
648
672
  const live = this.ctx.agents.get(route.sessionId);
649
673
  if (live !== undefined) {
650
- await this.ensureRestoredComposition(live, companion, route, profile);
651
674
  await this.attachSession(companion, route);
652
675
  if (profile)
653
676
  this.injectProfileUpdate(live, route.sessionId, profile);
@@ -659,24 +682,8 @@ export class PartnerAgentRuntime {
659
682
  if (presets === undefined)
660
683
  throw new Error('伙伴会话缺少 Agent Presets 服务');
661
684
  await presets.mount(agentCtx, companion.presetId);
662
- agentCtx.systemPrompt.section({
663
- name: 'partner-identity',
664
- order: -10,
665
- text: renderPartnerPersona(companion, route.kind === 'local' ? 'local' : 'conversation'),
666
- });
667
- agentCtx.systemPrompt.section({
668
- name: 'partner-tool-routing',
669
- order: -9,
670
- text: renderToolProtocol(),
671
- });
672
- if (profile && profile.entries.length > 0)
673
- agentCtx.systemPrompt.section({
674
- name: 'partner-user-profile',
675
- order: -8,
676
- text: renderProfile(profile),
677
- });
678
- this.questionAnswerer?.(agentCtx, route);
679
- await this.composer?.compose(agentCtx, companion);
685
+ // Partner composition is installed at the same awaited pre-step boundary
686
+ // for factory-created and native browser-resumed Agents alike.
680
687
  };
681
688
  let handle;
682
689
  try {
@@ -703,69 +710,53 @@ export class PartnerAgentRuntime {
703
710
  await this.attachSession(companion, route);
704
711
  return handle.agent;
705
712
  }
706
- async ensureRestoredComposition(agent, companion, route, profile) {
707
- const installed = this.restoredCompositions.get(route.sessionId);
708
- if (installed?.agent === agent)
709
- return;
710
- const pending = this.restoredCompositionJobs.get(route.sessionId);
711
- if (pending !== undefined)
712
- return pending;
713
- const job = this.composeRestoredAgent(agent, companion, route, profile);
714
- this.restoredCompositionJobs.set(route.sessionId, job);
715
- try {
716
- await job;
717
- }
718
- finally {
719
- if (this.restoredCompositionJobs.get(route.sessionId) === job)
720
- this.restoredCompositionJobs.delete(route.sessionId);
713
+ async composeRestoredAgent(agent, companion, route, revision, profile, signal) {
714
+ // Already inside the driver's awaited pre-step hook. Waiting for idle or
715
+ // runMaintenance here would deadlock the very turn we are preparing.
716
+ signal.throwIfAborted();
717
+ const agentCtx = agent.ctx;
718
+ const key = scopeOf(agentCtx);
719
+ if (key === undefined)
720
+ throw new Error('伙伴会话缺少 Agent 作用域');
721
+ if (scopeParentOf(key) === undefined) {
722
+ const presets = agentCtx.get('agentPresets');
723
+ if (presets === undefined)
724
+ throw new Error('伙伴会话缺少 Agent Presets 服务');
725
+ await presets.mount(agentCtx, companion.presetId);
721
726
  }
722
- }
723
- async composeRestoredAgent(agent, companion, route, profile) {
724
- await agent.whenIdle();
725
- let dispose;
726
- await agent.runMaintenance(async (signal) => {
727
- if (signal.aborted)
728
- throw signal.reason;
729
- const agentCtx = agent.ctx;
730
- const key = scopeOf(agentCtx);
731
- if (key === undefined)
732
- throw new Error('伙伴会话缺少 Agent 作用域');
733
- if (scopeParentOf(key) === undefined) {
734
- const presets = agentCtx.get('agentPresets');
735
- if (presets === undefined)
736
- throw new Error('伙伴会话缺少 Agent Presets 服务');
737
- await presets.mount(agentCtx, companion.presetId);
738
- }
739
- const disposers = [];
740
- try {
741
- disposers.push(agentCtx.systemPrompt.section({ name: 'partner-identity', order: -10, text: renderPartnerPersona(companion, route.kind === 'local' ? 'local' : 'conversation') }));
742
- disposers.push(agentCtx.systemPrompt.section({ name: 'partner-tool-routing', order: -9, text: renderToolProtocol() }));
743
- if (profile && profile.entries.length > 0)
744
- disposers.push(agentCtx.systemPrompt.section({ name: 'partner-user-profile', order: -8, text: renderProfile(profile) }));
745
- if (this.questionAnswerer)
746
- disposers.push(this.questionAnswerer(agentCtx, route));
747
- if (this.composer)
748
- disposers.push(await this.composer.compose(agentCtx, companion));
749
- }
750
- catch (error) {
751
- for (let index = disposers.length - 1; index >= 0; index -= 1)
752
- disposers[index]?.();
753
- throw error;
754
- }
755
- let disposed = false;
756
- dispose = () => {
757
- if (disposed)
758
- return;
759
- disposed = true;
760
- for (let index = disposers.length - 1; index >= 0; index -= 1)
761
- disposers[index]?.();
762
- };
763
- });
764
- if (dispose === undefined)
765
- throw new Error('伙伴会话恢复组合未完成');
766
727
  const previous = this.restoredCompositions.get(route.sessionId);
767
728
  previous?.dispose();
768
- this.restoredCompositions.set(route.sessionId, { agent, dispose });
729
+ this.restoredCompositions.delete(route.sessionId);
730
+ const disposers = [];
731
+ try {
732
+ disposers.push(agentCtx.systemPrompt.section({ name: 'partner-identity', order: -10, text: renderPartnerPersona(companion, route.kind === 'local' ? 'local' : 'conversation') }));
733
+ disposers.push(agentCtx.systemPrompt.section({ name: 'partner-tool-routing', order: -9, text: renderToolProtocol() }));
734
+ if (profile && profile.entries.length > 0)
735
+ disposers.push(agentCtx.systemPrompt.section({ name: 'partner-user-profile', order: -8, text: renderProfile(profile) }));
736
+ if (this.questionAnswerer)
737
+ disposers.push(this.questionAnswerer(agentCtx, route));
738
+ if (this.composer)
739
+ disposers.push(await this.composer.compose(agentCtx, companion));
740
+ signal.throwIfAborted();
741
+ if (this.closed)
742
+ throw new Error('伙伴运行时已关闭');
743
+ }
744
+ catch (error) {
745
+ for (let index = disposers.length - 1; index >= 0; index -= 1)
746
+ disposers[index]?.();
747
+ throw error;
748
+ }
749
+ let disposed = false;
750
+ const dispose = () => {
751
+ if (disposed)
752
+ return;
753
+ disposed = true;
754
+ for (let index = disposers.length - 1; index >= 0; index -= 1)
755
+ disposers[index]?.();
756
+ };
757
+ // Keep the captured revision: a concurrent save must be applied next turn,
758
+ // never incorrectly recorded as already installed by this composition.
759
+ this.restoredCompositions.set(route.sessionId, { agent, dispose, revision });
769
760
  }
770
761
  injectProfileUpdate(agent, sessionId, profile) {
771
762
  const previous = this.profileVersions.get(sessionId);