diff --git a/docs/codex-config-proxy-plan.md b/docs/codex-config-proxy-plan.md deleted file mode 100644 index d504b11..0000000 --- a/docs/codex-config-proxy-plan.md +++ /dev/null @@ -1,158 +0,0 @@ -# Codex Config And Proxy Plan - -## Goals - -- Launch Codex from Hermes without mutating the user's real `~/.codex`. -- Keep model/provider config isolated by Hermes profile and provider. -- Support OpenAI Responses providers directly. -- Add a local adapter for providers that only expose OpenAI Chat Completions. -- Keep Codex resume/history stable by using a consistent model provider id. - -## Directory Layout - -All generated Codex state should live under the Web UI home: - -```text -~/.hermes-web-ui/coding-agent/ - model/{profile}/{provider}/codex/ - config.toml - auth.json - AGENTS.md - workspace/{profile}/{provider}/ -``` - -Launch command shape: - -```bash -cd ~/.hermes-web-ui/coding-agent/workspace/{profile}/{provider} \ - && CODEX_HOME=~/.hermes-web-ui/coding-agent/model/{profile}/{provider}/codex \ - codex --model {model} -``` - -## Current MVP Config - -For Responses-compatible providers, generate: - -```toml -model_provider = "custom" -model = "provider-model-id" -disable_response_storage = true - -[model_providers.custom] -name = "provider-id" -base_url = "https://provider.example/v1" -wire_api = "responses" -requires_openai_auth = false -experimental_bearer_token = "provider-api-key" -``` - -Keep `auth.json` empty for third-party providers: - -```json -{} -``` - -Reason: avoid overwriting the user's official Codex / ChatGPT login cache. - -## Stable Provider Id - -Use `model_provider = "custom"` for third-party providers. - -Codex history and resume behavior can depend on provider identity. Keeping a stable provider id avoids making history appear to move between provider-specific ids. - -Provider identity remains visible in: - -- `[model_providers.custom].name` -- the generated directory path -- the UI launch result - -## Local Proxy Plan - -Some providers expose only OpenAI Chat Completions: - -```text -/v1/chat/completions -``` - -Codex prefers Responses: - -```text -/v1/responses -``` - -Add a local proxy endpoint: - -```text -/api/codex-proxy/{routeKey}/v1/responses -``` - -The `routeKey` should encode: - -```text -profile + "\0" + provider + "\0" + model -``` - -Authentication should use a generated `hwui_...` token, not the upstream provider key. - -## Responses To Chat Mapping - -When the upstream provider is Chat Completions only: - -- Convert Responses `input` items to Chat `messages`. -- Convert Responses `tools` to Chat `tools`. -- Convert `max_output_tokens` to `max_tokens`. -- Preserve `stream: true`. -- Map function calls and function outputs both ways. - -Response event mapping for streaming: - -```text -chat delta.content -> response.output_text.delta -chat delta.tool_calls -> response.function_call_arguments.delta -finish -> response.completed -``` - -Non-streaming mapping: - -```text -chat.choices[0].message.content -> output message/content -chat.choices[0].message.tool_calls -> output function_call items -chat.usage -> response.usage -``` - -## Config Generation With Proxy - -For Chat-only providers, generated Codex config should point at Hermes: - -```toml -model_provider = "custom" -model = "provider-model-id" -disable_response_storage = true - -[model_providers.custom] -name = "provider-id" -base_url = "http://127.0.0.1:{serverPort}/api/codex-proxy/{routeKey}/v1" -wire_api = "responses" -requires_openai_auth = false -experimental_bearer_token = "hwui_generated_route_token" -``` - -The proxy then forwards to the real provider with the real provider key. - -## Implementation Tasks - -1. Add a Codex proxy service parallel to the Claude Code proxy service. -2. Register route targets in memory for launch-time provider/model selection. -3. Add `/api/codex-proxy/:key/v1/responses`. -4. Implement Responses to Chat conversion. -5. Implement Chat to Responses conversion for streaming and non-streaming. -6. Add launch-time api mode selection for Codex providers. -7. Generate Codex `base_url` against the local proxy when api mode is Chat Completions. -8. Add server tests for config generation, auth rejection, streaming conversion, tool call conversion, and error passthrough. - -## Open Questions - -- Whether to expose Codex protocol selection in the UI immediately or infer it from provider preset metadata. -- Whether to persist proxy targets or require relaunch after server restart. -- Whether model catalog generation is needed for the first Codex MVP. -- Whether MCP and `AGENTS.md` should be copied from a template or edited only through the advanced config editor. diff --git a/docs/plans/2026-05-23-latex-rendering.md b/docs/plans/2026-05-23-latex-rendering.md deleted file mode 100644 index 6c14d5b..0000000 --- a/docs/plans/2026-05-23-latex-rendering.md +++ /dev/null @@ -1,491 +0,0 @@ -# LaTeX Rendering Implementation Plan - -> **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task. - -**Goal:** Add reliable LaTeX/math rendering to Hermes Web UI chat Markdown messages so formulas render visually instead of appearing as raw TeX, including explicit fenced math blocks like ```latex. - -**Architecture:** Keep the existing `markdown-it` renderer in `MarkdownRenderer.vue` and add a KaTeX-backed math plugin there, plus a small fence override for explicit LaTeX code fences. Load KaTeX styles globally once, cover inline, display, and fenced math in component tests, then verify with a production build and a local Web UI smoke test. - -**Tech Stack:** Vue 3, TypeScript, Vite, markdown-it, KaTeX, Vitest, @vue/test-utils. - ---- - -## Acceptance Criteria - -- Chat messages render inline math like `$x^2 + y^2 = z^2$` as KaTeX HTML. -- Chat messages render block math like `$$\n\\int_0^1 x^2 dx = \\frac{1}{3}\n$$` as display KaTeX HTML. -- Chat messages render explicit fenced math blocks like ```latex, ```tex, or ```math as display KaTeX HTML. -- Existing Markdown features still work: code fences, Mermaid fences, headings, local file links, mentions. -- Ordinary fenced code blocks stay literal and are not rendered as math. -- Invalid/unsupported math syntax does not crash the chat renderer. -- `npm run test -- tests/client/markdown-rendering.test.ts` passes. -- `npm run build` passes. -- Local `hermes-web-ui.service` can be restarted onto the built fork and displays formulas in the real panel. - -## Recommended Dependency Choice - -Use `markdown-it-katex` + `katex` because the app already uses `markdown-it`, and this is the smallest change surface. - -Install as runtime dependencies, not dev-only, because the renderer runs in the browser bundle: - -```bash -npm install katex markdown-it-katex -npm install -D @types/katex -``` - -If TypeScript reports no module declaration for `markdown-it-katex`, add a local declaration file instead of weakening `tsconfig`. - ---- - -## Task 1: Add KaTeX dependencies - -**Objective:** Make the required renderer and styles available to the client bundle. - -**Files:** -- Modify: `package.json` -- Modify: `package-lock.json` or lockfile generated by npm if present - -**Step 1: Install packages** - -Run from repo root: - -```bash -cd /home/werserk/2-kira/hermes-web-ui -npm install katex markdown-it-katex -npm install -D @types/katex -``` - -**Expected:** `package.json` includes `katex` and `markdown-it-katex`; lockfile is updated. - -**Step 2: Verify dependency tree** - -```bash -npm ls katex markdown-it-katex -``` - -**Expected:** both packages resolve without `UNMET DEPENDENCY`. - -**Step 3: Commit** - -```bash -git add package.json package-lock.json -git commit -m "chore: add latex rendering dependencies" -``` - ---- - -## Task 2: Add TypeScript module declaration if needed - -**Objective:** Keep TypeScript strict and avoid `any` leaking into the renderer setup. - -**Files:** -- Create if needed: `packages/client/src/types/markdown-it-katex.d.ts` - -**Step 1: Run typecheck to discover whether declaration is needed** - -```bash -npm run build -``` - -**Expected before implementation:** build may still fail later because code is not changed yet. For this task, only check whether TypeScript knows the `markdown-it-katex` module after it is imported in Task 3. - -**Step 2: If TS2307/TS7016 appears, create declaration** - -Create `packages/client/src/types/markdown-it-katex.d.ts`: - -```ts -declare module 'markdown-it-katex' { - import type MarkdownIt from 'markdown-it' - - interface MarkdownItKatexOptions { - throwOnError?: boolean - errorColor?: string - strict?: boolean | string | ((errorCode: string) => boolean | string) - output?: 'html' | 'mathml' | 'htmlAndMathml' - trust?: boolean | ((context: unknown) => boolean) - macros?: Record - } - - const markdownItKatex: MarkdownIt.PluginWithOptions - export default markdownItKatex -} -``` - -**Step 3: Commit only if file was needed** - -```bash -git add packages/client/src/types/markdown-it-katex.d.ts -git commit -m "chore: add markdown-it-katex types" -``` - ---- - -## Task 3: Wire KaTeX into MarkdownRenderer - -**Objective:** Render math in the same path that currently renders Markdown chat messages. - -**Files:** -- Modify: `packages/client/src/components/hermes/chat/MarkdownRenderer.vue` - -**Step 1: Import the plugin** - -Near existing Markdown imports: - -```ts -import markdownItKatex from 'markdown-it-katex' -``` - -**Step 2: Register the plugin after creating `md`** - -After the `new MarkdownItConstructor(...)` block and before custom fence renderer setup: - -```ts -md.use(markdownItKatex, { - throwOnError: false, - errorColor: '#cc3344', - output: 'htmlAndMathml', -}) -``` - -**Step 3: Preserve existing Mermaid fence behavior** - -Do not move or remove this existing code: - -```ts -const defaultFenceRenderer = md.renderer.rules.fence?.bind(md.renderer.rules) - -md.renderer.rules.fence = (tokens, idx, options, env, self) => { - const token = tokens[idx] - if (isMermaidFence(token.info)) { - return renderMermaidPlaceholder(token.content) - } - - if (defaultFenceRenderer) { - return defaultFenceRenderer(tokens, idx, options, env, self) - } - - return self.renderToken(tokens, idx, options) -} -``` - -**Step 4: Run focused test suite** - -```bash -npm run test -- tests/client/markdown-rendering.test.ts -``` - -**Expected:** existing tests still pass or only fail because new math tests are not yet added. - -**Step 5: Commit** - -```bash -git add packages/client/src/components/hermes/chat/MarkdownRenderer.vue -git commit -m "feat: enable latex rendering in chat markdown" -``` - ---- - -## Task 4: Load and tune KaTeX CSS - -**Objective:** Ensure rendered math is visually correct in the Web UI. - -**Files:** -- Modify: `packages/client/src/main.ts` -- Optionally modify: `packages/client/src/styles/global.scss` - -**Step 1: Import KaTeX CSS once globally** - -In `packages/client/src/main.ts`, add after global styles import: - -```ts -import 'katex/dist/katex.min.css' -``` - -Current import area should become: - -```ts -import App from './App.vue' -import './styles/global.scss' -import 'katex/dist/katex.min.css' -``` - -**Step 2: Add Hermes-specific layout tweaks only if needed** - -If visual smoke test shows cramped block equations, append to `packages/client/src/styles/global.scss`: - -```scss -.markdown-body { - .katex-display { - overflow-x: auto; - overflow-y: hidden; - padding: 0.25rem 0; - } -} -``` - -Do not restyle KaTeX itself unless there is a concrete visual bug. - -**Step 3: Commit** - -```bash -git add packages/client/src/main.ts packages/client/src/styles/global.scss -git commit -m "style: load katex styles for markdown math" -``` - ---- - -## Task 5: Add regression tests for math rendering - -**Objective:** Prove formulas render, code fences stay literal, and malformed math is safe. - -**Files:** -- Modify: `tests/client/markdown-rendering.test.ts` - -**Step 1: Add inline math test** - -Inside `describe('MarkdownRenderer', () => { ... })`: - -```ts -it('renders inline latex math with katex', () => { - const wrapper = mount(MarkdownRenderer, { - props: { - content: 'Pythagoras: $x^2 + y^2 = z^2$.', - }, - }) - - const body = wrapper.find('.markdown-body') - expect(body.find('.katex').exists()).toBe(true) - expect(body.html()).toContain('x') - expect(body.html()).toContain('z') - expect(body.text()).not.toContain('$x^2 + y^2 = z^2$') -}) -``` - -**Step 2: Add block math test** - -```ts -it('renders display latex math with katex', () => { - const wrapper = mount(MarkdownRenderer, { - props: { - content: '$$\n\\int_0^1 x^2 dx = \\frac{1}{3}\n$$', - }, - }) - - const body = wrapper.find('.markdown-body') - expect(body.find('.katex-display').exists()).toBe(true) - expect(body.find('.katex').exists()).toBe(true) - expect(body.text()).not.toContain('$$') -}) -``` - -**Step 3: Add fenced-code non-rendering test** - -```ts -it('does not render latex inside fenced code blocks', () => { - const wrapper = mount(MarkdownRenderer, { - props: { - content: '```md\n$x^2 + y^2 = z^2$\n```', - }, - }) - - expect(wrapper.find('.markdown-body').find('.katex').exists()).toBe(false) - expect(wrapper.find('code.hljs').text()).toContain('$x^2 + y^2 = z^2$') -}) -``` - -**Step 4: Add invalid math safety test** - -```ts -it('keeps rendering when latex syntax is invalid', () => { - const wrapper = mount(MarkdownRenderer, { - props: { - content: 'Before $\\notacommand{ after', - }, - }) - - expect(wrapper.find('.markdown-body').text()).toContain('Before') -}) -``` - -**Step 5: Run focused tests** - -```bash -npm run test -- tests/client/markdown-rendering.test.ts -``` - -**Expected:** all tests in the file pass. - -**Step 6: Commit** - -```bash -git add tests/client/markdown-rendering.test.ts -git commit -m "test: cover latex rendering in markdown" -``` - ---- - -## Task 6: Run build and broader regression checks - -**Objective:** Catch TypeScript, Vite, and existing client regressions before deploying locally. - -**Files:** -- No source changes expected unless checks fail. - -**Step 1: Run focused tests** - -```bash -npm run test -- tests/client/markdown-rendering.test.ts tests/client/markdown-rendering-mermaid-import-timeout.test.ts -``` - -**Expected:** pass. - -**Step 2: Run all tests if practical** - -```bash -npm run test -``` - -**Expected:** pass. If unrelated pre-existing failures appear, record them in the PR body with evidence. - -**Step 3: Run production build** - -```bash -npm run build -``` - -**Expected:** pass and update `dist/` only as build output. Do not commit `dist/` unless the package/release workflow requires it. - -**Step 4: Commit fixes only if needed** - -```bash -git add -git commit -m "fix: stabilize latex markdown rendering" -``` - ---- - -## Task 7: Deploy to Maxim's local Hermes Web UI instance - -**Objective:** Start using the feature on `https://hermes.kiraproject.ru/` before upstream PR review completes. - -**Files:** -- Local runtime install under npm/global prefix may change outside the repo. -- Do not commit generated deployment artifacts unless intentionally part of the repo. - -**Step 1: Build from the fork checkout** - -```bash -cd /home/werserk/2-kira/hermes-web-ui -npm run build -``` - -**Step 2: Replace currently installed package using npm link or local global install** - -Preferred reversible approach: - -```bash -cd /home/werserk/2-kira/hermes-web-ui -npm install -g --prefix /home/werserk/.npm-global . -``` - -**Step 3: Restart the Web UI service** - -```bash -systemctl --user restart hermes-web-ui.service -systemctl --user status hermes-web-ui.service --no-pager --lines=20 -``` - -**Step 4: Smoke test HTTP response** - -```bash -curl -sS -I --max-time 5 http://127.0.0.1:8648/ | head -20 -``` - -**Expected:** HTTP 200/304 or another successful static response, not connection refused. - -**Step 5: Browser smoke test** - -Open the chat panel and send/render a message containing: - -```md -Inline: $x^2 + y^2 = z^2$ - -Block: -$$ -\\int_0^1 x^2 dx = \\frac{1}{3} -$$ - -Code fence should stay literal: -```md -$x^2 + y^2 = z^2$ -``` -``` - -**Expected:** first two formulas render visually; fenced formula stays literal. - ---- - -## Task 8: Prepare upstream PR - -**Objective:** Make the contribution easy for upstream maintainers to review. - -**Files:** -- No code changes required unless PR polish finds an issue. - -**Step 1: Push branch** - -```bash -git push origin feat/latex-rendering -``` - -**Step 2: Create PR draft** - -```bash -gh pr create \ - --repo EKKOLearnAI/hermes-web-ui \ - --head kira-project-lab:feat/latex-rendering \ - --base main \ - --title "feat(chat): render LaTeX math in Markdown messages" \ - --body "$(cat <<'EOF' -## Summary -- Adds KaTeX-backed LaTeX rendering to chat Markdown messages. -- Supports inline `$...$` and display `$$...$$` math. -- Keeps math inside fenced code blocks literal. - -## Test Plan -- [ ] npm run test -- tests/client/markdown-rendering.test.ts -- [ ] npm run test -- tests/client/markdown-rendering-mermaid-import-timeout.test.ts -- [ ] npm run build -- [ ] Manual browser smoke test with inline, block, and fenced math - -## Notes -This uses the existing markdown-it rendering path and keeps HTML disabled. -EOF -)" \ - --draft -``` - -**Step 3: Attach visual proof** - -Add before/after screenshots in the PR body or a PR comment: - -- Before: raw `$...$` / `$$...$$` text. -- After: rendered KaTeX inline and display equations. - ---- - -## Risks and Guardrails - -- **Delimiter ambiguity:** `$` is common in shell snippets and prices. The fenced-code test protects code blocks, but inline text like `cost is $5 and $6` may be parsed depending on plugin behavior. If this is noisy, switch to `markdown-it-texmath` with explicit delimiter options or configure delimiters to prioritize `\(...\)` / `\[...\]`. -- **Security:** keep `html: false` in `markdown-it`; do not enable arbitrary HTML. KaTeX should run with `trust: false` unless a clear need appears. -- **Bundle size:** KaTeX adds client bundle weight. Acceptable for this feature, but mention it in PR if maintainers ask. -- **CSS scope:** KaTeX CSS is global. Import once in `main.ts`; avoid duplicating it inside component styles. -- **Invalid math:** `throwOnError: false` should prevent chat crashes. Regression test required. - -## Definition of Done - -- Branch `feat/latex-rendering` contains committed implementation. -- Local Web UI renders inline and block formulas. -- Focused Markdown tests pass. -- Production build passes. -- PR draft exists against `EKKOLearnAI/hermes-web-ui:main` or is ready to create. diff --git a/packages/server/src/services/coding-agents.ts b/packages/server/src/services/coding-agents.ts index 14eb0bf..a250c6e 100644 --- a/packages/server/src/services/coding-agents.ts +++ b/packages/server/src/services/coding-agents.ts @@ -2,7 +2,7 @@ import { execFile } from 'child_process' import { existsSync, realpathSync } from 'fs' import { mkdir, readFile, stat, writeFile } from 'fs/promises' import { homedir } from 'os' -import { delimiter, dirname, join } from 'path' +import { delimiter, dirname, extname, join } from 'path' import { promisify } from 'util' import { getWebUiHome } from '../config' import { registerClaudeCodeProxyTarget, type ApiMode } from './claude-code-proxy' @@ -12,9 +12,6 @@ import { getModelContextLength } from './hermes/model-context' const execFileAsync = promisify(execFile) const LAUNCH_API_MODES = new Set(['chat_completions', 'codex_responses', 'anthropic_messages']) -const CLAUDE_PROXY_HAIKU_MODEL = 'claude-haiku-4-5' -const CLAUDE_PROXY_SONNET_MODEL = 'claude-sonnet-4-6' -const CLAUDE_PROXY_OPUS_MODEL = 'claude-opus-4-7' const CODING_AGENT_HOME_DIR = 'coding-agent' const CODEX_MODEL_CATALOG_FILE = 'codex-model-catalog.json' const CODEX_CATALOG_BASE_INSTRUCTIONS = 'You are Codex, a coding agent. Be precise, safe, and helpful.' @@ -167,12 +164,15 @@ function getNpmBin() { } function normalizeScopeSegment(value: string | undefined, fallback: string, label: string): string { - const segment = String(value || '').trim() || fallback + // Replace invalid filename characters with underscores + // Windows invalid chars: < > : " / \ | ? * + // Additional problematic chars: control characters + const sanitizedValue = String(value || '').trim().replace(/[<>:"/\\|?*\x00-\x1f]/g, '_') + const segment = sanitizedValue || fallback + if ( segment === '.' || segment === '..' || - segment.includes('/') || - segment.includes('\\') || segment.includes('\0') ) { const err = new Error(`Invalid ${label}`) @@ -309,6 +309,10 @@ function powerShellQuote(value: string): string { return `'${value.replace(/'/g, "''")}'` } +function cmdQuote(value: string): string { + return `"${value.replace(/"/g, '""')}"` +} + function buildLaunchShellCommand(input: { workspaceDir: string env: Record @@ -357,11 +361,11 @@ function isDockerRuntime(): boolean { async function openNativeTerminal(shellCommand: string): Promise { if (process.platform === 'win32') { + const escapedCommand = shellCommand.replace(/"/g, '""').replace(/\$/g, '`$') await execFileAsync('powershell.exe', [ '-NoProfile', '-Command', - "Start-Process -FilePath powershell.exe -ArgumentList @('-NoExit', '-Command', $args[0])", - shellCommand, + `Start-Process -FilePath powershell.exe -ArgumentList @('-NoExit', '-Command', "${escapedCommand}")`, ], { encoding: 'utf-8', timeout: 8000, @@ -508,6 +512,34 @@ async function findCommandPaths(command: string, env: NodeJS.ProcessEnv): Promis } } +function windowsCommandNeedsShell(command: string): boolean { + const extension = extname(command).toLowerCase() + return extension === '.cmd' || extension === '.bat' +} + +async function resolveCommandForExecution(command: string, env: NodeJS.ProcessEnv): Promise { + if (process.platform !== 'win32') return command + const paths = await findCommandPaths(command, env) + // On Windows, prioritize paths with .cmd or .bat extensions since where may return + // both the unix-style script (without extension) and the Windows shim (.cmd) + const windowsPath = paths.find(path => windowsCommandNeedsShell(path)) + return windowsPath || paths[0] || command +} + +function commandExecution(command: string, args: string[]): { command: string; args: string[] } { + if (process.platform === 'win32' && windowsCommandNeedsShell(command)) { + // For CMD /C, the command and args need to be passed as a single string + // The command path should be quoted if it contains spaces, but args are joined directly + const commandArg = / /.test(command) ? `"${command}"` : command + const argsString = args.map(arg => / /.test(arg) ? `"${arg}"` : arg).join(' ') + return { + command: 'cmd.exe', + args: ['/d', '/s', '/c', `${commandArg} ${argsString}`], + } + } + return { command, args } +} + function packageParts(packageName: string): string[] { return packageName.split('/').filter(Boolean) } @@ -601,11 +633,14 @@ export function getCodingAgentConfigFileDefinitions(id: string): CodingAgentConf export async function getCodingAgentStatus(definition: CodingAgentDefinition): Promise { try { - const { stdout, stderr } = await execFileAsync(definition.command, ['--version'], { + const env = await commandEnv() + const resolvedCommand = await resolveCommandForExecution(definition.command, env) + const execution = commandExecution(resolvedCommand, ['--version']) + const { stdout, stderr } = await execFileAsync(execution.command, execution.args, { encoding: 'utf-8', timeout: 8000, windowsHide: true, - env: await commandEnv(), + env, }) const rawVersion = `${stdout || ''}${stderr || ''}`.trim() return { @@ -864,16 +899,21 @@ export async function prepareCodingAgentLaunch(id: string, input: CodingAgentLau : null const claudeBaseUrl = proxyTarget?.baseUrl || baseUrl const claudeApiKey = proxyTarget?.token || apiKey + const modelName = displayNameForModel(model) const settings = { + model, env: { ...(claudeApiKey ? { ANTHROPIC_API_KEY: claudeApiKey } : {}), ...(claudeBaseUrl ? { ANTHROPIC_BASE_URL: claudeBaseUrl } : {}), - ANTHROPIC_DEFAULT_HAIKU_MODEL: CLAUDE_PROXY_HAIKU_MODEL, - ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME: model, - ANTHROPIC_DEFAULT_SONNET_MODEL: CLAUDE_PROXY_SONNET_MODEL, - ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: model, - ANTHROPIC_DEFAULT_OPUS_MODEL: CLAUDE_PROXY_OPUS_MODEL, - ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: model, + ANTHROPIC_MODEL: model, + ANTHROPIC_CUSTOM_MODEL_OPTION: model, + ANTHROPIC_CUSTOM_MODEL_OPTION_NAME: modelName, + ANTHROPIC_DEFAULT_HAIKU_MODEL: model, + ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME: modelName, + ANTHROPIC_DEFAULT_SONNET_MODEL: model, + ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: modelName, + ANTHROPIC_DEFAULT_OPUS_MODEL: model, + ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: modelName, }, } await writeScopedFile('settings', `${JSON.stringify(settings, null, 2)}\n`) diff --git a/tests/server/coding-agents-launch.test.ts b/tests/server/coding-agents-launch.test.ts index 08263c6..127b54b 100644 --- a/tests/server/coding-agents-launch.test.ts +++ b/tests/server/coding-agents-launch.test.ts @@ -108,17 +108,21 @@ describe('coding agent launch preparation', () => { expect(result.shellCommand).not.toContain('--model') const settings = JSON.parse(readFileSync(join(result.rootDir, 'settings.json'), 'utf-8')) + expect(settings.model).toBe('cognitivecomputations/dolphin-mistral-24b-venice-edition:free') expect(settings.env.ANTHROPIC_API_KEY).toMatch(/^hwui_/) expect(settings.env.ANTHROPIC_BASE_URL).toMatch(/^http:\/\/127\.0\.0\.1:\d+\/api\/claude-code-proxy\/.+$/) expect(settings.env).toMatchObject({ - ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5', - ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', - ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-6', - ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', - ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-7', - ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', + ANTHROPIC_MODEL: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', + ANTHROPIC_CUSTOM_MODEL_OPTION: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', + ANTHROPIC_CUSTOM_MODEL_OPTION_NAME: 'Dolphin Mistral 24b Venice Edition:Free', + ANTHROPIC_DEFAULT_HAIKU_MODEL: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', + ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME: 'Dolphin Mistral 24b Venice Edition:Free', + ANTHROPIC_DEFAULT_SONNET_MODEL: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', + ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: 'Dolphin Mistral 24b Venice Edition:Free', + ANTHROPIC_DEFAULT_OPUS_MODEL: 'cognitivecomputations/dolphin-mistral-24b-venice-edition:free', + ANTHROPIC_DEFAULT_OPUS_MODEL_NAME: 'Dolphin Mistral 24b Venice Edition:Free', }) - expect(settings.env).not.toHaveProperty('ANTHROPIC_MODEL') + expect(settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL).not.toBe('claude-sonnet-4-6') }) it('keeps Claude Code protocol overrides behind the local proxy', async () => {