diff --git a/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.i18n.yaml b/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.i18n.yaml index 3a6b18cecf..16a2f7752a 100644 --- a/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.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 -2026-07-14-provider-routed-llm-adapters.md: 98205d18d07752e0cdba86d7cba80368d45fd816 -2026-07-14-provider-routed-llm-adapters.zh.md: c35225a86baf4c2d09732b5940abbc8046d365fb +2026-07-14-provider-routed-llm-adapters.md: a0f43751a17134f42e3f1a73879c79521fa7921b +2026-07-14-provider-routed-llm-adapters.zh.md: a8712166e74e323e3091251766fd26fefd0690d0 diff --git a/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.md b/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.md index 98205d18d0..a0f43751a1 100644 --- a/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.md +++ b/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.md @@ -84,6 +84,7 @@ The on-disk session format remains the pre-release pinned version `0`, with no c - Unit coverage exercises registry conflicts, request reconstruction, session validation, profile resolution, single-attempt option forwarding, native API selection including OpenAI Responses, conversion, replay validation, error mapping, caller cancellation, idle-timeout transport termination, content rewrites, and same-instance versus different-instance replay dispatch. - Keyless loop/session tests and ACP snapshots exercise durable provider/model metadata, resume and fork propagation, workflow/subagent overrides, and unchanged user-visible transcripts; the key-gated DeepSeek e2e retains real provider streaming and tool follow-up coverage. +- The TUI's `pi-ai-anthropic.cordis.yml` overlay disables the native provider owner, mounts an explicit Anthropic profile over deployment-supplied credentials and endpoint, and selects a catalog model. Its keyless PTY smoke opens `/model`, observes the profile's default reasoning effort, and selects the next adapter-advertised effort without provider I/O. - Public JSDoc, package READMEs, architecture and core-data-structure docs, generated catalogs, examples, session fixtures, and Python SDK pairs use provider/model targets consistently and are checked by the repository documentation and type-equivalence gates. ## Risks diff --git a/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.zh.md b/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.zh.md index c35225a86b..a8712166e7 100644 --- a/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.zh.md +++ b/.agents/notes/implemented/architecture/2026-07-14-provider-routed-llm-adapters.zh.md @@ -84,6 +84,7 @@ JSON-RPC 运行时显式接收 provider 与 model。仅当 `deepseek` 提供方 - 单元测试覆盖注册表冲突、请求重建、会话验证、配置解析、单次请求的选项转发、包括 OpenAI Responses 在内的原生 API 选择、转换、回放验证、错误映射、调用方取消、空闲超时导致的传输终止、内容重写,以及同一实例与不同实例间的回放分发。 - 无密钥的 agent loop/会话测试和 ACP 快照覆盖持久化 provider/model 元数据、恢复与 fork 传播、工作流/subagent 覆盖,以及不变的用户可见 transcript(文本记录);密钥门控的 DeepSeek e2e 测试保留真实提供方的流式输出与工具后续调用覆盖率。 +- TUI 的 `pi-ai-anthropic.cordis.yml` 覆盖配置会禁用原生提供方所有者,在部署提供的凭据和端点之上挂载显式 Anthropic 配置,并选择一个目录模型。其无密钥 PTY 冒烟测试会打开 `/model`,观察该配置的默认推理级别,并在不执行提供方 I/O 的情况下选择适配器公布的下一个推理级别。 - 公共 JSDoc、package README、架构与核心数据结构文档、生成目录、示例、会话 fixture(测试前置数据)和 Python SDK 配对文档统一使用 provider/model 目标,并由仓库文档与类型等价门禁校验。 ## 风险 diff --git a/examples/tui-agent/README.md b/examples/tui-agent/README.md index 47ba6e45a2..fef7f99a91 100644 --- a/examples/tui-agent/README.md +++ b/examples/tui-agent/README.md @@ -13,6 +13,14 @@ pnpm run demo:tui Both the demo script and the installable `dsh` CLI ([`apps/cli`](../../apps/cli/README.md)) boot this example's `cordis.yml` as the shipped default config; `dsh` additionally applies the personal overlay from `~/.dsh` and uses the invoking directory as the workspace. +### Anthropic-compatible pi-ai route + +The checked-in `pi-ai-anthropic.cordis.yml` overlay disables the native DeepSeek adapter, mounts one explicit `anthropic` profile through `dsh-llm-pi-ai`, and selects `claude-sonnet-4-6` with `high` reasoning by default. It deliberately reuses `DEEPSEEK_API_KEY` and `DEEPSEEK_BASE_URL` for a deployment whose private endpoint accepts the Anthropic Messages protocol; a DeepSeek Chat Completions endpoint is not interchangeable. Run it from the repository root: + +```sh +pnpm run demo:tui --config examples/tui-agent/pi-ai-anthropic.cordis.yml +``` + Type a coding task. The agent works through the `read`/`write`/`edit` filesystem tools for ordinary file operations and `bash` (+ the generic `task_output` / `task_list` / `task_kill` for background tasks) for shell commands, searches, and test runs, each in a fresh `bash -c` (the system prompt tells the model to pass `workdir` instead of `cd`). Both the fs tools and bash resolve relative paths against the session workspace. It can also delegate with `subagent`/`subagent_fork`. The `todo_write` task tracker is opt-in and not in the shipped config: add `@deepseek-ai/dsh-tool-todo` to `cordis.yml` (or a personal-config overlay under `~/.dsh`) to expose it. Once loaded, the model records a whole-list plan to the session log and the TUI renders it. @@ -51,7 +59,7 @@ This example is a thin leaf `cordis.yml`: it picks the swappable backends, loads | Entry | Demonstrates | |---|---| | `hmr` (`@cordisjs/plugin-hmr`) | the dev/demo edit-reload loop — a **leaf** entry (not baked into the app) because it depends on the Loader's internal module access | -| `llm-deepseek` | real `LlmAdapter` via config (`!!js process.env.…` secrets); swap one line to `@deepseek-ai/dsh-llm-pi-ai` for the library-backed twin | +| `llm-deepseek` | the default native adapter; `pi-ai-anthropic.cordis.yml` disables that owner and mounts an explicit library-backed Anthropic profile | | `bash` (`dsh-bash-local`) | the executor implementation — the swappable half of the bash seam. The model-facing `bash` schema (`tool-bash`) and generic `task_*` controls (`tool-tasks`) come from `dsh-agent-spine-demo`, so only the executor is a leaf choice | | `tui-agent` (`@deepseek-ai/dsh-tui-demo`) | the app bundle: the agent-spine demo + JSONL persistence + the pi-tui channel + a pre-created `main` agent | | `subagent`, `subagent-spawn`, `subagent-fork` | the subagent provider registry plus the two in-process backends: a fresh child and a child seeded with the parent's completed-turn prefix | diff --git a/examples/tui-agent/pi-ai-anthropic.cordis.yml b/examples/tui-agent/pi-ai-anthropic.cordis.yml new file mode 100644 index 0000000000..c1ec3b2968 --- /dev/null +++ b/examples/tui-agent/pi-ai-anthropic.cordis.yml @@ -0,0 +1,39 @@ +# Replace the native DeepSeek adapter with pi-ai's Anthropic route. The +# configured endpoint must implement the Anthropic Messages protocol even +# though this deployment reuses the repository's DeepSeek-named environment +# variables. +- id: base + name: '@cordisjs/plugin-include' + config: + path: ./cordis.yml + patches: + - id: llm-deepseek + name: '@deepseek-ai/dsh-llm-deepseek' + disabled: true + - id: tui-agent + name: '@deepseek-ai/dsh-tui-demo' + config: + provider: anthropic + model: claude-sonnet-4-6 + resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined" + persistenceRoot: './.sessions' + resumeCommand: 'dsh --config examples/tui-agent/pi-ai-anthropic.cordis.yml --resume {session}' + workspaceContext: + maxBytes: 65536 + ui: + showReasoning: true + maxToolOutputLines: 6 + persona: | + You are a coding agent powered by the {{model}} model. + + Verify your work by running the code or tests. Keep answers brief and + factual. + - insert: + - id: llm-pi-ai + name: '@deepseek-ai/dsh-llm-pi-ai' + config: + providers: + - provider: anthropic + apiKey: !!js process.env.DEEPSEEK_API_KEY + baseURL: !!js process.env.DEEPSEEK_BASE_URL + reasoning: high diff --git a/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts b/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts index 169b582492..79f0155cbd 100644 --- a/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts +++ b/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts @@ -11,6 +11,7 @@ import { runTuiPtySmoke, type TuiPtySmokeOptions } from './pty-harness.ts' const dshBinScript = fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url)) const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url)) const codeModeConfigPath = fileURLToPath(new URL('../code-mode.cordis.yml', import.meta.url)) +const piAiAnthropicConfigPath = fileURLToPath(new URL('../pi-ai-anthropic.cordis.yml', import.meta.url)) const scriptedConfigPath = fileURLToPath(new URL('./fixtures/tui-scripted.cordis.yml', import.meta.url)) const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url)) @@ -246,6 +247,26 @@ describe('tui-agent keyless smoke (real Loader tree in a PTY)', () => { expect(output).toContain('\u001B[?2004l') }, LOADER_SMOKE_TEST_TIMEOUT_MS) + it('boots the pi-ai Anthropic overlay and selects an advertised reasoning effort', async () => { + const output = await smoke({ + label: 'tui-agent pi-ai anthropic', + tempDirPrefix: 'tui-agent-pi-ai-anthropic-', + configPath: piAiAnthropicConfigPath, + env: { + DEEPSEEK_API_KEY: 'keyless-tui-no-call', + DEEPSEEK_BASE_URL: 'http://127.0.0.1:1', + }, + actions: [ + { waitFor: 'main-session-', send: '/model\r' }, + { waitFor: 'Claude Sonnet 4.6 — High — current', send: '\x1b[Z\r' }, + { waitFor: 'Reasoning effort: Max.', send: '/exit\r' }, + ], + }) + expect(output).toContain('anthropic/claude-sonnet-4-6') + expect(output).toContain('Reasoning effort: Max.') + expect(output).toContain('\u001B[?2004l') + }, LOADER_SMOKE_TEST_TIMEOUT_MS) + }) describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => {