feat: 背景工作線(work 分類、子代理派工、完成後由角色回報)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
root
2026-10-15 08:32:07 +00:00
co-authored by Claude Sonnet 5.5
parent e99e7b1536
commit 8c25728206
4 changed files with 87 additions and 14 deletions
+30 -3
View File
@@ -1,9 +1,9 @@
import { expect, mock, test } from 'claude-code/testing'
type Ctl = { complete: (e: any) => unknown; usage: unknown; seen?: any }
type Ctl = { complete: (e: any) => unknown; usage: unknown; seen?: any; submitted: string[] }
const setup = (on: any): Ctl => {
const ctl: Ctl = { complete: () => ({ isAnswered: false, reason: 'aborted' }), usage: { rateLimits: [], context: {} } }
const ctl: Ctl = { complete: () => ({ isAnswered: false, reason: 'aborted' }), usage: { rateLimits: [], context: {} }, submitted: [] }
mock.store(on)
mock.clock(on, { now: Date.parse('2026-10-15T08:00:00Z') })
on('ui.log', () => ({ value: undefined }) as never)
@@ -11,7 +11,7 @@ const setup = (on: any): Ctl => {
on('session.start', () => ({ cwd: '/tmp' }) as never)
on('command.run', () => ({ text: '' }))
on('prompt.compose', () => ({ sections: [] }))
on('prompt.submit', (_$: any, e: any) => ({ text: e.text }) as never)
on('prompt.submit', (_$: any, e: any) => (ctl.submitted.push(e.text), { text: e.text }) as never)
on('model.complete', (_$: any, e: any) => ({ value: ctl.complete(e) }) as never)
on('session.usage', () => ({ value: ctl.usage }) as never)
on('turn.step', async function* (_$: any, e: any) {
@@ -142,3 +142,30 @@ test('未成年角色維持友情,即使好感度最高且開啟戀愛式依
await $.command.run({ command: 'anima-affinity', args: '100' } as never)
expect((await personaText($)).includes('關係定位:友情')).toBe(true)
})
const workRoute = (model: string, work: boolean) => ({ isAnswered: true, usage, text: `{"difficulty":8,"model":"${model}","effort":"high","tone":"neutral","ack":"好,我來處理。","work":${work}}` })
test('需要動手的要求交給背景工作線,對話線改用 Haiku', async ($, on) => {
const ctl = setup(on)
const spawned: any[] = []
on('agent.spawn', (_$: any, e: any) => (spawned.push(e), { model: e.model, agentId: 'a1' }) as never)
ctl.complete = () => workRoute('opus', true)
await start($)
await submit($, '幫我重構 src 目錄')
expect(spawned.length).toBe(1)
expect(spawned[0].model).toBe('opus')
await step($, 'main')
expect(ctl.seen.model).toBe('haiku')
const sys = await $.prompt.compose({ model: 'm', promptModel: 'm', surfaces: [], tools: [], outputStyle: null, traits: [] } as never)
expect(sys.sections.find((s: any) => s.id === 'jsc-anima:persona')?.text).toContain('背景工作線')
})
test('work 為 false 或關閉背景工作線時不派工', async ($, on) => {
const ctl = setup(on)
let n = 0
on('agent.spawn', () => (n++, { model: 'm', agentId: 'a' }) as never)
ctl.complete = () => workRoute('sonnet', false)
await start($)
await submit($, '你好')
expect(n).toBe(0)
})