diff --git a/apps/cli/config/agent-presets/standard/agent.cordis.yml b/apps/cli/config/agent-presets/standard/agent.cordis.yml index ec4f71a849..41d1d81a90 100644 --- a/apps/cli/config/agent-presets/standard/agent.cordis.yml +++ b/apps/cli/config/agent-presets/standard/agent.cordis.yml @@ -227,6 +227,8 @@ - id: tool-todo name: '@deepseek-ai/dsh-tool-todo' + config: + allowParallelInProgress: true # The `web` service and its search provider stay in the host composition; only # the model-facing tool is per-session. diff --git a/packages/preset/agent-presets/tests/fixtures/plugins/contribute.js b/packages/preset/agent-presets/tests/fixtures/plugins/contribute.js index b7b67be5d6..0d03277605 100644 --- a/packages/preset/agent-presets/tests/fixtures/plugins/contribute.js +++ b/packages/preset/agent-presets/tests/fixtures/plugins/contribute.js @@ -17,4 +17,9 @@ export function apply(ctx, config) { order: 10, text: `section for ${config.tool}`, })) + // Reconfiguring a live row runs the Loader's `internal/update` waterfall, + // which persists the owning tree. That is the trigger reaching the preset + // tree's `write` while the subtree is still mounted; tearing the agent down + // instead stops earlier, in the loader's own "tree is being disposed" case. + globalThis.__RECONFIGURE__ = tool => ctx.fiber.update({ ...config, tool }) } diff --git a/packages/preset/agent-presets/tests/mount.spec.ts b/packages/preset/agent-presets/tests/mount.spec.ts index 93b77d0047..fb3bfb6d8a 100644 --- a/packages/preset/agent-presets/tests/mount.spec.ts +++ b/packages/preset/agent-presets/tests/mount.spec.ts @@ -219,22 +219,25 @@ describe('a roster with nothing in it', () => { describe('attributing a service to a subtree', () => { it('never writes the preset file back, however the subtree changes', async () => { + delete (globalThis as { __RECONFIGURE__?: unknown }).__RECONFIGURE__ const handle = await ctx.agents.create({ sessionId: SessionId('sess-write'), setup: async (agentCtx: Context) => void await ctx.agentPresets.mount(agentCtx, 'standard'), }) - const [mount] = livePresetMounts().filter(entry => entry.presetId === 'standard') - expect(mount).toBeDefined() const file = join(FIXTURES, 'system', 'standard', 'agent.cordis.yml') const before = await readFile(file, 'utf8') - // The inherited `write()` persists the tree whenever the Loader thinks the - // config moved, and disposing an agent disposes its whole subtree — which - // is enough to trigger it. Inheriting that truncates the shipped preset to - // `[]` the first time a session ends. - await handle.dispose() + // The inherited `write()` persists the whole tree whenever the Loader + // decides a row's config moved, so one row reconfiguring itself would + // rewrite the shipped composition — here, with the row's new tool name. + const reconfigure = (globalThis as { __RECONFIGURE__?: (tool: string) => Promise }).__RECONFIGURE__ + expect(reconfigure).toBeTypeOf('function') + await reconfigure!('rewritten') + expect(toolNames(ctx, handle.agent)).toContain('rewritten') expect(await readFile(file, 'utf8')).toBe(before) + + await handle.dispose() }) it('attributes nothing to a subtree that is already torn down', async () => {