Files
qiming/qimingcode/packages/opencode/docs/model-acp-replay-retention.md

9.9 KiB

Models and Local ACP — Implementation Record

Purpose

This document records all custom model/ACP enhancements on feat/qimingcode-qimingclaw-builtin that differ from origin/dev. Use it to replay work after future dev merges, or to audit what's custom vs upstream.

Branch Baseline

  • Base branch: dev
  • Feature branch: feat/qimingcode-qimingclaw-builtin
  • Merge strategy: manual replay by batch after each dev merge, not bulk restore

Batch 1 — Model Source, ACP Session, CLI (COMPLETED 2026-04-27)

1.1 Model Source Loading

New file: src/provider/models-source.ts

Unified model source loader. Priority: cache file → macro → bundled asset → network.

Key design:

  • FetchModelsOptions interface with url?: string to respect Flag.OPENCODE_MODELS_URL
  • Falls back to "https://models.dev/api.json" when url not provided
  • Logs source and URL at each hit point for observability
  • Uses Installation.USER_AGENT for network requests

Modified: src/provider/models.ts

Changes from origin/dev baseline:

  • fetchApi() deleted — replaced by fetchModelsFromSource
  • import { Installation } removed — no longer referenced
  • lastResolvedSource variable tracks where data was resolved from
  • bundledPaths restored for compiled binary support:
    path.join(path.dirname(process.execPath), "assets", "models.json")  // compiled binary
    path.join(__dirname, "../../assets/models.json")                    // dev mode
    
  • Data() uses fetchModelsFromSource with bundledPaths + url, throws on all-sources-failed
  • refresh() uses fetchModelsFromSource with skipCache: true, updates lastResolvedSource
  • sourceInfo() export exposes { source, cachePath, modelsURL, fetchDisabled } for observability
  • Writes cache file only when source is "network_fetch" (bundled/macro data is read-only)

Replay checklist for this section:

  • models-source.ts exists with url in FetchModelsOptions
  • bundledPaths has both paths (execPath + __dirname)
  • Data() and refresh() both use fetchModelsFromSource
  • No fetchApi function or Installation import
  • sourceInfo() is exported

1.2 ACP Session Model Caching & Logging

Modified: src/acp/agent.ts

  • MODEL_CACHE_TTL = 30_000 (30s) per-directory model resolution cache
  • defaultModel() has per-branch structured logging with totalMs timing:
    • config.defaultModel / memory_cache / user_config_validated / user_config_no_providers / opencode_big_pickle / opencode_best / provider_sort_best / user_config_fallback / hard_fallback
  • loadSessionMode logs loadSessionMode.model_source with full source metadata
  • Import: import { ModelsDev, Provider } from "../provider"

Replay checklist:

  • modelCache Map exists with 30s TTL
  • All defaultModel return paths log source + duration
  • loadSessionMode includes ModelsDev.sourceInfo() log

1.3 TUI Model Interaction

No changes needed — dev baseline is sufficient.

src/cli/cmd/tui/context/local.tsx already has:

  • null-safe agent handling (agent.current() can return undefined)
  • parseModel() local function (replaces removed Provider.parseModel import)
  • variant.selected() / variant.current() separation
  • Filesystem.readJson / writeJson for persistence

1.4 CLI Model Source Display

Modified: src/cli/cmd/models.ts

  • --verbose flag outputs ModelsDev.sourceInfo() fields (source, cache path, URL, fetch status)

Modified: src/cli/cmd/run.ts

  • Non-JSON mode shows ~ models source: <source> (<url>) on startup
  • Import: import { ModelsDev, Provider } from "../../provider"

Replay checklist:

  • models --verbose prints source info
  • run shows source hint in non-JSON mode

1.5 Model Adaptation — Compatible Providers

Modified: src/provider/provider.ts

Three additions to the custom() function:

a) anthropic enhanced with token limit overrides:

  • Changed from Effect.succeed to Effect.fnUntraced to access env
  • Injects max_tokens from OPENCODE_MAX_TOKENS env / provider.options.max_tokens
  • Injects max_context_tokens from OPENCODE_MAX_CONTEXT_TOKENS env / provider.options.max_context_tokens

b) anthropic-compatible loader (new):

  • Env: OPENCODE_ANTHROPIC_API_BASE / ANTHROPIC_BASE_URL, OPENCODE_ANTHROPIC_API_KEY / ANTHROPIC_API_KEY
  • /v1 suffix auto-normalization
  • autoload gate: !!(baseURL || apiKey)
  • getModel fallback chain: sdk.languageModel(modelID)sdk(modelID) → recreate @ai-sdk/anthropic
  • Includes max_tokens/max_context_tokens support

c) openai-compatible loader (new):

  • Env priority: OPENCODE_OPENAI_API_BASEOPENCODE_API_BASEOPENAI_BASE_URL
  • Env priority: OPENCODE_OPENAI_API_KEYOPENCODE_API_KEYOPENAI_API_KEY
  • autoload gate: !!(baseURL && apiKey) (both required)
  • getModel fallback chain: sdk.chatModel(modelID)sdk(modelID)sdk.languageModel(modelID) → recreate @ai-sdk/openai-compatible
  • Includes max_tokens/max_context_tokens support

