All notable changes to this project are documented here. Format: Keep a Changelog - SemVer.
- Actionable Command Code errors. JSON failure envelopes keep their HTTP status and shape, but the message gains a resolution hint: add credits or another key for 402, pick another model or upgrade for MODEL_NOT_IN_PLAN, replace or rotate for 401/403, wait or rotate for 429, and the Anthropic-surface note for unsupported_model. The status is never rewritten (OpenCode's retry classification depends on it), only JSON bodies are touched, and every decoration is logged to commandcode-errors.log.
- Multi-key rotation (opt-in). With two or more keys from
COMMANDCODE_API_KEYS,COMMANDCODE_KEYS_FILEor~/.config/commandcode/keys.txt, the plugin picks the first key that is not cooling down, so a dead account does not end the turn.402and401/403block a key for 24h,429honoursRetry-Afterup to 6h (5m by default), and5xxnever blames the key. The soonest recovery wins when every key is cooling down. State is labels-only (commandcode-keys-state.json, 0600) and every cooldown is logged tocommandcode-rotation.logwithout secrets. Single-key mode keeps the previous behaviour: the plugin never overrides anAuthorizationheader the host already set.
- Zero data retention passthrough. With
CMD_ZDR=1every request toapi.commandcode.aicarriesx-cmd-zdr: 1, mirroring the Command Code CLI. When the opt-in is absent, any caller-suppliedx-cmd-zdrheader is stripped so a session that never asked for ZDR cannot emit the flag by accident. The header is applied on the strict Command Code host only, never on a lookalike that merely shares the request path.
- Auto-repair for models requiring historical
reasoning_content(such asreflection/beam:free). Prevents HTTP 400 parameter errors in multi-turn assistant and agent workflows by synthesizing compliant thoughts.
- Updated tracked Command Code CLI version signature (
CLI_VERSION) from1.73.4to1.88.0.
- Lowered catalog cache TTL (
CATALOG_TTL_MS) from 12h to 6h so upstream provider rotations, deprecations, and newly added free promo models are discovered twice as fast.
- The declared output limit was hardcoded to 16,384 tokens for every model. OpenCode
forwards
limit.outputasmax_completion_tokens, so every response was capped at 16k even though the catalog supports far more (the DeepSeek V4.1 Flash 1017 build declares 393,216). The default is now 32,768, the highest value that is safe across the whole catalog (the minimum declared output in models.dev's commandcode provider is exactly 32,768), andCOMMANDCODE_OUTPUT_TOKENSoverrides it per process. - The static model entries in
opencode.jsoncarriedoutput: 16384, which took precedence over the plugin. Those limits are now owned by the plugin alone.
- Model ordering in the picker: promo/free models come first (with the runtime's "Free"
badge), then models are grouped by family and, inside each family, the newest version
comes first. Implemented by writing
cost(zero input) andtime.releasedfrom the model editor; the release timestamp encodes the family rank and the version recency. src/catalog.tswith pure helpers (detectFamily,parseVersion,compareVersions,versionScore,buildReleaseDate,isFreeModel,freeCostEntry) and 20 unit tests.
- models.dev publishes its own
commandcodeprovider, which OpenCode merges with the configured one. That merge reintroduced the Anthropic-shapedclaude-*models that answer400 unsupported_modelon the OpenAI-compatible transport. The catalog transform now disables every provider model that is not in the curated set.
- Retry telemetry recorded the model as
undefinedin every single entry. The OpenCode v2retryhook exposes the model asmodel.id/model.providerID, and there is nomodelIDfield, so the previous mapping always producedundefined. Verified against the real event shape captured from a probe server that cuts the stream mid-flight. - The retry log now also records the agent, the attempt number and the backoff delay the runtime chose, which makes it possible to tell an upstream timeout (large gap between entries) from a runtime-side retry loop.
src/telemetry.tswith pure helpers (describeRetryEvent,extractModelId,extractProviderId) and 6 unit tests covering the real event shape.- Troubleshooting section (English and Spanish) on attributing resets to a specific model.
- Installation straight from GitHub with the
github:plugin specifier, verified end to end (clone, cache, load, inference) with no npm publication and no build step. - Troubleshooting section for the plugins-directory auto-discovery asymmetry: a plugin
removed from
opencode.json, or renamed inside~/.config/opencode/plugins/, keeps loading until the directory leavesplugins/(then the watcher drops it live).
- README and configuration docs document three installation paths: GitHub specifier (recommended), local clone, and the auto-discovered plugins directory.
First modular release. Migrates the single-file commandcode-quota plugin into a
structured package with dynamic model discovery.
- Dynamic model discovery from
GET /provider/v1/modelsthrough the OpenCode v2 model editor, with transport-aware filtering and automatic[FREE PROMO]badges for free/promo tiers. - On-disk model catalog cache (12h TTL) so an upstream outage cannot leave the provider with an empty model list at startup.
- Multi-source credential resolution:
COMMAND_CODE_API_KEY,COMMANDCODE_KEY_FILE,$XDG_CONFIG_HOME/commandcode/key.txt,/etc/environment, then the official CLI stores under~/.commandcode/. - Strict origin guard (
isCommandCodeHost) for credential injection, plus theCOMMANDCODE_ALLOWED_HOSTSallow-list opt-in. - Bounded API calls via
AbortSignal.timeout(10s default). - Vision capability metadata (
imageinput) for multimodal models. applyModelCatalogexported for testability.- Offline test suite (58 tests) and an opt-in live suite gated by
COMMANDCODE_LIVE_TESTS. - Strict typecheck configuration (
allowImportingTsExtensions,strict,noUncheckedIndexedAccess) withbun-types. - Documentation in English and Spanish:
README.md/README.es.md,docs/{ARCHITECTURE,CONFIGURATION,TROUBLESHOOTING}.md/docs/es/*,SECURITY,CONTRIBUTINGandCODE_OF_CONDUCTpairs, plusLICENSE.
- State directory, key paths and the connection-close sentinel are resolved from
XDG/home instead of hardcoded/rootpaths. - Retry telemetry now ignores non-Command-Code providers, keeping
commandcode-retries.logmeaningful. - The
retrylog records the provider id and degrades gracefully when the hook is unsupported by the running build.
- Security: credentials were attached based on substring URL matching
(
api.commandcode.aianywhere in the URL, or any URL containing/provider/v1/chat/completions). That leaked the API key to lookalike hosts such asapi.commandcode.ai.evil.com, to any host carrying the provider path, and to values injected in query strings or fragments. - Anthropic-shaped models (
claude-*,/messagesonly) were registered under the OpenAI-compatible transport and failed with400 unsupported_model. They are now filtered out. - Free-model detection missed
-free/_free/.freeid shapes, so e.g.poolside/laguna-s-2.1-freewas not tagged. - The sanitizer had regressed relative to the proven v1 implementation:
nulland empty tool response content was no longer normalized, orphan detection was no longer order-sensitive, and malformedtool_callentries were not pruned. - The sanitizer mutated the caller's message array in place instead of working on a shallow clone.
- The watchdog could fire an abort after a consumer cancellation, and a throwing logging callback could leave the stream open.
- Quota report rendering crashed on a missing window; it now degrades per field.
- Hardcoded user-specific absolute paths removed from the runtime code in favour of XDG/home resolution.
- Network calls from the default test suite.