mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-10-21 14:12:34 +00:00
* fix(opencode): price Vertex turns on a Vertex row and keep an unpriceable recorded cost through the cache (#1547) OpenCode records the cost of each turn. For an id CodeBurn cannot price the parser fell back to that cost, but the session cache stored no cost and re-priced the cached tokens on every read, so the call showed $0. The recorded cost now rides on the cached call as fallbackCostUSD and applies only while the tokens price at $0, so a later catalog row or price override still wins. The session-level rollup used provider/model while per-message turns use the bare id since #1543; it now uses the bare id too. google-vertex and google-vertex-anthropic map to a metered Vertex route. OpenCode and Kilo Code re-parse once; daily cache v37 re-derives settled days. * fix(opencode): keep a declared-free id at $0 instead of its recorded cost A model the user marked flat-rate, priced at a zero rate, or that looks local prices at $0 on purpose. The recorded-cost fallback now skips those ids at parse time and on cache read, through one guard in models.ts.
105 lines
5.5 KiB
Markdown
105 lines
5.5 KiB
Markdown
# OpenCode
|
|
|
|
OpenCode (sst/opencode).
|
|
|
|
- **Source:** `src/providers/opencode.ts`
|
|
- **Loading:** lazy (`src/providers/index.ts:59-75`)
|
|
- **Test:** `tests/providers/opencode.test.ts` (676 lines, the largest provider test)
|
|
|
|
## Where it reads from
|
|
|
|
Default `~/.local/share/opencode/` or `$XDG_DATA_HOME/opencode/`. The discovery walk picks up `opencode*.db` files (`opencode.ts:71-88`).
|
|
|
|
For renamed/forked OpenCode-compatible builds (e.g. MiMoCode writing
|
|
`~/.local/share/mimocode/mimicode.db` with the same `session`/`message`/`part`
|
|
schema), point CodeBurn at the fork's data directory with two env vars:
|
|
|
|
- `OPENCODE_DATA_DIR` — the **exact** data directory (no `opencode` suffix is
|
|
appended). Example: `OPENCODE_DATA_DIR=$HOME/.local/share/mimocode`. Relocates
|
|
both file-based and SQLite storage.
|
|
- `OPENCODE_DB_PREFIX` — the SQLite filename prefix (default `opencode`,
|
|
matching `opencode*.db`). Example: `OPENCODE_DB_PREFIX=mimicode` discovers
|
|
`mimicode*.db`. Affects SQLite discovery only; file-based storage under
|
|
`<OPENCODE_DATA_DIR>/storage/` is found regardless.
|
|
|
|
Precedence when no `dataDir` argument is passed (the production path):
|
|
`OPENCODE_DATA_DIR` → `$XDG_DATA_HOME/opencode` → `~/.local/share/opencode`.
|
|
|
|
## Storage format
|
|
|
|
SQLite (older builds) or file-based JSON (OpenCode 1.1+, under `storage/`).
|
|
|
|
OpenCode 2.x (mainline since 2.0.3, issue #1293) writes a second SQLite
|
|
generation into the same `opencode.db`: `session_v2` + `session_message`
|
|
(messages tagged by a `type` column, ordered by `seq`, payload JSON in `data`;
|
|
assistant rows carry `model: {id, providerID}`, `cost` and `tokens` inline).
|
|
The desktop app (Electron) runs the same CLI as a background service, so CLI
|
|
and GUI sessions land in the same file. On first start 2.x copies 1.x sessions
|
|
into the new tables under their original session and message ids, but the
|
|
legacy `session`/`message`/`part` tables stay and freeze. The copy is lossy:
|
|
it drops compaction summary turns and task-tool turns, skips any message it
|
|
fails to decode, and can stop partway, leaving `session_v2` rows with no
|
|
`session_message` rows.
|
|
|
|
So on a database with both generations the parser reads a session's legacy
|
|
turns first and then adds the `session_message` turns legacy does not have
|
|
(dedup by message id). Pre-upgrade history counts exactly as it did on 1.x and
|
|
post-upgrade turns are added. Legacy sessions missing from `session_v2` are
|
|
discovered from the legacy table. A 2.x-only database reads `session_message`
|
|
alone; a 1.x database runs the legacy path unchanged. v2 model ids are used
|
|
bare (as 1.x stored `modelID`) so a turn prices the same from either table.
|
|
|
|
## Caching
|
|
|
|
None.
|
|
|
|
## Deduplication
|
|
|
|
Per `<sessionId>:<messageId>`.
|
|
|
|
## Billing routes
|
|
|
|
OpenCode stores the transport in each assistant message's `providerID`. CodeBurn
|
|
preserves the exact usage-bearing values across legacy SQLite, v2
|
|
`session_message`, file storage, and session-level rollup fallbacks:
|
|
|
|
- `openrouter` → OpenRouter, metered
|
|
- `amazon-bedrock` → Bedrock, metered
|
|
- `google-vertex`, `google-vertex-anthropic` → Vertex, metered
|
|
|
|
Case or whitespace variants of those values are not inferred. The Bedrock
|
|
model-id detector remains a fallback for recognised Anthropic and OpenAI
|
|
foundation-model ids, but `providerID=amazon-bedrock` also covers model families
|
|
such as Nova whose ids do not match that detector. Direct provider values remain
|
|
unrouted/unknown. OpenCode and KiloCode session-cache fingerprints move whenever
|
|
this shared provider-field mapping changes, so warm and cold reads agree.
|
|
|
|
## Quirks
|
|
|
|
- **Schema validation is loud.** When a required table is missing, the parser logs an actionable warning telling the user which table is gone and what version of OpenCode it expects. This is the right behavior; do not silently swallow these.
|
|
- Source paths are encoded as `<dbPath>:<sessionId>`.
|
|
- Discovery only emits root sessions (`parent_id IS NULL`) to avoid double
|
|
counting. Parsing a root session walks the whole `session.parent_id` subtree,
|
|
archived children included — OpenCode's archive is organizational, the rows
|
|
stay — so child and grandchild agent sessions contribute their message,
|
|
token, and tool usage back to the root session.
|
|
- Each message's `parts` are indexed; preserving the order matters for reasoning-token correctness.
|
|
- Tokens are reported across `input`, `output`, `reasoning`, `cache.read`, and `cache.write`. Anthropic semantics.
|
|
- A turn or session rollup whose model CodeBurn cannot price keeps OpenCode's
|
|
recorded `cost` (as `fallbackCostUSD` on the cached call), unless the user
|
|
declared the id free (`model-flat-rate`, a zero-rate price override, a local
|
|
model). Tokens still win whenever the id prices, including after a catalog
|
|
update or price override.
|
|
- Assistant messages with missing router usage are kept as zero-cost calls
|
|
when their parts contain non-empty text or tool activity. Empty zero-usage
|
|
assistant placeholders are still skipped.
|
|
- External MCP tools are stored as `<server>_<tool>` names (for example
|
|
`clickup_clickup_get_task`). The provider normalizes those to CodeBurn's
|
|
canonical `mcp__<server>__<tool>` names before aggregation so shared MCP
|
|
panels and `optimize` findings count OpenCode usage.
|
|
|
|
## When fixing a bug here
|
|
|
|
1. The 558-line test suite catches a lot. Run `npx vitest run tests/providers/opencode.test.ts` before and after any change.
|
|
2. If the bug is "missing table" warning, do not catch and silence it. Either upgrade the version expectation in the parser or document the breaking schema change.
|
|
3. If the bug is "reasoning tokens off by one", check the parts index ordering.
|