@lemoncat7/dsh-partner 2.21.11 → 2.21.13
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 +3 -1
- package/docs/conversation-autonomy.md +27 -0
- package/docs/releases/2.21.12.md +5 -0
- package/docs/releases/2.21.13.md +13 -0
- package/lib/agent-runtime.d.ts +2 -1
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +54 -32
- package/lib/agent-runtime.js.map +1 -1
- package/lib/channels/authored-progress.d.ts +16 -0
- package/lib/channels/authored-progress.d.ts.map +1 -0
- package/lib/channels/authored-progress.js +38 -0
- package/lib/channels/authored-progress.js.map +1 -0
- package/lib/channels/manager.d.ts.map +1 -1
- package/lib/channels/manager.js +9 -6
- package/lib/channels/manager.js.map +1 -1
- package/lib/channels/qq/attention-classifier.js +5 -5
- package/lib/channels/qq/attention-classifier.js.map +1 -1
- package/lib/character/activity-tool.d.ts +3 -0
- package/lib/character/activity-tool.d.ts.map +1 -1
- package/lib/character/activity-tool.js +8 -1
- package/lib/character/activity-tool.js.map +1 -1
- package/lib/character/affection.d.ts +2 -2
- package/lib/character/conversation-behavior.d.ts +2 -0
- package/lib/character/conversation-behavior.d.ts.map +1 -1
- package/lib/character/conversation-behavior.js +7 -1
- package/lib/character/conversation-behavior.js.map +1 -1
- package/lib/character/idle-contact.d.ts.map +1 -1
- package/lib/character/idle-contact.js +13 -1
- package/lib/character/idle-contact.js.map +1 -1
- package/lib/character/post-reply.js +2 -2
- package/lib/character/post-reply.js.map +1 -1
- package/lib/client.js +31 -26
- package/lib/client.js.map +3 -3
- package/lib/collaboration/tool-discovery.d.ts.map +1 -1
- package/lib/collaboration/tool-discovery.js +11 -4
- package/lib/collaboration/tool-discovery.js.map +1 -1
- package/lib/core/semaphore.d.ts.map +1 -1
- package/lib/core/semaphore.js +6 -3
- package/lib/core/semaphore.js.map +1 -1
- package/lib/execution/reply-progress.d.ts +3 -1
- package/lib/execution/reply-progress.d.ts.map +1 -1
- package/lib/execution/reply-progress.js +16 -0
- package/lib/execution/reply-progress.js.map +1 -1
- package/lib/execution/task-preamble.d.ts +15 -0
- package/lib/execution/task-preamble.d.ts.map +1 -0
- package/lib/execution/task-preamble.js +107 -0
- package/lib/execution/task-preamble.js.map +1 -0
- package/lib/memory-admission.d.ts +5 -0
- package/lib/memory-admission.d.ts.map +1 -0
- package/lib/memory-admission.js +20 -0
- package/lib/memory-admission.js.map +1 -0
- package/lib/memory-reflection.js +1 -1
- package/lib/ui/push-draft.d.ts +2 -2
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@lemoncat7/dsh-partner)
|
|
6
6
|
|
|
7
|
+
本次发布:[2.21.13 更新说明](docs/releases/2.21.13.md),包含 [2.21.12 会话与自主活动改进](docs/releases/2.21.12.md)。
|
|
8
|
+
|
|
7
9
|
## 功能
|
|
8
10
|
|
|
9
11
|
- 伙伴身份、Agent Preset、模型与能力管理;可选人格卡、创建引导、角色记忆与独立试聊。
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
|
|
18
20
|
## 安装
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
当前插件版本 **2.21.13**,最低支持 DeepSeek Harness **0.2.1-rc.2**,已在 **0.2.1-rc.5** 部署验证(宿主仍为 RC),需要 Node.js **^22.19.0 或 >=24.0.0**。其他宿主版本请先查阅[兼容说明](docs/dsh-021-compatibility.md)和对应版本的发布说明。
|
|
21
23
|
|
|
22
24
|
```bash
|
|
23
25
|
dsh plugin --profile web add @lemoncat7/dsh-partner@latest
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 会话并发、人格回应与记忆收录
|
|
2
|
+
|
|
3
|
+
独立会话按真实 sessionId 排队,同时最多执行 6 个渠道回复。同一群的不同成员共用群会话,仍按顺序处理;明确配置共享主会话的联系人也保持串行。不同群、独立私聊不再因为属于同一个伙伴互相等待。群聊本地分类独立处理最多 4 个请求,超过容量立即回退规则判断;仍保留 1.2 秒超时和故障熔断。
|
|
4
|
+
|
|
5
|
+
回复成功送达后,每个符合既有自主活动授权条件的用户轮次独立抽取一次主动延续机会。概率为 5% + 40% × 亲密度进度²,最高 45%;关闭亲密度保留 5%。自主活动总开关、睡眠状态、渠道权限和 QQ 身份限制继续生效。重复投递不能重复抽取,自主活动不会为自身创建新的回复后机会。
|
|
6
|
+
|
|
7
|
+
机会仍在原会话中运行,带入最近最多 8 条对话摘录(每条最多 800 字)、最近 3 次活动结果与当前可用 QQ 能力摘要。工具执行时按实时权限校验。角色可以探索、聊天、发说说、做一组相关动作或安静结束。提示建议本轮限制为 6 次活动工具调用、3 次对外表达;这是模型行为约束,不是新增的硬性工具计数器。新输入只取消同一真实会话的旧机会,不取消其他独立会话的机会。优先选择更早到期的可运行机会。
|
|
8
|
+
|
|
9
|
+
私聊调查和执行任务时,角色可在首次工作工具调用前生成自然回应。QQ/微信转发该可见正文,最多 3 段,去重后按顺序发送;最终回复等待这些发送收尾。不会转发 reasoning、工具参数或固定状态标签,失败不自动重发未知结果。聊天终结工具的附带文字不作为过程消息再次发送。支持过程消息的其他渠道沿用原投递控制器。
|
|
10
|
+
|
|
11
|
+
回应和最终交付都服从角色设定,不使用硬编码接任务话术。长任务使用已有后台任务/需求与结果回传能力;没有相应能力时继续当前执行。未新增另一套后台任务引擎;2.21.13 的前置回应执行保障见下文。
|
|
12
|
+
|
|
13
|
+
长期记忆自动提炼以主会话为入口。独立私聊和群聊保留自己的对话上下文、按现有权限读取共享记忆,但不自动写入长期记忆;主会话里的群来源消息也排除,纯问候和自主活动通知不收录。上下文整理的历史补录遵循同样规则,不自动把旧群记忆迁入主会话。主会话提炼提示优先保留稳定偏好、重要约定、明确记忆请求与有后续价值的进展,允许不新增条目。已有记忆不删除;手动记忆和明确授权的工具写入不受自动收录规则替代。
|
|
14
|
+
|
|
15
|
+
独立会话上下文焕新不再要求先将整段群聊归档进长期记忆。主会话仍等待应收录内容完成提炼和回顾后再焕新。
|
|
16
|
+
|
|
17
|
+
## 生图工具发现
|
|
18
|
+
|
|
19
|
+
`generate_image` 增加独立的 media(图片创作)分类和中文检索别名。旧版本生图工具即使仅有英文说明,也可通过“生图 / 画图 / 生成图片 / 图生图 / 改图”等词发现。目录只返回当前确实存在且已授权的工具,不会因关键词增加权限。
|
|
20
|
+
|
|
21
|
+
统一入口提示明确:图片任务先查目录,读取参数后实际调用,并按返回文件交付;顶层仅显示入口不等于没有生图能力。生图插件自身增加中文说明。QQ 非归属身份仍遵循仅聊天权限,不开放生成工具。
|
|
22
|
+
|
|
23
|
+
## 2.21.13:前置回应的执行保障
|
|
24
|
+
|
|
25
|
+
2.21.12 仅转发模型附在工具调用旁的正文,模型直接输出工具调用时不会产生前置回应。2.21.13 在当前私聊回复期间注册 `partner_chat_progress` 非终结工具,并在工作工具 pre-execute 阶段检查回应。角色先生成自己的话并等待渠道确认;缺失时工作不会执行,模型收到调用进度回复工具的指引。原生带正文的工具调用也会等待正文投递。发送失败/未知结果不重复发送,也不放行后续工作。工具发现、普通聊天、群聊与内部自主任务不强制前置回应。
|
|
26
|
+
|
|
27
|
+
修复自主活动选择休息后仍发送解释文字:宿主 compact-checkpoint 摘要不再作为新用户输入阻止静默结束;实际调用 partner_activity_finish 即保留静默意图,即使结束工具失败也不转发兜底正文。重启后的待发送恢复同样检查对应活动的静默决定。真实用户新消息仍不能被静默工具跳过。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 2.21.13
|
|
2
|
+
|
|
3
|
+
插件版本:**2.21.13**。部署验证宿主:DSH **0.2.1-rc.5**。保留原有最低 rc.2 依赖兼容范围。
|
|
4
|
+
|
|
5
|
+
包含 2.21.12 的独立会话并发、群聊判断隔离、回复后概率自主活动、主会话记忆收录与中文生图工具发现改进,详见 [2.21.12](2.21.12.md) 和[行为说明](../conversation-autonomy.md)。
|
|
6
|
+
|
|
7
|
+
修复私聊任务只调用工具、不生成前置正文时一直沉默的问题。新增非终结工具 `partner_chat_progress`:回应由当前角色生成,确认送达后继续任务。实际工作工具在 pre-execute 阶段等待前置回应,缺少回应时返回纠正指引且不执行。发送失败或结果未知时不重复发送、不放行工作;普通聊天和工具目录查询保持直接处理,群聊与自主活动不强制套用私聊回应。
|
|
8
|
+
|
|
9
|
+
保留最终回复流程。前置回应不会结束回合,也不会再次触发回复后自主活动。
|
|
10
|
+
|
|
11
|
+
修复自主活动选择休息后仍发送解释文字:宿主 compact-checkpoint 摘要不再作为新用户输入阻止静默结束;实际调用 partner_activity_finish 即保留静默意图,即使结束工具失败也不转发兜底正文。重启后的待发送恢复同样检查对应活动的静默决定。真实用户新消息仍不能被静默工具跳过。
|
|
12
|
+
|
|
13
|
+
验证:全量 1052 项测试通过;rc.5 生产部署、网页与插件设置验收通过。未发送真实测试消息或触发付费生图。
|
package/lib/agent-runtime.d.ts
CHANGED
|
@@ -84,6 +84,7 @@ export declare class PartnerAgentRuntime {
|
|
|
84
84
|
private readonly latestPostReplyInput;
|
|
85
85
|
private readonly handles;
|
|
86
86
|
private readonly queues;
|
|
87
|
+
private readonly replySlots;
|
|
87
88
|
private readonly heartbeatQueues;
|
|
88
89
|
private readonly taskQueues;
|
|
89
90
|
private readonly steeringQueues;
|
|
@@ -109,7 +110,7 @@ export declare class PartnerAgentRuntime {
|
|
|
109
110
|
setSleepService(service: SleepService): void;
|
|
110
111
|
setQuestionAnswerer(answerer: PartnerQuestionAnswerer): void;
|
|
111
112
|
/** Cancel pending/claimed continuations as soon as another real input is admitted. */
|
|
112
|
-
cancelPostReply(companionId: string, keepUserMessageId?: string): Promise<void>;
|
|
113
|
+
cancelPostReply(companionId: string, keepUserMessageId?: string, sessionId?: string): Promise<void>;
|
|
113
114
|
localReplyDisplayed(source: NonNullable<PartnerReply['postReplySource']>): Promise<void>;
|
|
114
115
|
/** Called only after delivery succeeds; autonomous notices never carry this provenance. */
|
|
115
116
|
replyDelivered(source: NonNullable<PartnerReply['postReplySource']>): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-runtime.d.ts","sourceRoot":"","sources":["../src/agent-runtime.ts"],"names":[],"mappings":"
|
|
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,3 +1,7 @@
|
|
|
1
|
+
import { isActivitySilenceRequested } from './character/activity-tool.js';
|
|
2
|
+
import { installTaskPreamble } from './execution/task-preamble.js';
|
|
3
|
+
import { AsyncSemaphore } from './core/semaphore.js';
|
|
4
|
+
import { automaticMemoryAllowed } from './memory-admission.js';
|
|
1
5
|
import { personaReferenceRequested, renderPersonaReference } from './character/identity-guide.js';
|
|
2
6
|
import { activityPolicyCurrent, postReplyAllowed, postReplyCurrent, postReplyProbability, POST_REPLY_PROMPT } from './character/post-reply.js';
|
|
3
7
|
import { PARTNER_TOOL_ENTRIES } from './collaboration/tool-discovery.js';
|
|
@@ -72,6 +76,7 @@ export class PartnerAgentRuntime {
|
|
|
72
76
|
latestPostReplyInput = new Map();
|
|
73
77
|
handles = new Map();
|
|
74
78
|
queues = new Map();
|
|
79
|
+
replySlots = new AsyncSemaphore(6);
|
|
75
80
|
heartbeatQueues = new Map();
|
|
76
81
|
taskQueues = new Map();
|
|
77
82
|
steeringQueues = new Map();
|
|
@@ -177,8 +182,8 @@ export class PartnerAgentRuntime {
|
|
|
177
182
|
this.questionAnswerer = answerer;
|
|
178
183
|
}
|
|
179
184
|
/** Cancel pending/claimed continuations as soon as another real input is admitted. */
|
|
180
|
-
async cancelPostReply(companionId, keepUserMessageId) {
|
|
181
|
-
const needsCancel = (r) => r.companionId === companionId && r.postReplyOpportunity
|
|
185
|
+
async cancelPostReply(companionId, keepUserMessageId, sessionId) {
|
|
186
|
+
const needsCancel = (r) => r.companionId === companionId && (!sessionId || r.sessionId === sessionId) && r.postReplyOpportunity
|
|
182
187
|
&& ['pending', 'claimed'].includes(r.postReplyOpportunity.status) && r.postReplyOpportunity.userMessageId !== keepUserMessageId;
|
|
183
188
|
if (!this.store.snapshot().sessions.some(needsCancel))
|
|
184
189
|
return;
|
|
@@ -202,21 +207,21 @@ export class PartnerAgentRuntime {
|
|
|
202
207
|
const companion = state.companions.find(c => c.id === route?.companionId);
|
|
203
208
|
if (!route || !companion || !postReplyAllowed(state, companion, route) || this.closed || this.isArchived(route) || this.store.isCompanionRemoving(companion.id))
|
|
204
209
|
return;
|
|
205
|
-
const key =
|
|
210
|
+
const key = route.sessionId;
|
|
206
211
|
if (this.queues.has(key) || this.steeringQueues.has(key))
|
|
207
212
|
return;
|
|
208
213
|
const agent = await this.ensureAgent(companion, route);
|
|
209
|
-
if (agent.status !== 'idle' || (this.latestPostReplyInput.has(
|
|
214
|
+
if (agent.status !== 'idle' || (this.latestPostReplyInput.has(route.sessionId) && this.latestPostReplyInput.get(route.sessionId) !== source.userMessageId) || lastIdleContactUser(agent.session.snapshotEvents())?.id !== source.userMessageId)
|
|
210
215
|
return;
|
|
211
216
|
await this.store.update(draft => {
|
|
212
217
|
const current = draft.companions.find(c => c.id === companion.id), target = draft.sessions.find(r => r.id === route.id);
|
|
213
218
|
if (!current || !target || target.sessionId !== source.sessionId || !postReplyAllowed(draft, current, target)
|
|
214
219
|
|| target.postReplyOpportunity?.userMessageId === source.userMessageId || agent.status !== 'idle'
|
|
215
|
-
|| this.queues.has(key) || this.steeringQueues.has(key) || (this.latestPostReplyInput.has(
|
|
220
|
+
|| this.queues.has(key) || this.steeringQueues.has(key) || (this.latestPostReplyInput.has(route.sessionId) && this.latestPostReplyInput.get(route.sessionId) !== source.userMessageId) || lastIdleContactUser(agent.session.snapshotEvents())?.id !== source.userMessageId)
|
|
216
221
|
return;
|
|
217
|
-
//
|
|
222
|
+
// Shared history has one opportunity; unrelated conversations remain independent.
|
|
218
223
|
for (const other of draft.sessions)
|
|
219
|
-
if (other.
|
|
224
|
+
if (other.sessionId === route.sessionId && other.postReplyOpportunity?.status === 'pending')
|
|
220
225
|
other.postReplyOpportunity.status = 'cancelled';
|
|
221
226
|
const now = Date.now(), probability = postReplyProbability(current);
|
|
222
227
|
target.postReplyOpportunity = { userMessageId: source.userMessageId, sessionId: source.sessionId, policyRevision: current.updatedAt,
|
|
@@ -227,16 +232,17 @@ export class PartnerAgentRuntime {
|
|
|
227
232
|
/** Use the existing timer and real conversation; never impersonate user input. */
|
|
228
233
|
async idleContact(companionId, timeZone = 'Asia/Shanghai') {
|
|
229
234
|
const key = `companion:${companionId}`;
|
|
230
|
-
if (this.closed || this.
|
|
235
|
+
if (this.closed || this.taskQueues.has(key) || this.heartbeatQueues.has(key) || this.store.isCompanionRemoving(companionId))
|
|
231
236
|
return;
|
|
232
237
|
const state = this.store.snapshot(), companion = state.companions.find(c => c.id === companionId);
|
|
233
|
-
const
|
|
238
|
+
const activity = this.companionActivity(companionId);
|
|
239
|
+
const pendingRoute = state.sessions.filter(r => r.companionId === companionId && r.postReplyOpportunity?.status === 'pending' && !this.queues.has(r.sessionId) && !this.steeringQueues.has(r.sessionId) && !activity.some(a => a.sessionId === r.sessionId)).sort((a, b) => a.postReplyOpportunity.dueAt - b.postReplyOpportunity.dueAt)[0];
|
|
234
240
|
let route = pendingRoute ?? state.sessions.find(r => r.id === companion?.idleContact?.routeId && r.companionId === companionId);
|
|
235
241
|
const continuation = !!pendingRoute;
|
|
236
242
|
if (pendingRoute && companion) {
|
|
237
243
|
const pending = pendingRoute.postReplyOpportunity;
|
|
238
244
|
if (!postReplyCurrent(state, companion, pendingRoute) || Date.now() > pending.expiresAt) {
|
|
239
|
-
await this.cancelPostReply(companionId);
|
|
245
|
+
await this.cancelPostReply(companionId, undefined, route?.sessionId);
|
|
240
246
|
return;
|
|
241
247
|
}
|
|
242
248
|
if (Date.now() < pending.dueAt)
|
|
@@ -248,7 +254,7 @@ export class PartnerAgentRuntime {
|
|
|
248
254
|
if (channel?.platform === 'qq' && route.qqSessionMode !== (qqSettings(channel.qq).mainSessionPeers.includes(qqSessionTarget(route.userId)) ? 'main' : 'separate'))
|
|
249
255
|
route = await this.ensureSession(companion, route.channelId, route.userId);
|
|
250
256
|
const agent = await this.ensureAgent(companion, route);
|
|
251
|
-
if (agent.status !== 'idle' || this.companionActivity(companionId).some(a => a.
|
|
257
|
+
if (agent.status !== 'idle' || this.companionActivity(companionId).some(a => a.sessionId === route.sessionId) || this.queues.has(route.sessionId) || this.steeringQueues.has(route.sessionId) || this.taskQueues.has(key))
|
|
252
258
|
return;
|
|
253
259
|
const user = lastIdleContactUser(agent.session.snapshotEvents());
|
|
254
260
|
const completed = [...agent.session.snapshotEvents()].reverse().find(e => e.type === 'turn/end');
|
|
@@ -256,7 +262,7 @@ export class PartnerAgentRuntime {
|
|
|
256
262
|
user.at = Math.max(user.at, completed.time);
|
|
257
263
|
if (continuation) {
|
|
258
264
|
if (user?.id !== route.postReplyOpportunity?.userMessageId) {
|
|
259
|
-
await this.cancelPostReply(companionId);
|
|
265
|
+
await this.cancelPostReply(companionId, undefined, route?.sessionId);
|
|
260
266
|
return;
|
|
261
267
|
}
|
|
262
268
|
}
|
|
@@ -282,7 +288,7 @@ export class PartnerAgentRuntime {
|
|
|
282
288
|
claimed = true;
|
|
283
289
|
});
|
|
284
290
|
// Claim before generation survives restart. Don't replay uncertain sends or steal active turns.
|
|
285
|
-
if (!claimed || this.closed || this.store.isCompanionRemoving(companionId) || agent.status !== 'idle' || agent.session.seq !== stableSeq || this.queues.has(
|
|
291
|
+
if (!claimed || this.closed || this.store.isCompanionRemoving(companionId) || agent.status !== 'idle' || agent.session.seq !== stableSeq || this.queues.has(route.sessionId) || this.taskQueues.has(key))
|
|
286
292
|
return;
|
|
287
293
|
const currentState = this.store.snapshot(), current = currentState.companions.find(c => c.id === companionId), currentRoute = currentState.sessions.find(r => r.id === route.id);
|
|
288
294
|
if (!current || !currentRoute || !activityPolicyCurrent(currentState, current, currentRoute) || isSleeping(current) || current.updatedAt !== companion.updatedAt || this.isArchived(route))
|
|
@@ -298,16 +304,25 @@ export class PartnerAgentRuntime {
|
|
|
298
304
|
if (channel?.platform === 'qq' && route.qqSessionMode !== (qqSettings(channel.qq).mainSessionPeers.includes(qqSessionTarget(route.userId)) ? 'main' : 'separate'))
|
|
299
305
|
return false;
|
|
300
306
|
const agent = await this.ensureAgent(companion, route);
|
|
301
|
-
|
|
307
|
+
const events = agent.session.snapshotEvents();
|
|
308
|
+
const noticeIndex = events.findIndex(e => e.type === 'user/message' && e.data.id === origin.messageId);
|
|
309
|
+
if (noticeIndex >= 0) {
|
|
310
|
+
const tail = events.slice(noticeIndex + 1);
|
|
311
|
+
const boundary = tail.findIndex(e => e.type === 'turn/start' || e.type === 'turn/end' || e.type === 'user/message' && e.data.source.kind === 'user');
|
|
312
|
+
if (tail.slice(0, boundary < 0 ? tail.length : boundary).some(isActivitySilenceRequested))
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
return lastIdleContactUser(events)?.id === origin.userMessageId;
|
|
302
316
|
}
|
|
303
317
|
async reply(companion, channelId, userId, message, progress) {
|
|
304
|
-
const
|
|
305
|
-
|
|
318
|
+
const route = await this.ensureSession(companion, channelId, userId, message.sessionTitle);
|
|
319
|
+
const key = route.sessionId;
|
|
320
|
+
await this.cancelPostReply(companion.id, undefined, route.sessionId);
|
|
306
321
|
const queuedAt = Date.now();
|
|
307
322
|
const previous = this.queues.get(key) ?? Promise.resolve();
|
|
308
323
|
let output = { text: '', attachments: [] };
|
|
309
324
|
reportProgress(progress, 'queued');
|
|
310
|
-
const current = previous.catch(() => { }).then(async () => { output = await this.drive(companion, channelId, userId, message, progress, queuedAt); });
|
|
325
|
+
const current = previous.catch(() => { }).then(async () => { output = await this.replySlots.use(() => this.drive(companion, channelId, userId, message, progress, queuedAt, route)); });
|
|
311
326
|
this.queues.set(key, current);
|
|
312
327
|
try {
|
|
313
328
|
await current;
|
|
@@ -319,8 +334,9 @@ export class PartnerAgentRuntime {
|
|
|
319
334
|
}
|
|
320
335
|
}
|
|
321
336
|
async steer(companion, channelId, userId, message) {
|
|
322
|
-
await this.
|
|
323
|
-
const key =
|
|
337
|
+
const route = await this.ensureSession(companion, channelId, userId, message.sessionTitle);
|
|
338
|
+
const key = route.sessionId;
|
|
339
|
+
await this.cancelPostReply(companion.id, undefined, route.sessionId);
|
|
324
340
|
const previous = this.steeringQueues.get(key) ?? Promise.resolve();
|
|
325
341
|
let accepted = false;
|
|
326
342
|
const current = previous.catch(() => { }).then(async () => { accepted = await this.deliverSteering(companion, channelId, userId, message); });
|
|
@@ -722,13 +738,13 @@ export class PartnerAgentRuntime {
|
|
|
722
738
|
}
|
|
723
739
|
async runHeartbeatWhenIdle(key, companion, route, concerns) {
|
|
724
740
|
for (;;) {
|
|
725
|
-
await this.queues.get(
|
|
741
|
+
await this.queues.get(route.sessionId)?.catch(() => { });
|
|
726
742
|
const conversation = await this.ensureAgent(companion, route);
|
|
727
743
|
if (conversation.status !== 'idle')
|
|
728
744
|
await conversation.whenIdle();
|
|
729
745
|
const stableSeq = conversation.session.seq;
|
|
730
746
|
await delay(2_000);
|
|
731
|
-
if (this.queues.has(
|
|
747
|
+
if (this.queues.has(route.sessionId))
|
|
732
748
|
continue;
|
|
733
749
|
if (conversation.status !== 'idle' || conversation.session.seq !== stableSeq)
|
|
734
750
|
continue;
|
|
@@ -744,8 +760,8 @@ export class PartnerAgentRuntime {
|
|
|
744
760
|
if (event.type === 'user/message' && event.data.source.kind === 'user') {
|
|
745
761
|
const owner = this.store.snapshot().sessions.find(r => r.sessionId === session.id)?.companionId;
|
|
746
762
|
if (owner) {
|
|
747
|
-
this.latestPostReplyInput.set(
|
|
748
|
-
await this.cancelPostReply(owner, event.data.id);
|
|
763
|
+
this.latestPostReplyInput.set(session.id, event.data.id);
|
|
764
|
+
await this.cancelPostReply(owner, event.data.id, session.id);
|
|
749
765
|
}
|
|
750
766
|
return;
|
|
751
767
|
}
|
|
@@ -787,7 +803,7 @@ export class PartnerAgentRuntime {
|
|
|
787
803
|
return;
|
|
788
804
|
const scopeId = memoryScope(route.channelId, route.userId);
|
|
789
805
|
const concernDirective = identityRelation && !identityRelation.matched ? undefined : await this.concerns?.applyUserDirective(companion.id, scopeId, userText, event.time);
|
|
790
|
-
const created = companion.automation.memory.enabled && this.reflection !== undefined ? await this.reflection.reflect(companion, {
|
|
806
|
+
const created = companion.automation.memory.enabled && automaticMemoryAllowed(session.id, events, this.store.snapshot().sessions) && this.reflection !== undefined ? await this.reflection.reflect(companion, {
|
|
791
807
|
id: `turn-${createHash('sha256').update(JSON.stringify([session.id, event.time, userText])).digest('hex').slice(0, 32)}`, companionId: companion.id, scopeId: conversationMemoryScope(route, this.store.snapshot().sessions),
|
|
792
808
|
characterEnabled: companion.characterCard?.enabled === true,
|
|
793
809
|
...(identityRelation ? { identityRelation } : {}),
|
|
@@ -1048,7 +1064,7 @@ export class PartnerAgentRuntime {
|
|
|
1048
1064
|
const companion = this.configurations.companion(companionId);
|
|
1049
1065
|
if (!companion?.automation.memory.enabled)
|
|
1050
1066
|
return;
|
|
1051
|
-
const routes =
|
|
1067
|
+
const routes = this.store.snapshot().sessions.filter(r => r.companionId === companionId && r.kind === 'local');
|
|
1052
1068
|
for (const route of routes) {
|
|
1053
1069
|
if (this.isArchived(route))
|
|
1054
1070
|
continue;
|
|
@@ -1072,6 +1088,8 @@ export class PartnerAgentRuntime {
|
|
|
1072
1088
|
replies.push(textContent(next.content));
|
|
1073
1089
|
}
|
|
1074
1090
|
const original = agent.session.snapshotEvents().find(e => e.type === 'user/message' && e.data.id === item.id);
|
|
1091
|
+
if (!original || !automaticMemoryAllowed(route.sessionId, [original], this.store.snapshot().sessions))
|
|
1092
|
+
continue;
|
|
1075
1093
|
await this.memory.enqueue({ id: 'renewal-' + createHash('sha256').update(route.sessionId + ':' + item.id).digest('hex').slice(0, 32),
|
|
1076
1094
|
companionId, scopeId: scope, sessionId: route.sessionId, at: original?.time ?? Date.now(), userMessageIds: [item.id],
|
|
1077
1095
|
user: textContent(item.content), assistant: replies.join('\n\n') || '(本条消息没有已完成的回复,仅整理用户原话,不推测处理结果。)',
|
|
@@ -1080,17 +1098,17 @@ export class PartnerAgentRuntime {
|
|
|
1080
1098
|
}
|
|
1081
1099
|
}
|
|
1082
1100
|
async renewReviewedContexts(companionId, day) {
|
|
1083
|
-
if (!this.memory || this.isCompanionBusy(companionId) || this.queues.has(
|
|
1101
|
+
if (!this.memory || this.isCompanionBusy(companionId) || this.store.snapshot().sessions.some(r => r.companionId === companionId && this.queues.has(r.sessionId)))
|
|
1084
1102
|
return 0;
|
|
1085
1103
|
const companion = this.configurations.companion(companionId);
|
|
1086
1104
|
if (!companion || companion.automation.memory.contextRenewalEnabled === false)
|
|
1087
1105
|
return 0;
|
|
1088
|
-
const learning = companion.automation.memory.enabled;
|
|
1089
1106
|
let renewed = 0;
|
|
1090
1107
|
const routes = [...new Map(this.store.snapshot().sessions.filter(r => r.companionId === companionId).map(r => [r.sessionId, r])).values()];
|
|
1091
1108
|
for (const route of routes) {
|
|
1092
1109
|
if (this.ctx.workspaceRegistry.archivedSessionIds.includes(route.sessionId))
|
|
1093
1110
|
continue;
|
|
1111
|
+
const learning = companion.automation.memory.enabled && this.store.snapshot().sessions.some(r => r.kind === 'local' && r.sessionId === route.sessionId && r.companionId === companionId);
|
|
1094
1112
|
const scope = conversationMemoryScope(route, this.store.snapshot().sessions);
|
|
1095
1113
|
const agent = await this.ensureAgent(companion, route);
|
|
1096
1114
|
if (agent.session.surface.nodes.length <= 1) {
|
|
@@ -1109,7 +1127,7 @@ export class PartnerAgentRuntime {
|
|
|
1109
1127
|
return false;
|
|
1110
1128
|
if (!learning)
|
|
1111
1129
|
return true;
|
|
1112
|
-
return this.memory.coversContext(companionId, scope, agent.session.deriveMessages().filter(m => m.role === 'user' && m.source.kind === 'user').map(m => ({ id: m.id, text: textContent(m.content) })));
|
|
1130
|
+
return this.memory.coversContext(companionId, scope, agent.session.deriveMessages().filter(m => m.role === 'user' && m.source.kind === 'user' && automaticMemoryAllowed(route.sessionId, agent.session.snapshotEvents().filter(e => e.type === 'user/message' && e.data.id === m.id), this.store.snapshot().sessions)).map(m => ({ id: m.id, text: textContent(m.content) })));
|
|
1113
1131
|
}, session => this.ctx.sessions.flush(session), learning);
|
|
1114
1132
|
if (removed > 0 || agent.session.deriveMessages().some(m => m.role === 'user' && m.source.kind === 'plugin:@lemoncat7/dsh-partner' && m.source.form === 'notice' && m.source.summary === `上下文焕新 ${day}`)) {
|
|
1115
1133
|
await this.memory.markRenewed(companionId, route.sessionId, day, removed);
|
|
@@ -1194,11 +1212,13 @@ export class PartnerAgentRuntime {
|
|
|
1194
1212
|
restored?.dispose();
|
|
1195
1213
|
this.restoredCompositions.delete(sessionId);
|
|
1196
1214
|
}
|
|
1197
|
-
async drive(companion, channelId, userId, message, progress, queuedAt = Date.now()) {
|
|
1215
|
+
async drive(companion, channelId, userId, message, progress, queuedAt = Date.now(), preparedRoute) {
|
|
1198
1216
|
reportProgress(progress, 'working');
|
|
1199
1217
|
const enteredAt = Date.now();
|
|
1200
1218
|
let submittedAt = enteredAt, modelEndedAt = enteredAt, lastTurnEnd;
|
|
1201
|
-
const session = await this.ensureSession(companion, channelId, userId, message.sessionTitle);
|
|
1219
|
+
const session = preparedRoute ?? await this.ensureSession(companion, channelId, userId, message.sessionTitle);
|
|
1220
|
+
if (!this.store.snapshot().sessions.some(r => r.id === session.id && r.sessionId === session.sessionId))
|
|
1221
|
+
throw new Error('会话配置已变化,请重新发送消息');
|
|
1202
1222
|
const agent = await this.ensureAgent(companion, session);
|
|
1203
1223
|
if (agent.status !== 'idle')
|
|
1204
1224
|
await agent.whenIdle();
|
|
@@ -1220,7 +1240,7 @@ export class PartnerAgentRuntime {
|
|
|
1220
1240
|
if (event.type === 'turn/start' || event.type === 'turn/end' || (isInternalTaskNotice(event) && !isRequirementDeliveryReceipt(event)))
|
|
1221
1241
|
directProgress = false;
|
|
1222
1242
|
if (event.type === 'user/message' && event.data.source.kind === 'user')
|
|
1223
|
-
directProgress =
|
|
1243
|
+
directProgress = questionOrigin(agent.session.id, agent.session.snapshotEvents(), this.store.snapshot().sessions)?.id === session.id;
|
|
1224
1244
|
const stage = replyStage(event);
|
|
1225
1245
|
if (stage)
|
|
1226
1246
|
reportProgress(progress, stage);
|
|
@@ -1230,6 +1250,7 @@ export class PartnerAgentRuntime {
|
|
|
1230
1250
|
reportProgress(progress, activity);
|
|
1231
1251
|
}
|
|
1232
1252
|
});
|
|
1253
|
+
const detachPreamble = progress?.acknowledge ? installTaskPreamble(agent, progress.acknowledge, () => questionOrigin(agent.session.id, agent.session.snapshotEvents(), this.store.snapshot().sessions)?.id === session.id) : undefined;
|
|
1233
1254
|
try {
|
|
1234
1255
|
const userMessage = createUserMessage({
|
|
1235
1256
|
content: inbound.content,
|
|
@@ -1243,6 +1264,7 @@ export class PartnerAgentRuntime {
|
|
|
1243
1264
|
modelEndedAt = Date.now();
|
|
1244
1265
|
}
|
|
1245
1266
|
finally {
|
|
1267
|
+
detachPreamble?.();
|
|
1246
1268
|
detach();
|
|
1247
1269
|
}
|
|
1248
1270
|
const response = channelReplyPartsAfter(agent.session.snapshotEvents(), startSeq);
|
|
@@ -1314,7 +1336,7 @@ export class PartnerAgentRuntime {
|
|
|
1314
1336
|
return existing;
|
|
1315
1337
|
const sharedScope = conversationMemoryScope(primary, [primary]);
|
|
1316
1338
|
const sources = isQq ? snapshot.sessions.filter(r => r.channelId === channelId && r.companionId === companion.id && qqSessionTarget(r.userId) === target && r.memoryScopeId !== sharedScope).map(r => memoryScope(r.channelId, r.userId)) : [];
|
|
1317
|
-
if (existing?.memoryScopeId !== sharedScope || sources.length)
|
|
1339
|
+
if ((!isQq || !target.startsWith('group:') && qqSettings(channel?.qq).mainSessionPeers.includes(target)) && (existing?.memoryScopeId !== sharedScope || sources.length))
|
|
1318
1340
|
await this.memory?.mergeScopes(companion.id, sharedScope, [...new Set([...sources, memoryScope(channelId, userId)])], recoverLeases);
|
|
1319
1341
|
let result;
|
|
1320
1342
|
await this.store.update(state => {
|