fix: /compact 改走宿主命令通道(commands.execute),绕开隔离作用域

根因:compaction-basic 挂载在隔离作用域,插件 ctx 的 ctx.get('compaction')
解析不到(实测 undefined;agent.ctx 亦不可达)。但宿主命令运行时的按 agent
作用层已注册 /compact(command-compact,Web 端同款通道),
commands.find(agent,'compact') 可命中。

因此 /compact 改为 ctx.commands.execute(agent,'/compact',[],signal) 派发:
- 与 Web 端 /compact 完全同语义(busy/错误映射、command/run 生命周期日志)
- 结果文本按 command-compact 固定文案翻译为中文回复;无法识别时回退原文详情
- 忙会话提前提示(busySessions/phase 预检);异步执行不阻塞轮询;同会话去重
- 测试改 mock commands.find/execute(51 全绿)
This commit is contained in:
RRRosmontis
2026-09-05 17:34:37 +08:00
parent d712fb3494
commit 8600835436
6 changed files with 160 additions and 132 deletions
+23 -12
View File
@@ -912,11 +912,11 @@ test('/compact compacts bound session and reports result', async () => {
roots: () => [agent],
get: (id: ReturnType<typeof SessionId>) => (String(id) === sessionId ? agent : undefined),
},
compaction: {
compactNow: async () => ({
shadowedSeqs: [1, 2, 3, 4],
shadowedTokenCount: 12_345,
summarySeq: 9,
commands: {
find: () => true,
execute: async () => ({
commandId: 'cmd-1',
result: { kind: 'success', text: 'Compacted 4 history items (~12345 tokens).' },
}),
},
on() { return () => {} },
@@ -962,10 +962,15 @@ test('/compact busy agent replies COMPACT_BUSY without starting', async () => {
roots: () => [agent],
get: (id: ReturnType<typeof SessionId>) => (String(id) === sessionId ? agent : undefined),
},
compaction: {
compactNow: () => {
throw Object.assign(new Error('busy'), { code: 'busy' })
},
commands: {
find: () => true,
execute: async () => ({
commandId: 'cmd-busy',
result: {
kind: 'error',
text: 'Compaction is unavailable because this process has an active compaction, or the agent is not idle.',
},
}),
},
on() { return () => {} },
}
@@ -987,11 +992,14 @@ test('/compact busy agent replies COMPACT_BUSY without starting', async () => {
})
sent.length = 0 // drop the BOUND message; keep only /compact replies
await bridge.processUpdate(messageUpdate(10, 1, '/compact', 2))
assert.equal(sent.length, 1)
assert.equal(sent[0]!.text, MSG.COMPACT_BUSY)
assert.ok(sent.some((m) => m.text.includes('已开始压缩')), 'ack sent')
for (let i = 0; i < 100 && !sent.some((m) => m.text.includes(MSG.COMPACT_BUSY)); i++) {
await new Promise((r) => setTimeout(r, 5))
}
assert.ok(sent.some((m) => m.text === MSG.COMPACT_BUSY), 'busy notice arrives')
})
test('/compact without compaction engine reports unavailable', async () => {
test('/compact without registered /compact command reports unavailable', async () => {
const sent: SentMessage[] = []
const followups: UserMessage[] = []
const sessionId = 'live-noeng'
@@ -1003,6 +1011,9 @@ test('/compact without compaction engine reports unavailable', async () => {
roots: () => [agent],
get: (id: ReturnType<typeof SessionId>) => (String(id) === sessionId ? agent : undefined),
},
commands: {
find: () => undefined, // command not registered on this host
},
on() { return () => {} },
}
const bridge = new TelegramBridge(ctx as any, {