Replay checklist:

  • anthropic uses Effect.fnUntraced, has max_tokens/max_context_tokens injection
  • anthropic-compatible entry in custom() return object
  • openai-compatible entry in custom() return object
  • Both have /v1 normalization (anthropic-compatible) or proper env priority (openai-compatible)

Batch 2 — Routes, Skills, Logging (NOT NEEDED)

All files match origin/dev baseline. No custom changes required:

  • src/server/routes/project.ts
  • src/server/routes/pty.ts
  • src/skill/skill.ts
  • src/util/log.ts

Batch 3 — Tests, Prompts (NOT NEEDED)

All files match origin/dev baseline. No custom changes required:

  • test/acp/system-prompt.test.ts
  • test/mcp/connection-reuse.test.ts
  • test/provider/env_config.test.ts
  • src/session/prompt/anthropic-20250930.txt
  • src/session/prompt/codex_header.txt
  • src/session/prompt/anthropic_spoof.txt

Post-Replay Validation

bun run --cwd packages/opencode typecheck

Acceptance criteria:

  • typecheck passes with zero errors
  • ACP session init returns model metadata correctly
  • local model switching works for provider/model pairs
  • invalid model rejected with user-visible warning
  • fallback model selected when configured model unavailable
  • no unresolved merge markers in repo

Execution Notes

  • Keep CI/workflow files on ours.
  • Keep packages/opencode/package.json workspace identity as qimingcode.
  • Never replay full historical deltas. Replay only behavior-relevant slices.
  • After each dev merge: check this doc, replay unmerged batches, update status.

Change Log

  • 2026-04-27: Initial retention document created after merge stabilization.
  • 2026-04-27: Batch 1.1 — models-source.ts created + models.ts refactored (unified source loading, bundledPaths, sourceInfo, url configurability, refresh unification, fail-fast).
  • 2026-04-27: Batch 1.2 — ACP defaultModel caching + branch logging + loadSessionMode.model_source.
  • 2026-04-27: Batch 1.4 — CLI verbose output (models --verbose, run source hint).
  • 2026-04-27: Batch 1.5 — Provider model adaptation: anthropic token overrides, anthropic-compatible, openai-compatible restored in Effect architecture.

Batch 4 — GitHub CI Build & Release (COMPLETED)

4.1 Build-Release Workflow

New file: .github/workflows/build-release.yml (130 lines)

Dedicated build-release workflow for the fork repo (qiming-ai/qimingcode):

  • Tag-triggered cross-compile for 11 platform targets
  • Cross-compile darwin-x64 on ARM runner with smoke test + archive handling
  • Single runner builds all targets (not matrix)
  • Links @opentui/core and @opentui/solid symlinks for build script compatibility
  • Installs opencode workspace deps before build
  • Uses standard GitHub runners (replaces blacksmith runners)
  • Windows bash syntax fixed

4.2 Publish Workflow Simplification

Modified: .github/workflows/publish.yml

  • Drastically simplified (478 lines removed, 559→81)
  • Repository check fixed for qiming-ai/qimingcode
  • Release version derived from package.json, upload loop fixed
  • Allows release to proceed when some build jobs are cancelled
  • Allows publish on feat/qimingcode-qimingclaw-builtin branch
  • Uses macos-13 instead of deprecated macos-13-large

4.3 Pre-Push Hook

Modified: .husky/pre-push

  • Auto-updates models.json before push
  • Aborts push if models content changed (prevents stale model data)
  • Typecheck is non-blocking in pre-push

4.4 Offline Models Loading

New: scripts/update-models.ts

  • Update script to fetch latest models from source

New: packages/opencode/assets/models.json

  • Bundled models snapshot for offline/compiled binary use

Modified: packages/opencode/package.json

  • Added models update script

Replay checklist:

  • .github/workflows/build-release.yml exists
  • .github/workflows/publish.yml uses simplified structure
  • .husky/pre-push has models auto-update logic
  • scripts/update-models.ts exists
  • packages/opencode/assets/models.json exists

Change Log

  • 2026-04-27: Initial retention document created after merge stabilization.
  • 2026-04-27: Batch 1.1 — models-source.ts created + models.ts refactored (unified source loading, bundledPaths, sourceInfo, url configurability, refresh unification, fail-fast).
  • 2026-04-27: Batch 1.2 — ACP defaultModel caching + branch logging + loadSessionMode.model_source.
  • 2026-04-27: Batch 1.4 — CLI verbose output (models --verbose, run source hint).
  • 2026-04-27: Batch 1.5 — Provider model adaptation: anthropic token overrides, anthropic-compatible, openai-compatible restored in Effect architecture.
  • 2026-04-27: Batch 4 — CI build-release workflow, publish simplification, pre-push hook, offline models loading.
  • 2026-04-27: Batch 1 complete. Typecheck passes. Batches 2 & 3 confirmed no-op.