From 2886c6391bd7752d403a08d8d083fb5f3d094410 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Wed, 5 Aug 2026 16:40:03 +0800 Subject: [PATCH] feat(agent-presets): rename the two-tool preset to minimal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `core-web` said neither of the things that matter about it. The `-web` suffix is a leftover from the whole-process `core-web.cordis.yml` overlay, and presets are per-session and not web-specific. `core` reads as "the foundational one" when it is in fact the one with the fewest capabilities. `minimal` says what it is and orders the shipped set legibly by capability: minimal, standard, cordis. Breaking: a session created under `core-web` records that id in its header and will fail to resolve it on resume. Nothing outside this repository has shipped, so no migration path is offered. The identically named `config/core-web.cordis.yml` — the legacy whole-process overlay behind the web snapshot test — is a different thing and keeps its name. --- ...-08-03-per-session-agent-presets.i18n.yaml | 4 +- .../2026-08-03-per-session-agent-presets.md | 4 +- ...2026-08-03-per-session-agent-presets.zh.md | 4 +- .../{core-web => minimal}/agent.cordis.yml | 2 +- .../profile.yml => minimal/preset.yml} | 0 apps/cli/tests/web-agent-presets.spec.ts | 42 +++++++++---------- .../client/connection/src/client/fixture.ts | 2 +- .../tests/settings-store.spec.ts | 32 +++++++------- .../tests/api-proxy-agent-preset.spec.ts | 32 +++++++------- .../apiproxy/tests/api-proxy-config.spec.ts | 4 +- .../host/apiproxy/tests/fetch-carrier.spec.ts | 4 +- packages/preset/README.i18n.yaml | 4 +- packages/preset/README.md | 2 +- packages/preset/README.zh.md | 2 +- packages/preset/agent-presets/README.md | 2 +- packages/preset/agent-presets/README.zh.md | 2 +- 16 files changed, 71 insertions(+), 71 deletions(-) rename apps/cli/config/agent-presets/{core-web => minimal}/agent.cordis.yml (95%) rename apps/cli/config/agent-presets/{core-web/profile.yml => minimal/preset.yml} (100%) diff --git a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml index 229fffae78..6ca4bc24cd 100644 --- a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md -2026-08-03-per-session-agent-presets.md: cc40af246d018560b7bc7bea92286e0ddaf53e55 -2026-08-03-per-session-agent-presets.zh.md: 5b6113ee9f905478e59db3e982331b8faa190e3c +2026-08-03-per-session-agent-presets.md: c66503bf47b3b571c6c31673b2d975571a3af93d +2026-08-03-per-session-agent-presets.zh.md: 21f021c6bbaa11c33bc93104aba0f6e8ce2f1448 diff --git a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md index bddecff516..25099b48c5 100644 --- a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md +++ b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md @@ -6,7 +6,7 @@ English | [中文](2026-08-03-per-session-agent-presets.zh.md) ## Problem -One `dsh` process serves many sessions, but the composition that decides what an agent *is* — its tools, persona, prompt sections, delegation backends — is fixed for the whole process by the `cordis.yml` the launcher booted. A deployment that wants a benchmark-minimal agent beside a full coding agent has to run two processes, and the shipped workaround (`apps/cli/config/core-web.cordis.yml`, a `--config` overlay that disables tool rows) changes every session at once. +One `dsh` process serves many sessions, but the composition that decides what an agent *is* — its tools, persona, prompt sections, delegation backends — is fixed for the whole process by the `cordis.yml` the launcher booted. A deployment that wants a benchmark-minimal agent beside a full coding agent has to run two processes, and the shipped workaround (`apps/cli/config/minimal.cordis.yml`, a `--config` overlay that disables tool rows) changes every session at once. The obvious reading of "let a session pick its composition" is that the loader needs a new tier. It does not. [`dsh-tools`](../../../../packages/core/tools/README.md) and [`dsh-system-prompt`](../../../../packages/core/system-prompt/README.md) already file registrations into the calling context's scope layer, and [the agent is a registration scope](2026-07-08-agent-scope-contexts.md). What was missing is a way to point a whole `cordis.yml` at one agent's scope. @@ -23,7 +23,7 @@ Composition splits into two planes, decided by what must be shared rather than b Model routing stays out of presets. `installAgentLlmTarget` is already the per-agent seam for provider, model, and reasoning effort, and an LLM adapter mounted inside a preset would never be resolved by `agent-loop`, which lives in the host plane. -The deployment ships three presets — `standard` (the full coding agent), `core-web` (a two-tool benchmark surface), and `cordis` (the standard agent plus the self-referential toolset and a composition-authoring skill). +The deployment ships three presets — `standard` (the full coding agent), `minimal` (a two-tool benchmark surface), and `cordis` (the standard agent plus the self-referential toolset and a composition-authoring skill). Mounting is per-session by default. Measured cost for a twelve-row composition is ~3ms and ~600KB per session, so isolation is the cheaper default than any sharing scheme, and a preset authored by a user or by an agent then has the smallest possible blast radius. A preset that genuinely owns an expensive singleton opts into sharing with Cordis's own `isolate` vocabulary: a named realm label is process-global, so two subtrees naming the same label resolve one instance. diff --git a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md index 5d926fffb4..07a518d3bf 100644 --- a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md @@ -6,7 +6,7 @@ Status: implemented ## 问题 -一个 `dsh` 进程服务多个会话,但决定 agent(智能体)究竟是什么的那套组装——它的工具、人设、提示词段落、委派后端——由启动器所引导的 `cordis.yml` 一次性固定给整个进程。若某个部署希望一个 benchmark 精简 agent 与一个完整编码 agent 并存,就必须跑两个进程;而现有的变通方案(`apps/cli/config/core-web.cordis.yml`,一个用来禁用工具行的 `--config` 覆盖层)会一次性改变所有会话。 +一个 `dsh` 进程服务多个会话,但决定 agent(智能体)究竟是什么的那套组装——它的工具、人设、提示词段落、委派后端——由启动器所引导的 `cordis.yml` 一次性固定给整个进程。若某个部署希望一个 benchmark 精简 agent 与一个完整编码 agent 并存,就必须跑两个进程;而现有的变通方案(`apps/cli/config/minimal.cordis.yml`,一个用来禁用工具行的 `--config` 覆盖层)会一次性改变所有会话。 对"让会话自选组装"最直觉的理解,是 loader 需要新增一层。其实不需要。[`dsh-tools`](../../../../packages/core/tools/README.md) 与 [`dsh-system-prompt`](../../../../packages/core/system-prompt/README.md) 本就按调用方上下文的 scope 分层归档注册,而且 [agent 本身就是一个注册 scope](2026-07-08-agent-scope-contexts.md)。此前缺的只是一种把整份 `cordis.yml` 指向某一个 agent scope 的办法。 @@ -23,7 +23,7 @@ Status: implemented 模型路由不进 preset。`installAgentLlmTarget` 已经是 provider、model 与 reasoning effort 的按 agent 可替换点;而挂在 preset 内部的 LLM 适配器永远不会被 `agent-loop` 解析到,因为后者位于宿主平面。 -部署随附三个 preset —— `standard`(完整编码 agent)、`core-web`(两个工具的 benchmark 表层)与 `cordis`(标准 agent 加上自指工具集与一份组装创作 skill)。 +部署随附三个 preset —— `standard`(完整编码 agent)、`minimal`(两个工具的 benchmark 表层)与 `cordis`(标准 agent 加上自指工具集与一份组装创作 skill)。 挂载默认按会话进行。实测一份十二行组装每会话约 3ms、约 600KB,因此隔离比任何共享方案都更划算;而由用户或 agent 写出的 preset 也因此拥有尽可能小的影响面。确实自带昂贵单例的 preset,可以用 Cordis 自身的 `isolate` 词汇显式选择共享:命名 realm 的 label 是进程级全局的,因此两棵子树只要写同一个 label 就解析到同一个实例。 diff --git a/apps/cli/config/agent-presets/core-web/agent.cordis.yml b/apps/cli/config/agent-presets/minimal/agent.cordis.yml similarity index 95% rename from apps/cli/config/agent-presets/core-web/agent.cordis.yml rename to apps/cli/config/agent-presets/minimal/agent.cordis.yml index 9a251461c5..6302a3cf14 100644 --- a/apps/cli/config/agent-presets/core-web/agent.cordis.yml +++ b/apps/cli/config/agent-presets/minimal/agent.cordis.yml @@ -1,4 +1,4 @@ -# The `core-web` agent preset: the two-tool benchmark surface. +# The `minimal` agent preset: the two-tool benchmark surface. # # The native model surface is exactly persistent `bash` plus # `str_replace_editor`. Everything else a session could reach — skills, goals, diff --git a/apps/cli/config/agent-presets/core-web/profile.yml b/apps/cli/config/agent-presets/minimal/preset.yml similarity index 100% rename from apps/cli/config/agent-presets/core-web/profile.yml rename to apps/cli/config/agent-presets/minimal/preset.yml diff --git a/apps/cli/tests/web-agent-presets.spec.ts b/apps/cli/tests/web-agent-presets.spec.ts index 5a9ba88d35..38cb27ca2e 100644 --- a/apps/cli/tests/web-agent-presets.spec.ts +++ b/apps/cli/tests/web-agent-presets.spec.ts @@ -82,7 +82,7 @@ describe('the shipped Web composition', () => { it('supplies both shipped presets, and only those, from the system root', async () => { const listed = await ctx.agentPresets.list() - expect(listed.map(preset => preset.id).sort()).toEqual(['cordis', 'core-web', 'standard']) + expect(listed.map(preset => preset.id).sort()).toEqual(['cordis', 'minimal', 'standard']) expect(listed.every(preset => preset.trust === 'system')).toBe(true) expect(ctx.agentPresets.defaultId).toBe('standard') }) @@ -110,10 +110,10 @@ describe('the shipped Web composition', () => { } }) - it('composes exactly two tools from `core-web`', async () => { + it('composes exactly two tools from `minimal`', async () => { const handle = await ctx.agents.create({ - sessionId: SessionId('preset-core-web'), - setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined), + sessionId: SessionId('preset-minimal'), + setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'minimal').then(() => undefined), }) try { // Exactly what the preset lists — nothing arrives from the host. @@ -130,7 +130,7 @@ describe('the shipped Web composition', () => { }) const minimal = await ctx.agents.create({ sessionId: SessionId('preset-both-minimal'), - setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined), + setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'minimal').then(() => undefined), }) try { expect(toolNames(ctx, minimal.agent)).toEqual(['bash', 'str_replace_editor']) @@ -217,7 +217,7 @@ describe('the shipped Web composition', () => { it('gives each session its own persona', async () => { const handle = await ctx.agents.create({ sessionId: SessionId('preset-persona'), - setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined), + setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'minimal').then(() => undefined), }) try { const assembly = await ctx.systemPrompt.assemble({ scope: handle.agent }) @@ -238,12 +238,12 @@ describe('a switch survives the session', () => { }) try { // The api-proxy's select does exactly this pair while the session is blank. - await ctx.agentPresets.recompose(handle.agent.ctx, 'core-web') - handle.agent.session.append('agent-preset/selected', { agentPreset: 'core-web' }) + await ctx.agentPresets.recompose(handle.agent.ctx, 'minimal') + handle.agent.session.append('agent-preset/selected', { agentPreset: 'minimal' }) // The header keeps the creation fact; the log carries what it runs. expect(handle.agent.session.header.agentPreset).toBe('standard') - expect(resolveSessionPreset(handle.agent.session)).toBe('core-web') + expect(resolveSessionPreset(handle.agent.session)).toBe('minimal') } finally { await handle.dispose() } @@ -255,14 +255,14 @@ describe('a switch survives the session', () => { const rebuilt = resolveSessionPreset({ header: { version: 0, id: SessionId('x'), createdAt: 0, agentPreset: 'standard' }, events: [ - { type: 'agent-preset/selected', seq: 1, time: 0, data: { agentPreset: 'core-web' } }, + { type: 'agent-preset/selected', seq: 1, time: 0, data: { agentPreset: 'minimal' } }, { type: 'turn/start', seq: 2, time: 0, data: { turn: 0, trigger: { kind: 'message', source: { kind: 'user' } } } }, ] as never, }) // Reading the header alone would compose the creation-time preset over a // history another one produced — the replay the blank-only lock prevents. - expect(rebuilt).toBe('core-web') + expect(rebuilt).toBe('minimal') }) }) @@ -270,8 +270,8 @@ describe('a forked session', () => { it('inherits the composition its seeded history was produced under', async () => { const parent = await ctx.agents.create({ sessionId: SessionId('preset-fork-parent'), - meta: { agentPreset: 'core-web' }, - setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined), + meta: { agentPreset: 'minimal' }, + setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'minimal').then(() => undefined), }) const inherited = resolveSessionPreset(parent.agent.session) const child = await ctx.agents.create({ @@ -335,7 +335,7 @@ describe('authoring a preset on the shipped composition', () => { }) it('writes a preset a session then really composes from', async () => { - const copied = await authorCtx.agentPresets.read('core-web') + const copied = await authorCtx.agentPresets.read('minimal') await authorCtx.agentPresets.write('my-agent', copied) @@ -352,7 +352,7 @@ describe('authoring a preset on the shipped composition', () => { setup: agentCtx => authorCtx.agentPresets.mount(agentCtx, 'my-agent').then(() => undefined), }) try { - // The same tools the shipped `core-web` composes, from a file written + // The same tools the shipped `minimal` composes, from a file written // through the service into a root outside the installed harness. expect(toolNames(authorCtx, handle.agent)).toEqual(['bash', 'str_replace_editor']) } finally { @@ -380,9 +380,9 @@ describe('the default preset as a user setting', () => { it('composes an unnamed session from the stored default, not the composed one', async () => { expect(ctx.agentPresets.defaultId).toBe('standard') - await ctx.settings.update(settingsNamespace(SETTINGS_NAMESPACE), { default: 'core-web' }) + await ctx.settings.update(settingsNamespace(SETTINGS_NAMESPACE), { default: 'minimal' }) try { - expect(ctx.agentPresets.defaultId).toBe('core-web') + expect(ctx.agentPresets.defaultId).toBe('minimal') const handle = await ctx.agents.create({ sessionId: SessionId('preset-user-default'), @@ -410,14 +410,14 @@ describe('a session keeps the preset it was created with', () => { it('refuses to adopt a live session under a different preset', async () => { const handle = await ctx.agents.create({ sessionId: SessionId('preset-locked'), - meta: { agentPreset: 'core-web' }, - setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined), + meta: { agentPreset: 'minimal' }, + setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'minimal').then(() => undefined), }) try { // The api-proxy guard reads exactly this: the header records what the // session runs, so naming anything else is a caller error rather than a - // switch. Its history was produced under `core-web`'s two tools. - expect(handle.agent.session.header.agentPreset).toBe('core-web') + // switch. Its history was produced under `minimal`'s two tools. + expect(handle.agent.session.header.agentPreset).toBe('minimal') } finally { await handle.dispose() } diff --git a/packages/client/connection/src/client/fixture.ts b/packages/client/connection/src/client/fixture.ts index 6383e9edac..6d91d7b17d 100644 --- a/packages/client/connection/src/client/fixture.ts +++ b/packages/client/connection/src/client/fixture.ts @@ -1346,7 +1346,7 @@ export function createFixtureApi(options: FixtureOptions = {}): ApiProxy { */ const fixturePresets = new Map([ ['standard', { trust: 'system', content: "- id: tool-bash\n name: '@deepseek-ai/dsh-tool-bash'\n" }], - ['core-web', { trust: 'system', content: "- id: tool-web-search\n name: '@deepseek-ai/dsh-tool-web-search'\n" }], + ['minimal', { trust: 'system', content: "- id: tool-web-search\n name: '@deepseek-ai/dsh-tool-web-search'\n" }], ['my-agent', { trust: 'user', content: "- id: tool-read\n name: '@deepseek-ai/dsh-tool-read'\n" }], ]) let fixtureDefaultPreset = 'standard' diff --git a/packages/client/ui-agent-preset/tests/settings-store.spec.ts b/packages/client/ui-agent-preset/tests/settings-store.spec.ts index c018c5da6f..bde4a305d0 100644 --- a/packages/client/ui-agent-preset/tests/settings-store.spec.ts +++ b/packages/client/ui-agent-preset/tests/settings-store.spec.ts @@ -75,24 +75,24 @@ describe('the agent-preset settings controller', () => { const writes: Recorded[] = [] const controller = new AgentPresetSettingsController(fakeApi([ { id: 'standard', trust: 'system', isDefault: true }, - { id: 'core-web', trust: 'system', isDefault: false }, + { id: 'minimal', trust: 'system', isDefault: false }, ], { writes })) await controller.load() - await controller.select('core-web') + await controller.select('minimal') - expect(writes).toEqual([{ ns: AGENT_PRESET_SETTINGS_NS, patch: { default: 'core-web' } }]) - expect(controller.store.getSnapshot().currentValue).toBe('core-web') + expect(writes).toEqual([{ ns: AGENT_PRESET_SETTINGS_NS, patch: { default: 'minimal' } }]) + expect(controller.store.getSnapshot().currentValue).toBe('minimal') }) it('restores the previous value and surfaces the message when the write fails', async () => { const controller = new AgentPresetSettingsController(fakeApi([ { id: 'standard', trust: 'system', isDefault: true }, - { id: 'core-web', trust: 'system', isDefault: false }, + { id: 'minimal', trust: 'system', isDefault: false }, ], { failWrite: 'read-only settings' })) await controller.load() - await controller.select('core-web') + await controller.select('minimal') const state = controller.store.getSnapshot() expect(state.currentValue).toBe('standard') @@ -152,17 +152,17 @@ describe('the composer seat controller', () => { const ROSTER: { id: string; trust: 'system' | 'user'; isDefault: boolean }[] = [ { id: 'standard', trust: 'system', isDefault: true }, - { id: 'core-web', trust: 'system', isDefault: false }, + { id: 'minimal', trust: 'system', isDefault: false }, ] it('shows what the session runs, not the deployment default', async () => { - const controller = seat(ROSTER, { blank: true, agentPreset: 'core-web' }) + const controller = seat(ROSTER, { blank: true, agentPreset: 'minimal' }) await controller.load() // A resumed session runs what it was created with; showing `standard` // because it is the current default would be a lie about this session. - expect(controller.store.getSnapshot().current).toBe('core-web') + expect(controller.store.getSnapshot().current).toBe('minimal') expect(controller.store.getSnapshot().switchable).toBe(true) }) @@ -187,7 +187,7 @@ describe('the composer seat controller', () => { const controller = seat(ROSTER, { blank: false, agentPreset: 'standard' }, { writes }) await controller.load() - await controller.select('core-web') + await controller.select('minimal') // The host enforces the same rule; the seat simply never asks. expect(writes).toEqual([]) @@ -199,17 +199,17 @@ describe('the composer seat controller', () => { const controller = seat(ROSTER, { blank: true, agentPreset: 'standard' }, { writes }) await controller.load() - await controller.select('core-web') + await controller.select('minimal') - expect(writes).toEqual([{ ns: 'select', patch: 'core-web' }]) - expect(controller.store.getSnapshot().current).toBe('core-web') + expect(writes).toEqual([{ ns: 'select', patch: 'minimal' }]) + expect(controller.store.getSnapshot().current).toBe('minimal') }) it('restores the previous value when the host rejects the switch', async () => { const controller = seat(ROSTER, { blank: true, agentPreset: 'standard' }, { failSelect: 'already started' }) await controller.load() - await controller.select('core-web') + await controller.select('minimal') const state = controller.store.getSnapshot() expect(state.current).toBe('standard') @@ -292,9 +292,9 @@ describe('the composer seat controller', () => { const controller = seat(ROSTER, { blank: true, agentPreset: 'standard' }, { throwOn: 'select' }) await controller.load() - await controller.select('core-web') + await controller.select('minimal') - // Showing `core-web` after a failed switch would claim a composition the + // Showing `minimal` after a failed switch would claim a composition the // session never got. expect(controller.store.getSnapshot()).toMatchObject({ current: 'standard', busy: false, error: 'socket closed' }) }) diff --git a/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts b/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts index fea0888621..a37a02f458 100644 --- a/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts +++ b/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts @@ -113,16 +113,16 @@ async function harness(presets?: readonly string[]) { describe('session.create with an agent preset', () => { it('records the resolved preset on the session header', async () => { - const { api, ctx } = await harness(['standard', 'core-web']) + const { api, ctx } = await harness(['standard', 'minimal']) - const created = await api.sessions.create(request({ sessionId: SessionId('s1'), agentPreset: 'core-web' })) + const created = await api.sessions.create(request({ sessionId: SessionId('s1'), agentPreset: 'minimal' })) expect(created.result.ok).toBe(true) - expect(ctx.sessions.get(SessionId('s1'))?.header.agentPreset).toBe('core-web') + expect(ctx.sessions.get(SessionId('s1'))?.header.agentPreset).toBe('minimal') }) it('records the default when the caller names none', async () => { - const { api, ctx } = await harness(['standard', 'core-web']) + const { api, ctx } = await harness(['standard', 'minimal']) await api.sessions.create(request({ sessionId: SessionId('s2') })) @@ -140,8 +140,8 @@ describe('session.create with an agent preset', () => { }) it('refuses to adopt a live session under a different preset', async () => { - const { api } = await harness(['standard', 'core-web']) - await api.sessions.create(request({ sessionId: SessionId('s4'), agentPreset: 'core-web' })) + const { api } = await harness(['standard', 'minimal']) + await api.sessions.create(request({ sessionId: SessionId('s4'), agentPreset: 'minimal' })) const response = await api.sessions.create(request({ sessionId: SessionId('s4'), agentPreset: 'standard' })) @@ -151,13 +151,13 @@ describe('session.create with an agent preset', () => { expect(response.result.error.details).toEqual({ sessionId: 's4', requestedPreset: 'standard', - existingPreset: 'core-web', + existingPreset: 'minimal', }) }) it('adopts a live session unchanged when the caller names no preset', async () => { - const { api } = await harness(['standard', 'core-web']) - await api.sessions.create(request({ sessionId: SessionId('s5'), agentPreset: 'core-web' })) + const { api } = await harness(['standard', 'minimal']) + await api.sessions.create(request({ sessionId: SessionId('s5'), agentPreset: 'minimal' })) // Reconnecting and retrying a create must stay ordinary operations. const response = await api.sessions.create(request({ sessionId: SessionId('s5') })) @@ -257,7 +257,7 @@ describe('a capability the session\'s preset mounts', () => { describe('agentPreset.list', () => { it('marks the default and carries each preset\'s trust', async () => { - const { api } = await harness(['standard', 'core-web']) + const { api } = await harness(['standard', 'minimal']) const response = await api.agentPresets.list(request({})) @@ -265,7 +265,7 @@ describe('agentPreset.list', () => { if (!response.result.ok) throw new Error('unreachable') expect(response.result.value.presets).toEqual([ { id: 'standard', trust: 'system', isDefault: true }, - { id: 'core-web', trust: 'system', isDefault: false }, + { id: 'minimal', trust: 'system', isDefault: false }, ]) expect(response.result.value.authorable).toBe(true) }) @@ -288,26 +288,26 @@ describe('agentPreset.list', () => { describe('agentPreset.select', () => { it('recomposes a blank session', async () => { - const { api } = await harness(['standard', 'core-web']) + const { api } = await harness(['standard', 'minimal']) await api.sessions.create(request({ sessionId: SessionId('sel-1'), agentPreset: 'standard' })) const response = await api.agentPresets.select( - request({ sessionId: SessionId('sel-1'), agentPreset: 'core-web' })) + request({ sessionId: SessionId('sel-1'), agentPreset: 'minimal' })) expect(response.result.ok).toBe(true) if (!response.result.ok) throw new Error('unreachable') - expect(response.result.value.agentPreset).toBe('core-web') + expect(response.result.value.agentPreset).toBe('minimal') }) it('refuses once the conversation has started', async () => { - const { api, ctx } = await harness(['standard', 'core-web']) + const { api, ctx } = await harness(['standard', 'minimal']) await api.sessions.create(request({ sessionId: SessionId('sel-2'), agentPreset: 'standard' })) // One turn is enough: the history from here on was produced under // `standard`'s tools, and a swap would strand those tool calls. ctx.sessions.get(SessionId('sel-2'))?.append('turn/start', { turn: 0 }) const response = await api.agentPresets.select( - request({ sessionId: SessionId('sel-2'), agentPreset: 'core-web' })) + request({ sessionId: SessionId('sel-2'), agentPreset: 'minimal' })) expect(response.result.ok).toBe(false) if (response.result.ok) throw new Error('unreachable') diff --git a/packages/host/apiproxy/tests/api-proxy-config.spec.ts b/packages/host/apiproxy/tests/api-proxy-config.spec.ts index d606c12c4a..e8ed3ccb16 100644 --- a/packages/host/apiproxy/tests/api-proxy-config.spec.ts +++ b/packages/host/apiproxy/tests/api-proxy-config.spec.ts @@ -360,14 +360,14 @@ describe('settings domain', () => { ctx.settings.register(settingsNamespace('agent-presets'), z.object({ default: z.string() })) const api = createApiProxy(ctx, DEFAULTS) - expectOk(await api.settings.update(request({ ns: 'agent-presets', patch: { default: 'core-web' } }))) + expectOk(await api.settings.update(request({ ns: 'agent-presets', patch: { default: 'minimal' } }))) // Both browser surfaces that offer the choice — the General row and the // management section — write the default through `settings.update`, so a // namespace outside this boundary makes the picker move and then silently // forget, which is worse than refusing the control. expect(ctx.settings.describe().find(view => String(view.ns) === 'agent-presets')?.value) - .toEqual({ default: 'core-web' }) + .toEqual({ default: 'minimal' }) }) it('refuses even a model-provider namespace once its directory entry is gone', async () => { diff --git a/packages/host/apiproxy/tests/fetch-carrier.spec.ts b/packages/host/apiproxy/tests/fetch-carrier.spec.ts index fc99b31675..c51b9a830f 100644 --- a/packages/host/apiproxy/tests/fetch-carrier.spec.ts +++ b/packages/host/apiproxy/tests/fetch-carrier.spec.ts @@ -369,8 +369,8 @@ describe('unary round trip (handler ⇄ client, no network)', () => { expect((await c.agentPresets.list({})).result).toEqual({ ok: true, value: { presets: [], authorable: false }, }) - expect((await c.agentPresets.select({ sessionId: 's' as never, agentPreset: 'core-web' })).result) - .toEqual({ ok: true, value: { agentPreset: 'core-web' } }) + expect((await c.agentPresets.select({ sessionId: 's' as never, agentPreset: 'minimal' })).result) + .toEqual({ ok: true, value: { agentPreset: 'minimal' } }) expect((await c.agentPresets.read({ agentPreset: 'mine' })).result).toEqual({ ok: true, value: { agentPreset: 'mine', trust: 'user', content: '', writable: true }, }) diff --git a/packages/preset/README.i18n.yaml b/packages/preset/README.i18n.yaml index f756692364..2c654c7fa6 100644 --- a/packages/preset/README.i18n.yaml +++ b/packages/preset/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/preset/README.md -README.md: d6d51b4d83a85fed3f690334e9f5704567da83a9 -README.zh.md: 6485caa03856e24232648dc36dfbc07d1d6aa6f8 +README.md: 997e64c75e36d6a70438a92c223dea93eab5eb32 +README.zh.md: 946b98a7b1366facec37360c9f39ca36cc908872 diff --git a/packages/preset/README.md b/packages/preset/README.md index d6d51b4d83..ca874398f0 100644 --- a/packages/preset/README.md +++ b/packages/preset/README.md @@ -9,7 +9,7 @@ An **agent preset** is a directory holding one `agent.cordis.yml`. Mounting it u | `agent-presets/` | Preset vocabulary, filesystem discovery over trusted and user-authored roots, and the guarded per-agent mount | `ctx.agentPresets` | | `persona/` | The agent persona as a composable row, so a preset can change identity and not only tools | — | -The deployment ships `standard` (the full coding agent), `core-web` (a two-tool benchmark surface), and `cordis` (the standard agent plus the self-referential toolset and a composition-authoring skill, so a person can ask an agent to author another agent). +The deployment ships `standard` (the full coding agent), `minimal` (a two-tool benchmark surface), and `cordis` (the standard agent plus the self-referential toolset and a composition-authoring skill, so a person can ask an agent to author another agent). The composition split this group assumes: registries and cross-session facilities are process singletons and stay in the host composition, while a preset carries what one agent contributes to them. A preset that names a row publishing a process-global service is rejected at mount rather than allowed to collide with the next session. diff --git a/packages/preset/README.zh.md b/packages/preset/README.zh.md index 6485caa038..2187bc78ed 100644 --- a/packages/preset/README.zh.md +++ b/packages/preset/README.zh.md @@ -9,7 +9,7 @@ | `agent-presets/` | preset 词汇、在受信任目录与用户自建目录上的文件系统发现,以及带校验的按 agent 挂载 | `ctx.agentPresets` | | `persona/` | 把 agent 人设做成可组装的行,使 preset 不止能改工具、也能改身份 | — | -部署随附三个 preset:`standard`(完整编码 agent)、`core-web`(两个工具的 benchmark 表层),以及 `cordis`(标准 agent 加上自指工具集与一份组装创作 skill,使人可以让 agent 去创作另一个 agent)。 +部署随附三个 preset:`standard`(完整编码 agent)、`minimal`(两个工具的 benchmark 表层),以及 `cordis`(标准 agent 加上自指工具集与一份组装创作 skill,使人可以让 agent 去创作另一个 agent)。 本组假定的组装划分是:注册表与跨会话设施是进程单例,留在宿主组装中;preset 只承载单个 agent 对它们的贡献。若 preset 中某一行发布了进程级全局服务,挂载时即被拒绝,而不是留到与下一个会话相撞。 diff --git a/packages/preset/agent-presets/README.md b/packages/preset/agent-presets/README.md index f5c999756f..c61a84730a 100644 --- a/packages/preset/agent-presets/README.md +++ b/packages/preset/agent-presets/README.md @@ -82,7 +82,7 @@ When a settings provider is composed, this plugin registers the `agent-presets` ```yaml agent-presets: - default: core-web + default: minimal ``` The value is read per resolution rather than snapshotted, so a hot-reloaded document takes effect on the next session created and every running session stays on the preset it was composed from. Clearing the user field re-inherits the composition default. A default naming a preset no root supplies is stored without complaint and fails at the next `resolve()` — the roster is a live directory, so a name absent now may exist by the time a session asks for it. diff --git a/packages/preset/agent-presets/README.zh.md b/packages/preset/agent-presets/README.zh.md index e0dffe424c..1caacec4f9 100644 --- a/packages/preset/agent-presets/README.zh.md +++ b/packages/preset/agent-presets/README.zh.md @@ -82,7 +82,7 @@ description: 只向模型呈现 bash 与 str_replace_editor,适合 benchmark ```yaml agent-presets: - default: core-web + default: minimal ``` 该值在每次解析时读取而非快照,因此热重载的文档对**此后创建**的会话生效,而每个运行中的会话仍停留在它当初据以组装的 preset 上。清空用户字段即重新继承组装默认值。若默认值指向没有任何根目录提供的 preset,写入时不会报错,而在下一次 `resolve()` 时失败——名单是一个活动目录,此刻不存在的名字,等到某个会话真正索取时可能已经存在。