diff --git a/src/subagent.ts b/src/subagent.ts index cb1462ee7..bf1c48ce3 100644 --- a/src/subagent.ts +++ b/src/subagent.ts @@ -1702,8 +1702,13 @@ ${runtimeSnapshotContext('user', plan.entries)}` const completionPersona = `${persona} Completion protocol: call \`${resultToolName}\` exactly once with the final result matching its parameter schema. This is the only completion channel for this run. Do not finish with a plain-text answer.` - const perOpMaxTokens = operation === 'migration' || operation === 'compaction' || operation === 'document-archive' ? 8_192 - : operation === 'metadata-maintenance' ? 4_096 + // Maintenance delegates emit small structured results, but reasoning-style + // models can burn a large completion budget on analysis before emitting + // them. Undersized static caps abort the whole operation with + // stopReason=max-tokens (e.g. dense CJK capacity archives, see #70), so + // keep generous ceilings instead of tight ones. + const perOpMaxTokens = operation === 'migration' || operation === 'compaction' || operation === 'document-archive' ? 32_768 + : operation === 'metadata-maintenance' ? 16_384 : undefined const fixed = this.taskAgentModelResolver?.() const baseAgentOptions = perOpMaxTokens === undefined ? undefined : { maxTokens: perOpMaxTokens } diff --git a/tests/subagent.spec.ts b/tests/subagent.spec.ts index a0af11702..9038fce8e 100644 --- a/tests/subagent.spec.ts +++ b/tests/subagent.spec.ts @@ -815,7 +815,7 @@ describe('Mnemon memory subagent coordinator', () => { }) expect(host.start).toHaveBeenCalledWith('spawn', expect.objectContaining({ toolFilter: { allow: [expect.stringMatching(/^mnemon_subagent_result_/)] }, - agentOptions: { maxTokens: 4_096 }, + agentOptions: { maxTokens: 16_384 }, persona: expect.stringContaining('fastest bounded metadata-sampling path'), })) expect(memoryService.metadataSample).toHaveBeenCalledWith('product', expect.any(AbortSignal)) @@ -1001,7 +1001,7 @@ describe('Mnemon memory subagent coordinator', () => { }) expect(host.start).toHaveBeenCalledWith('spawn', expect.objectContaining({ toolFilter: { allow: [expect.stringMatching(/^mnemon_subagent_result_/)] }, - agentOptions: { maxTokens: 8_192 }, + agentOptions: { maxTokens: 32_768 }, })) const migrationCall = (host.start.mock.calls[0] as unknown as [string, { prompt: Array<{ text: string }>; persona: string; toolFilter: { allow: string[] } }])[1] const migrationPrompt = migrationCall.prompt[0]!.text @@ -1244,7 +1244,7 @@ describe('Mnemon memory subagent coordinator', () => { }) expect(host.start).toHaveBeenCalledWith('spawn', expect.objectContaining({ toolFilter: { allow: [expect.stringMatching(/^mnemon_subagent_result_/)] }, - agentOptions: { maxTokens: 8_192 }, + agentOptions: { maxTokens: 32_768 }, persona: expect.stringContaining('local USER.md compactor'), })) const compactionCall = (host.start.mock.calls[0] as unknown as [string, { prompt: Array<{ text: string }>; persona: string }])[1] @@ -1384,7 +1384,7 @@ describe('Mnemon memory subagent coordinator', () => { maintenance: { kind: 'local-compaction' }, }) expect(host.start).toHaveBeenCalledWith('spawn', expect.objectContaining({ - agentOptions: { provider: 'pinned-provider', model: 'pinned-model', maxTokens: 8_192 }, + agentOptions: { provider: 'pinned-provider', model: 'pinned-model', maxTokens: 32_768 }, })) }) })