merge main into fix/tool-call-streaming

This commit is contained in:
Patrick Wozniak
2026-08-25 17:18:02 +02:00
37 changed files with 1022 additions and 207 deletions
@@ -9,6 +9,8 @@ import {
COMMAND_CODE_CLI_VERSION,
MODEL_EFFORTS,
MODEL_INPUT_MODALITIES,
MODEL_MAX_OUTPUT_TOKENS,
MODEL_REASONING,
} from "../../src/commandcode-catalog.ts"
const execFileAsync = promisify(execFile)
@@ -21,7 +23,9 @@ const README_PATH = new URL("../../README.md", import.meta.url)
export interface CommandCodeModelMetadata {
imageModelIds: readonly string[]
reasoningModelIds: readonly string[]
reasoningEfforts: Readonly<Record<string, readonly string[]>>
maxOutputTokens: Readonly<Record<string, number>>
}
export interface ModelMetadataDiff {
@@ -30,7 +34,12 @@ export interface ModelMetadataDiff {
removedImageModelIds: readonly string[]
addedReasoningModelIds: readonly string[]
removedReasoningModelIds: readonly string[]
changedReasoningModelIds: readonly string[]
addedEffortModelIds: readonly string[]
removedEffortModelIds: readonly string[]
changedEffortModelIds: readonly string[]
addedMaxOutputModelIds: readonly string[]
removedMaxOutputModelIds: readonly string[]
changedMaxOutputModelIds: readonly string[]
}
interface PackedPackage {
@@ -123,27 +132,93 @@ export function parseKnownTextOnlyModelIds(bundle: string): readonly string[] {
return sorted(new Set(parsed))
}
function modelObject(bundle: string, modelId: string): string {
const start = bundle.indexOf(`{id:${JSON.stringify(modelId)},inputModalities:`)
if (start < 0) throw new Error(`Could not find model metadata for ${modelId}`)
let depth = 0
let quote = ""
let escaped = false
for (let index = start; index < bundle.length; index += 1) {
const character = bundle[index] ?? ""
if (quote) {
if (escaped) escaped = false
else if (character === "\\") escaped = true
else if (character === quote) quote = ""
continue
}
if (character === '"' || character === "'" || character === "`") {
quote = character
continue
}
if (character === "{") depth += 1
else if (character === "}" && --depth === 0) return bundle.slice(start, index + 1)
}
throw new Error(`Unterminated model metadata for ${modelId}`)
}
export function parseBundleModelCapabilities(
bundle: string,
modelIds: readonly string[],
): {
reasoningModelIds: readonly string[]
maxOutputTokens: Readonly<Record<string, number>>
} {
const reasoningModelIds: string[] = []
const maxOutputTokens: Record<string, number> = {}
for (const modelId of modelIds) {
const entry = modelObject(bundle, modelId)
if (entry.includes("reasoning:!0") || entry.includes("reasoningEfforts:[")) {
reasoningModelIds.push(modelId)
}
const maxOutput = /maxOutputTokens:([^,}]+)/.exec(entry)?.[1]
if (maxOutput) {
const value = Number(maxOutput)
if (!Number.isFinite(value) || value <= 0) {
throw new Error(`Unexpected max output tokens for ${modelId}: ${maxOutput}`)
}
maxOutputTokens[modelId] = value
}
}
return {
reasoningModelIds: sorted(reasoningModelIds),
maxOutputTokens: Object.fromEntries(
Object.entries(maxOutputTokens).sort(([left], [right]) => left.localeCompare(right)),
),
}
}
export function commandCodeModelMetadataFromContents(
modelsReference: string,
cliBundle: string,
): CommandCodeModelMetadata {
const reference = parseModelsReference(modelsReference)
const textOnlyModelIds = new Set(parseKnownTextOnlyModelIds(cliBundle))
const capabilities = parseBundleModelCapabilities(cliBundle, reference.modelIds)
return {
imageModelIds: reference.modelIds.filter((modelId) => !textOnlyModelIds.has(modelId)),
reasoningModelIds: capabilities.reasoningModelIds,
reasoningEfforts: reference.reasoningEfforts,
maxOutputTokens: capabilities.maxOutputTokens,
}
}
export function currentModelMetadata(): CommandCodeModelMetadata {
return {
imageModelIds: sorted(Object.keys(MODEL_INPUT_MODALITIES)),
reasoningModelIds: sorted(Object.keys(MODEL_REASONING)),
reasoningEfforts: Object.fromEntries(
Object.entries(MODEL_EFFORTS)
.sort(([left], [right]) => left.localeCompare(right))
.map(([modelId, efforts]) => [modelId, [...efforts]]),
),
maxOutputTokens: Object.fromEntries(
Object.entries(MODEL_MAX_OUTPUT_TOKENS).sort(([left], [right]) => left.localeCompare(right)),
),
}
}
@@ -155,10 +230,16 @@ export function diffModelMetadata(
): ModelMetadataDiff {
const currentImages = new Set(current.imageModelIds)
const upstreamImages = new Set(upstream.imageModelIds)
const currentReasoningIds = Object.keys(current.reasoningEfforts)
const upstreamReasoningIds = Object.keys(upstream.reasoningEfforts)
const currentReasoningSet = new Set(currentReasoningIds)
const upstreamReasoningSet = new Set(upstreamReasoningIds)
const currentReasoning = new Set(current.reasoningModelIds)
const upstreamReasoning = new Set(upstream.reasoningModelIds)
const currentEffortIds = Object.keys(current.reasoningEfforts)
const upstreamEffortIds = Object.keys(upstream.reasoningEfforts)
const currentEffortSet = new Set(currentEffortIds)
const upstreamEffortSet = new Set(upstreamEffortIds)
const currentMaxOutputIds = Object.keys(current.maxOutputTokens)
const upstreamMaxOutputIds = Object.keys(upstream.maxOutputTokens)
const currentMaxOutputSet = new Set(currentMaxOutputIds)
const upstreamMaxOutputSet = new Set(upstreamMaxOutputIds)
return {
versionChanged: currentVersion !== upstreamVersion,
@@ -169,19 +250,38 @@ export function diffModelMetadata(
current.imageModelIds.filter((modelId) => !upstreamImages.has(modelId)),
),
addedReasoningModelIds: sorted(
upstreamReasoningIds.filter((modelId) => !currentReasoningSet.has(modelId)),
upstream.reasoningModelIds.filter((modelId) => !currentReasoning.has(modelId)),
),
removedReasoningModelIds: sorted(
currentReasoningIds.filter((modelId) => !upstreamReasoningSet.has(modelId)),
current.reasoningModelIds.filter((modelId) => !upstreamReasoning.has(modelId)),
),
changedReasoningModelIds: sorted(
upstreamReasoningIds.filter(
addedEffortModelIds: sorted(
upstreamEffortIds.filter((modelId) => !currentEffortSet.has(modelId)),
),
removedEffortModelIds: sorted(
currentEffortIds.filter((modelId) => !upstreamEffortSet.has(modelId)),
),
changedEffortModelIds: sorted(
upstreamEffortIds.filter(
(modelId) =>
currentReasoningSet.has(modelId) &&
currentEffortSet.has(modelId) &&
JSON.stringify(current.reasoningEfforts[modelId]) !==
JSON.stringify(upstream.reasoningEfforts[modelId]),
),
),
addedMaxOutputModelIds: sorted(
upstreamMaxOutputIds.filter((modelId) => !currentMaxOutputSet.has(modelId)),
),
removedMaxOutputModelIds: sorted(
currentMaxOutputIds.filter((modelId) => !upstreamMaxOutputSet.has(modelId)),
),
changedMaxOutputModelIds: sorted(
upstreamMaxOutputIds.filter(
(modelId) =>
currentMaxOutputSet.has(modelId) &&
current.maxOutputTokens[modelId] !== upstream.maxOutputTokens[modelId],
),
),
}
}
@@ -229,14 +329,24 @@ export function renderCommandCodeCatalog(
const imageEntries = sorted(metadata.imageModelIds)
.map((modelId) => ` ${quoted(modelId)}: ["text", "image"],`)
.join("\n")
const reasoningEntries = recordEntries(metadata.reasoningEfforts)
const reasoningEntries = sorted(metadata.reasoningModelIds)
.map((modelId) => ` ${quoted(modelId)}: true,`)
.join("\n")
const effortEntries = recordEntries(metadata.reasoningEfforts)
.map(
([modelId, efforts]) =>
` ${quoted(modelId)}: [${efforts.map((effort) => quoted(effort)).join(", ")}],`,
)
.join("\n")
const maxOutputEntries = Object.entries(metadata.maxOutputTokens)
.sort(([left], [right]) => left.localeCompare(right))
.map(
([modelId, value]) =>
` ${quoted(modelId)}: ${value.toLocaleString("en-US").replaceAll(",", "_")},`,
)
.join("\n")
return `export const COMMAND_CODE_CLI_VERSION = ${quoted(packageVersion)}\n\nexport type CommandCodeInputType = "text" | "image"\nexport type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max"\n\n/**\n * Generated from command-code@${packageVersion} by \`npm run sync:commandcode-catalog\`.\n * Do not edit manually.\n */\nexport const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>> = {\n${imageEntries}\n}\n\nexport const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {\n${reasoningEntries}\n}\n`
return `export const COMMAND_CODE_CLI_VERSION = ${quoted(packageVersion)}\n\nexport type CommandCodeInputType = "text" | "image"\nexport type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max"\n\n/**\n * Generated from command-code@${packageVersion} by \`npm run sync:commandcode-catalog\`.\n * Do not edit manually.\n */\nexport const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>> = {\n${imageEntries}\n}\n\nexport const MODEL_REASONING: Readonly<Record<string, true>> = {\n${reasoningEntries}\n}\n\nexport const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {\n${effortEntries}\n}\n\nexport const MODEL_MAX_OUTPUT_TOKENS: Readonly<Record<string, number>> = {\n${maxOutputEntries}\n}\n`
}
function updateDocumentedCatalogVersion(
@@ -279,16 +389,23 @@ function metadataReport(
`- Repository snapshot: \`command-code@${COMMAND_CODE_CLI_VERSION}\``,
`- Inspected package: \`command-code@${packageVersion}\``,
`- Image-capable models: ${current.imageModelIds.length} repository / ${upstream.imageModelIds.length} upstream`,
`- Reasoning models: ${Object.keys(current.reasoningEfforts).length} repository / ${Object.keys(upstream.reasoningEfforts).length} upstream`,
`- Reasoning models: ${current.reasoningModelIds.length} repository / ${upstream.reasoningModelIds.length} upstream`,
`- Models with selectable efforts: ${Object.keys(current.reasoningEfforts).length} repository / ${Object.keys(upstream.reasoningEfforts).length} upstream`,
`- Model-specific output limits: ${Object.keys(current.maxOutputTokens).length} repository / ${Object.keys(upstream.maxOutputTokens).length} upstream`,
"",
"| Change | Models |",
"| --- | --- |",
`| CLI version | ${diff.versionChanged ? `\`${COMMAND_CODE_CLI_VERSION}\`\`${packageVersion}\`` : "Current"} |`,
`| New image support | ${formatList(diff.addedImageModelIds)} |`,
`| Removed image support | ${formatList(diff.removedImageModelIds)} |`,
`| New reasoning metadata | ${formatList(diff.addedReasoningModelIds)} |`,
`| Removed reasoning metadata | ${formatList(diff.removedReasoningModelIds)} |`,
`| Changed reasoning efforts | ${formatReasoningChanges(diff.changedReasoningModelIds, current, upstream)} |`,
`| New reasoning models | ${formatList(diff.addedReasoningModelIds)} |`,
`| Removed reasoning models | ${formatList(diff.removedReasoningModelIds)} |`,
`| New effort metadata | ${formatList(diff.addedEffortModelIds)} |`,
`| Removed effort metadata | ${formatList(diff.removedEffortModelIds)} |`,
`| Changed reasoning efforts | ${formatReasoningChanges(diff.changedEffortModelIds, current, upstream)} |`,
`| New output limits | ${formatList(diff.addedMaxOutputModelIds)} |`,
`| Removed output limits | ${formatList(diff.removedMaxOutputModelIds)} |`,
`| Changed output limits | ${formatList(diff.changedMaxOutputModelIds)} |`,
"",
].join("\n")
}
+2 -1
View File
@@ -79,7 +79,8 @@ jobs:
This updates only machine-readable compatibility metadata:
- CLI version used in the `x-command-code-version` header
- image-input capabilities
- supported reasoning efforts
- reasoning capability and selectable effort levels
- model-specific maximum output limits
- documented catalog snapshot version
Pricing remains review-only because CLI documentation does not represent every pricing tier and temporary promotion used by the provider.
+2 -3
View File
@@ -256,10 +256,10 @@ rules:
- pattern: process.env.$VAR
- metavariable-regex:
metavariable: $VAR
regex: "(?!COMMANDCODE_|NODE_|PATH|HOME|SHELL|USER|LANG|LC_|TERM|TMPDIR|NIX_).*"
regex: "(?!COMMANDCODE_|COMMAND_CODE_|CMD_ZDR|NODE_|PATH|HOME|SHELL|USER|LANG|LC_|TERM|TMPDIR|NIX_).*"
message: >
Reading unexpected environment variable $VAR. Provider should only
read COMMANDCODE_* variables.
read documented Command Code or standard runtime variables.
severity: WARNING
languages: [javascript, typescript]
paths:
@@ -270,4 +270,3 @@ rules:
# ────────────────────────────────────────────────────────────────────────
# OAuth flow manipulation
# ────────────────────────────────────────────────────────────────────────
+10 -4
View File
@@ -3,17 +3,23 @@
## Unreleased
- Stream incremental tool-call arguments from the `/alpha/generate` transport instead of waiting for the final complete tool-call event.
- Add a daily GitHub Actions synchronization job that opens or updates a pull request for CLI version, image capability, and reasoning-effort changes in the latest published Command Code catalog.
- Refresh static model capabilities from `command-code@1.32.2`, including new image and reasoning metadata.
- Add a daily GitHub Actions synchronization job that opens or updates a pull request for CLI version, image capability, reasoning, effort, and output-limit changes in the latest published Command Code catalog.
- Refresh static model capabilities from `command-code@1.32.2`, separating reasoning support from selectable effort levels and honoring model-specific output limits.
- Reject truncated, aborted, and network-failed generate streams instead of reporting partial responses as successful.
- Normalize malformed tool results and synthesize missing tool results so follow-up requests preserve valid tool-call history.
- Refresh display pricing for all 58 current models, including Gemini 3.7 Flash, Qwen 3.8 27B, Ox Alpha, Muse Spark 1.2, and Grok 4.6 long-context rates.
- Accept the official `COMMAND_CODE_API_KEY` and `CMD_ZDR` environment variables while retaining legacy aliases.
- Align generate request metadata with the CLI by forwarding stable session IDs, optional temperature, and the CLI user agent.
- Validate manually pasted API keys, use the CLI's two-minute browser timeout, and reject OAuth state mismatches without closing the callback server.
- Add `/commandcode-quota` with live credits, plan, usage totals, and rolling-limit diagnostics from Command Code's alpha usage endpoints.
- Add `zai-org/GLM-5.3` with its verified reasoning efforts and display pricing.
- Prefer Command Code's Provider API (`/provider/v1/chat/completions` and `/provider/v1/messages`) and automatically fall back to the existing `/alpha/generate` transport only when the Provider API returns `403 upgrade_required` for a Go-plan account.
- Remember the detected transport for the running process, re-detect it when credentials change, prevent stale in-flight requests from overwriting the new credential's transport, and never fall back for unrelated authentication, permission, rate-limit, network, or server failures.
- Use Pi's native OpenAI- and Anthropic-compatible providers for Provider API streaming, including adaptive thinking for current reasoning-capable Claude models, while preserving the existing hardened generate transport, dynamic model discovery, offline cache, refresh/status commands, pricing, and OAuth credentials.
- Let `/login` use browser authentication, an explicit API-key prompt, or a directly pasted API key.
- Add optional zero-data-retention headers through `COMMANDCODE_ZDR=1`.
- Add optional zero-data-retention headers through `CMD_ZDR=1` and the legacy `COMMANDCODE_ZDR=1` alias.
- Refresh GPT-5.6 Terra and Luna display prices after their temporary 50% promotion ended, and display the current DeepSeek V4 off-peak rates for its time-dependent pricing.
- Add isolated live E2E profiles for separate Go-plan and Provider-API credentials, including an explicit selected-transport assertion and packed-package validation.
- Add isolated live E2E profiles for separate Go-, GOAT-, and Provider-plan credentials, covering transport selection, reasoning across turns, quota identity, aborts, tools, GOAT vision, Go image rejection, and packed-package validation.
- Fix extension load failure on newer pi hosts that reject registering a custom API under a built-in name (`openai-completions`); register under `commandcode-custom` instead and restore the real wire API before native compat dispatch.
## 0.5.1 - 2026-08-11
+2 -1
View File
@@ -46,10 +46,11 @@ Run the transport-specific live tests with separate credentials:
```sh
COMMANDCODE_E2E_GO_API_KEY_FILE=/path/to/go-key npm run test:e2e:live:go
COMMANDCODE_E2E_GOAT_API_KEY_FILE=/path/to/goat-key npm run test:e2e:live:goat
COMMANDCODE_E2E_PROVIDER_API_KEY_FILE=/path/to/provider-key npm run test:e2e:live:provider
```
Use `npm run test:e2e:live:all` with both file variables to run them sequentially. Store the keys in a secret manager and export each one to a new mode-`0600` temporary file for the test; never add key files to the repository. The direct `COMMANDCODE_E2E_GO_API_KEY` and `COMMANDCODE_E2E_PROVIDER_API_KEY` variables are intended primarily for protected CI secrets.
Use `npm run test:e2e:live:all` with the Go and GOAT file variables to run both subscription transports sequentially. Store keys in a secret manager and export each one to a new mode-`0600` temporary file for the test; never add key files to the repository. Direct `*_API_KEY` variables are intended primarily for protected CI secrets.
Before opening a PR, run:
+11 -17
View File
@@ -7,15 +7,6 @@ A custom provider for [pi](https://github.com/earendil-works/pi) that connects t
> **Disclaimer:** This is an unofficial, community-maintained integration. It is not affiliated with, endorsed by, or supported by Command Code. You need your own Command Code account, API key, and a plan with Provider API access. Command Code's terms, availability, and pricing apply.
The extension uses one provider and automatically selects the transport supported by the authenticated account:
- `GET /provider/v1/models` for model discovery
- `POST /provider/v1/chat/completions` for non-Claude models with Provider API access
- `POST /provider/v1/messages` for Claude models with Provider API access
- `/alpha/generate` after the Provider API explicitly returns `403 upgrade_required`, which currently identifies Go-plan accounts
The detected transport is remembered only for the running process and is re-evaluated when the credential changes. Other authentication, permission, rate-limit, network, and server errors never trigger the fallback.
## Install
```sh
@@ -53,7 +44,7 @@ If automatic transfer from the browser fails, copy the API key shown by Command
### Environment variable
```sh
export COMMANDCODE_API_KEY="user_..."
export COMMAND_CODE_API_KEY="user_..."
```
### Auth file
@@ -93,7 +84,7 @@ Open `/model` and select one of the models provided by Command Code. Model avail
### Reasoning support
Reasoning metadata is enriched only for models whose Command Code effort support is known. Those models register a model-specific `thinkingLevelMap`, so pi and OMP expose only supported levels. Pi's native OpenAI- and Anthropic-compatible providers translate the selected level for Provider API accounts; the existing Command Code generate transport sends the matching `reasoning_effort` for Go accounts. Unsupported levels and newly discovered models without metadata do not claim reasoning support.
Reasoning capability and selectable effort levels follow the official CLI catalog independently. Models can therefore be marked as reasoning-capable even when Command Code chooses their depth automatically. Models with explicit effort support also register a model-specific `thinkingLevelMap`, so pi and OMP expose only valid levels. Pi's native OpenAI- and Anthropic-compatible providers translate the selected level for Provider API accounts; the existing Command Code generate transport sends the matching `reasoning_effort` for Go accounts.
List Command Code models from the terminal:
@@ -133,7 +124,7 @@ While pi is running, use these provider commands without restarting:
The `commandcode-quota` command reads from the Command Code alpha usage endpoints (the same ones the `cmd` CLI `/usage` command uses): `whoami`, `billing/credits`, `billing/subscriptions`, and `usage/summary`. It authenticates with the same API key the provider already uses. If the command cannot reach those endpoints or an endpoint schema changes, unavailable sections are reported explicitly instead of being displayed as zero usage. Output is plain text (via `ui.notify`) so it works across pi and compatible hosts such as OMP.
Set `COMMANDCODE_ZDR=1` to send Command Code's documented `x-cmd-zdr: 1` zero-data-retention header.
Set `CMD_ZDR=1` to send Command Code's documented `x-cmd-zdr: 1` zero-data-retention header. The legacy `COMMANDCODE_ZDR=1` alias remains supported.
The following environment variables are intended for tests, local mocks, and compatible API endpoints:
@@ -144,7 +135,7 @@ The following environment variables are intended for tests, local mocks, and com
## Image input
The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. A daily GitHub Actions job synchronizes the CLI version, image capabilities, and reasoning efforts with the latest published CLI package and opens or updates a reviewable pull request when they change. Pricing remains manually reviewed because the CLI catalog does not expose every pricing tier and temporary promotion used by the provider.
The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. A daily GitHub Actions job synchronizes the CLI version, image capabilities, reasoning flags, reasoning efforts, and model-specific output limits with the latest published CLI package and opens or updates a reviewable pull request when they change. Pricing remains manually reviewed because temporary promotions and long-context tiers require explicit review.
For vision-capable models, Pi's native provider adapters forward image blocks from user messages and tool results using the documented OpenAI or Anthropic message schema. Unknown and text-only models remain marked text-only in Pi.
@@ -195,23 +186,26 @@ Both commands accept additional pi arguments after `--`, for example `npm run pi
### Live transport tests
Keep the Go-plan and Provider-API test keys in separate secret-manager entries. Pass them through protected files so the keys do not enter shell history:
Keep Go-, GOAT-, and optional Provider-plan test keys in separate secret-manager entries. Pass them through protected files so the keys do not enter shell history:
```sh
COMMANDCODE_E2E_GO_API_KEY_FILE=/path/to/go-key \
npm run test:e2e:live:go
COMMANDCODE_E2E_GOAT_API_KEY_FILE=/path/to/goat-key \
npm run test:e2e:live:goat
COMMANDCODE_E2E_PROVIDER_API_KEY_FILE=/path/to/provider-key \
npm run test:e2e:live:provider
COMMANDCODE_E2E_GO_API_KEY_FILE=/path/to/go-key \
COMMANDCODE_E2E_PROVIDER_API_KEY_FILE=/path/to/provider-key \
COMMANDCODE_E2E_GOAT_API_KEY_FILE=/path/to/goat-key \
npm run test:e2e:live:all
```
Each profile runs with an isolated Pi agent directory and asserts the selected transport through `/commandcode-status`: Go must select `generate`, while a Provider API account must select `provider`. The profile-specific `*_API_KEY` environment variables are also supported for CI secrets, but key files are preferred for local use.
Each profile runs with an isolated Pi agent directory and asserts transport selection, reasoning across turns, quota plan identity, abort handling, tool calls, and the packed npm artifact. Go must select `generate` and reject unsupported images; GOAT must select `provider` and complete a live vision request. The profile-specific `*_API_KEY` environment variables are also supported for CI secrets, but key files are preferred for local use.
Override the default DeepSeek test model with `COMMANDCODE_E2E_GO_MODEL` or `COMMANDCODE_E2E_PROVIDER_MODEL`. A successful live Anthropic `/provider/v1/messages` test requires a Provider API account whose plan includes the selected Claude model.
The Go profile defaults to DeepSeek V4 Flash; GOAT defaults to Grok 4.6 because its Provider API stream exposes reasoning consistently across consecutive turns. Override them with `COMMANDCODE_E2E_GO_MODEL`, `COMMANDCODE_E2E_GOAT_MODEL`, or `COMMANDCODE_E2E_PROVIDER_MODEL`. A successful live Anthropic `/provider/v1/messages` test requires a paid account whose plan includes the selected Claude model.
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup and tests. See [RELEASE.md](RELEASE.md) for the release process.
+4 -3
View File
@@ -26,6 +26,7 @@ import {
getModelsTimeoutMs,
inputModalitiesForModel,
loadCommandCodeModels,
MODEL_EFFORTS,
thinkingMetadataForModel,
type CommandCodeModel,
} from "./src/models.ts"
@@ -37,7 +38,7 @@ import { createCommandCodeRuntime } from "./src/runtime.ts"
import { createCommandCodeTransportRouter } from "./src/transport.ts"
function commandCodeHeaders(): Record<string, string> | undefined {
if (process.env.COMMANDCODE_ZDR === "1") {
if (process.env.CMD_ZDR === "1" || process.env.COMMANDCODE_ZDR === "1") {
return { "x-cmd-zdr": "1" }
}
return undefined
@@ -52,7 +53,7 @@ function createProviderConfig(
return {
name: "Command Code",
baseUrl: apiBase,
apiKey: getConfiguredApiKey() ?? "$COMMANDCODE_API_KEY",
apiKey: getConfiguredApiKey() ?? "$COMMAND_CODE_API_KEY",
api: "commandcode-custom",
streamSimple: streamCommandCode,
headers,
@@ -79,7 +80,7 @@ function createProviderConfig(
? {
supportsStore: false,
supportsDeveloperRole: false,
supportsReasoningEffort: true,
supportsReasoningEffort: MODEL_EFFORTS[model.id] !== undefined,
maxTokensField: "max_tokens",
}
: {
+2 -1
View File
@@ -55,8 +55,9 @@
"test:smoke": "node tests/test-smoke.mjs",
"test:e2e:live": "node tests/test-live-e2e.mjs",
"test:e2e:live:go": "node scripts/live-e2e-profile.mjs go",
"test:e2e:live:goat": "node scripts/live-e2e-profile.mjs goat",
"test:e2e:live:provider": "node scripts/live-e2e-profile.mjs provider",
"test:e2e:live:all": "node scripts/live-e2e-profile.mjs go provider",
"test:e2e:live:all": "node scripts/live-e2e-profile.mjs go goat",
"test:cost": "tsx tests/test-cost.ts"
},
"pi": {
+19 -6
View File
@@ -11,14 +11,19 @@ const profiles = process.argv.slice(2)
if (
profiles.length === 0 ||
profiles.some((profile) => profile !== "go" && profile !== "provider")
profiles.some((profile) => profile !== "go" && profile !== "goat" && profile !== "provider")
) {
console.error("Usage: node scripts/live-e2e-profile.mjs <go|provider> [go|provider]")
console.error("Usage: node scripts/live-e2e-profile.mjs <go|goat|provider> [go|goat|provider]")
process.exit(2)
}
async function credentialFor(profile) {
const prefix = profile === "go" ? "COMMANDCODE_E2E_GO" : "COMMANDCODE_E2E_PROVIDER"
const prefix =
profile === "go"
? "COMMANDCODE_E2E_GO"
: profile === "goat"
? "COMMANDCODE_E2E_GOAT"
: "COMMANDCODE_E2E_PROVIDER"
const direct = process.env[`${prefix}_API_KEY`]?.trim()
const file = process.env[`${prefix}_API_KEY_FILE`]
@@ -35,15 +40,23 @@ async function credentialFor(profile) {
function runProfile(profile, apiKey) {
const modelVariable =
profile === "go" ? "COMMANDCODE_E2E_GO_MODEL" : "COMMANDCODE_E2E_PROVIDER_MODEL"
const model = process.env[modelVariable] ?? "deepseek/deepseek-v4-flash"
profile === "go"
? "COMMANDCODE_E2E_GO_MODEL"
: profile === "goat"
? "COMMANDCODE_E2E_GOAT_MODEL"
: "COMMANDCODE_E2E_PROVIDER_MODEL"
const model =
process.env[modelVariable] ??
(profile === "goat" ? "xai/grok-4.6" : "deepseek/deepseek-v4-flash")
const env = {
...process.env,
COMMANDCODE_API_KEY: apiKey,
COMMAND_CODE_API_KEY: apiKey,
COMMANDCODE_E2E_MODEL: model,
COMMANDCODE_E2E_PROFILE: profile,
}
delete env.COMMANDCODE_API_KEY
delete env.COMMANDCODE_E2E_GO_API_KEY
delete env.COMMANDCODE_E2E_GOAT_API_KEY
delete env.COMMANDCODE_E2E_PROVIDER_API_KEY
return new Promise((resolveRun, reject) => {
+1
View File
@@ -11,6 +11,7 @@ const env = {
...process.env,
PI_SKIP_VERSION_CHECK: "1",
}
delete env.COMMAND_CODE_API_KEY
delete env.COMMANDCODE_API_KEY
const child = spawn(
+1
View File
@@ -22,6 +22,7 @@ const env = {
PI_CODING_AGENT_SESSION_DIR: sessionDir,
PI_SKIP_VERSION_CHECK: "1",
}
delete env.COMMAND_CODE_API_KEY
delete env.COMMANDCODE_API_KEY
let activeChild
+1
View File
@@ -34,6 +34,7 @@ export function getConfiguredApiKey(
} = {},
): string | undefined {
const env = options.env ?? process.env
if (env.COMMAND_CODE_API_KEY) return env.COMMAND_CODE_API_KEY
if (env.COMMANDCODE_API_KEY) return env.COMMANDCODE_API_KEY
const home = options.homeDir?.() ?? homedir()
+7
View File
@@ -28,6 +28,7 @@ export interface AuthServer {
export interface AuthServerOptions {
startPort?: number
portRange?: number
expectedState?: string
}
function listenOnAvailablePort(
@@ -181,6 +182,12 @@ export async function startAuthServer(options: AuthServerOptions = {}): Promise<
return
}
if (options.expectedState !== undefined && state !== options.expectedState) {
res.writeHead(403)
res.end(JSON.stringify({ success: false, error: "Invalid state token" }))
return
}
res.writeHead(200)
res.end(JSON.stringify({ success: true }))
+57
View File
@@ -51,6 +51,57 @@ export const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCod
"xiaomi/mimo-v2.5": ["text", "image"],
}
export const MODEL_REASONING: Readonly<Record<string, true>> = {
"claude-fable-5": true,
"claude-opus-4-7": true,
"claude-opus-4-8": true,
"claude-opus-5": true,
"claude-sonnet-4-6": true,
"claude-sonnet-5": true,
"deepseek/deepseek-v4-flash": true,
"deepseek/deepseek-v4-flash-vision-exp": true,
"deepseek/deepseek-v4-pro": true,
"google/gemini-3.1-flash-lite": true,
"google/gemini-3.5-flash": true,
"google/gemini-3.5-flash-lite": true,
"google/gemini-3.6-flash": true,
"google/gemini-3.7-flash": true,
"gpt-5.3-codex": true,
"gpt-5.4": true,
"gpt-5.4-mini": true,
"gpt-5.5": true,
"gpt-5.6-luna": true,
"gpt-5.6-sol": true,
"gpt-5.6-terra": true,
"meta/muse-spark-1.1": true,
"meta/muse-spark-1.2": true,
"meta/muse-spark-1.2-contributor": true,
"MiniMaxAI/MiniMax-M3": true,
"moonshotai/Kimi-K2.7-Code": true,
"moonshotai/Kimi-K2.7-Code-Highspeed": true,
"moonshotai/Kimi-K3": true,
"nvidia/nemotron-3-ultra-550b-a55b": true,
"poolside/laguna-s-2.1-free": true,
"Qwen/Qwen3.6-Max-Preview": true,
"Qwen/Qwen3.6-Plus": true,
"Qwen/Qwen3.7-Flash": true,
"Qwen/Qwen3.7-Max": true,
"Qwen/Qwen3.7-Plus": true,
"Qwen/Qwen3.8-27B": true,
"Qwen/Qwen3.8-Max": true,
"sakana/fugu-ultra": true,
"stealth/ox-alpha": true,
"stepfun/Step-3.5-Flash": true,
"stepfun/Step-3.7-Flash": true,
"tencent/hy3-paid": true,
"thinkingmachines/inkling": true,
"thinkingmachines/inkling-small": true,
"xai/grok-4.5": true,
"xai/grok-4.6": true,
"zai-org/GLM-5.2": true,
"zai-org/GLM-5.3": true,
}
export const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {
"claude-fable-5": ["low", "medium", "high", "xhigh", "max"],
"claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"],
@@ -82,3 +133,9 @@ export const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasonin
"zai-org/GLM-5.2": ["high", "max"],
"zai-org/GLM-5.3": ["low", "high", "max"],
}
export const MODEL_MAX_OUTPUT_TOKENS: Readonly<Record<string, number>> = {
"poolside/laguna-s-2.1-free": 32_768,
"Qwen/Qwen3.8-27B": 32_768,
"stealth/ox-alpha": 131_072,
}
+42 -11
View File
@@ -107,6 +107,7 @@ export function getApiKey(
} = {},
): string | undefined {
const env = options.env ?? process.env
if (env.COMMAND_CODE_API_KEY) return env.COMMAND_CODE_API_KEY
if (env.COMMANDCODE_API_KEY) return env.COMMANDCODE_API_KEY
const home = options.homeDir?.() ?? homedir()
@@ -138,10 +139,11 @@ export function getApiKey(
return undefined
}
// Hosts such as OMP may pass the literal env-var name "$COMMANDCODE_API_KEY"
// (or "COMMANDCODE_API_KEY") as the "resolved" registry key instead of the
// actual credential. Treat those as unresolved.
// Hosts such as OMP may pass a literal env-var name as the "resolved" registry
// key instead of the actual credential. Treat those as unresolved.
export const COMMAND_CODE_PLACEHOLDER_KEYS = new Set([
"$COMMAND_CODE_API_KEY",
"COMMAND_CODE_API_KEY",
"$COMMANDCODE_API_KEY",
"COMMANDCODE_API_KEY",
])
@@ -162,6 +164,16 @@ export function pickCommandCodeApiKey(
}
export function textContent(message: { content?: unknown }): string {
if (typeof message.content === "string") return message.content
if (message.content === null || message.content === undefined) return ""
if (!Array.isArray(message.content)) {
try {
return JSON.stringify(message.content) ?? String(message.content)
} catch {
return String(message.content)
}
}
return recordArray(message.content)
.filter((part) => part.type === "text")
.map((part) => stringValue(part.text) ?? "")
@@ -182,7 +194,12 @@ export function toolsToJson(tools?: readonly ToolLike[]): unknown[] {
}))
}
function completeToolCallIds(messages?: readonly MessageLike[]): Set<string> {
interface ToolCallState {
callIds: ReadonlySet<string>
resultIds: ReadonlySet<string>
}
function toolCallState(messages?: readonly MessageLike[]): ToolCallState {
const callIds = new Set<string>()
const resultIds = new Set<string>()
@@ -194,12 +211,12 @@ function completeToolCallIds(messages?: readonly MessageLike[]): Set<string> {
if (id) callIds.add(id)
}
}
} else if (message.role === "toolResult") {
if (message.toolCallId) resultIds.add(message.toolCallId)
} else if (message.role === "toolResult" && message.toolCallId) {
resultIds.add(message.toolCallId)
}
}
return new Set([...callIds].filter((id) => resultIds.has(id)))
return { callIds, resultIds }
}
export function messagesToCC(
@@ -210,7 +227,7 @@ export function messagesToCC(
if (!allowImages) assertTextOnlyMessages(messages)
const out: unknown[] = []
const pairedToolCallIds = completeToolCallIds(messages)
const { callIds, resultIds } = toolCallState(messages)
for (const message of messages ?? []) {
if (message.role === "user") {
@@ -220,23 +237,37 @@ export function messagesToCC(
})
} else if (message.role === "assistant") {
const parts: unknown[] = []
const missingResults: unknown[] = []
for (const content of recordArray(message.content)) {
if (content.type === "text") {
parts.push({ type: "text", text: stringValue(content.text) ?? "" })
} else if (content.type === "toolCall") {
const toolCallId = stringValue(content.id) ?? ""
if (!pairedToolCallIds.has(toolCallId)) continue
const toolName = stringValue(content.name) ?? ""
if (!toolCallId) continue
parts.push({
type: "tool-call",
toolCallId,
toolName: stringValue(content.name) ?? "",
toolName,
input: recordOrEmpty(content.arguments),
})
if (!resultIds.has(toolCallId)) {
missingResults.push({
type: "tool-result",
toolCallId,
toolName,
output: {
type: "error-text",
value: "No result — the tool call did not complete (interrupted or lost).",
},
})
}
}
}
if (parts.length > 0) out.push({ role: "assistant", content: parts })
if (missingResults.length > 0) out.push({ role: "tool", content: missingResults })
} else if (message.role === "toolResult") {
if (!message.toolCallId || !pairedToolCallIds.has(message.toolCallId)) continue
if (!message.toolCallId || !callIds.has(message.toolCallId)) continue
out.push({
role: "tool",
content: [
+49 -16
View File
@@ -148,6 +148,10 @@ function mappedReasoningEffort(model: ModelLike, options?: StreamOptions): strin
return typeof mapped === "string" && mapped !== "off" ? mapped : undefined
}
function isUuid(value: string): boolean {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)
}
export function projectSlugFromPath(pathName: string): string {
const slug = pathName
.toLowerCase()
@@ -232,17 +236,15 @@ export function createStreamCommandCode(deps: CoreDependencies) {
const stream = deps.createStream()
async function run() {
// OMP may pass the legacy env-var name "COMMANDCODE_API_KEY" (old pi)
// or "$COMMANDCODE_API_KEY" (new pi) as the apiKey value instead of
// resolving it. Filter out these specific strings.
const LEGACY_API_KEY_REF = "$COMMANDCODE_API_KEY"
const OLD_API_KEY_REF = "COMMANDCODE_API_KEY"
// Some hosts pass a literal env-var reference instead of resolving it.
const PLACEHOLDER_API_KEYS = new Set([
"$COMMAND_CODE_API_KEY",
"COMMAND_CODE_API_KEY",
"$COMMANDCODE_API_KEY",
"COMMANDCODE_API_KEY",
])
const hostKey =
options?.apiKey &&
options.apiKey !== LEGACY_API_KEY_REF &&
options.apiKey !== OLD_API_KEY_REF
? options.apiKey
: undefined
options?.apiKey && !PLACEHOLDER_API_KEYS.has(options.apiKey) ? options.apiKey : undefined
const apiKey =
hostKey ??
@@ -262,7 +264,7 @@ export function createStreamCommandCode(deps: CoreDependencies) {
usage: defaultUsage(),
stopReason: "error",
errorMessage:
"No Command Code API key. Run /login and select Command Code, set the COMMANDCODE_API_KEY env var, or configure ~/.commandcode/auth.json, ~/.pi/agent/auth.json or ~/.omp/agent/auth.json",
"No Command Code API key. Run /login and select Command Code, set COMMAND_CODE_API_KEY (or legacy COMMANDCODE_API_KEY), or configure ~/.commandcode/auth.json, ~/.pi/agent/auth.json or ~/.omp/agent/auth.json",
timestamp: now(),
}
stream.push({ type: "error", reason: "error", error: msg })
@@ -484,6 +486,15 @@ export function createStreamCommandCode(deps: CoreDependencies) {
}
case "finish": {
const rawFinishReason = stringValue(event.rawFinishReason)
if (
rawFinishReason &&
/^(?:network|connection|upstream)[-_\s]?error$/i.test(rawFinishReason)
) {
throw new Error(
`Provider finished with reason "${rawFinishReason}" — upstream connection failed mid-stream`,
)
}
const usage = commandCodeUsage(event)
if (usage) {
const details = commandCodeInputTokenDetails(usage)
@@ -507,6 +518,10 @@ export function createStreamCommandCode(deps: CoreDependencies) {
break
}
case "abort": {
throw abortError("Request aborted")
}
case "error": {
const message =
commandCodeErrorMessage(event.error) ??
@@ -524,7 +539,11 @@ export function createStreamCommandCode(deps: CoreDependencies) {
if (controller.signal.aborted) throw abortError("Aborted")
const workingDir = cwd()
const threadId = uuid()
const threadId = options?.sessionId
? isUuid(options.sessionId)
? options.sessionId
: undefined
: uuid()
const reasoningEffort = mappedReasoningEffort(model, options)
const timeoutMs = options?.timeoutMs
@@ -552,8 +571,8 @@ export function createStreamCommandCode(deps: CoreDependencies) {
tools: toolsToJson(context.tools),
system: systemPromptToText(context.systemPrompt),
max_tokens: generateMaxTokens(model, options),
temperature: 0.3,
stream: true,
...(options?.temperature !== undefined ? { temperature: options.temperature } : {}),
...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
},
threadId,
@@ -583,7 +602,8 @@ export function createStreamCommandCode(deps: CoreDependencies) {
"x-cli-environment": "production",
"x-project-slug": projectSlugFromPath(workingDir),
"x-taste-learning": "true",
"x-co-flag": "false",
...(options?.sessionId ? { "x-session-id": options.sessionId } : {}),
"User-Agent": "cli",
...options?.headers,
}
const bodyStr = JSON.stringify(body)
@@ -696,6 +716,11 @@ export function createStreamCommandCode(deps: CoreDependencies) {
const { done, value } = await raceAbort(reader.read(), attemptController.signal)
if (done) {
if (buffer.trim()) handleEvent(parseStreamEventLine(buffer))
if (!finished) {
throw new Error(
"Stream ended unexpectedly before completion (no finish event) — response was truncated",
)
}
break
}
if (controller.signal.aborted) throw abortError("Aborted")
@@ -719,7 +744,12 @@ export function createStreamCommandCode(deps: CoreDependencies) {
} catch {}
reader = undefined
if (controller.signal.aborted) throw streamError
if (
controller.signal.aborted ||
(streamError instanceof Error && streamError.name === "AbortError")
) {
throw streamError
}
// Never retry after visible content was emitted (including timeout mid-stream).
const canRetry = output.content.length === 0 && attempt < maxRetries
@@ -756,7 +786,10 @@ export function createStreamCommandCode(deps: CoreDependencies) {
}
}
} catch (error: unknown) {
const reason: ErrorReason = controller.signal.aborted ? "aborted" : "error"
const reason: ErrorReason =
controller.signal.aborted || (error instanceof Error && error.name === "AbortError")
? "aborted"
: "error"
output.stopReason = reason
output.errorMessage =
reason === "aborted"
+25 -14
View File
@@ -4,11 +4,13 @@ import { dirname } from "node:path"
import {
MODEL_EFFORTS,
MODEL_INPUT_MODALITIES,
MODEL_MAX_OUTPUT_TOKENS,
MODEL_REASONING,
type CommandCodeInputType,
type CommandCodeReasoningEffort,
} from "./commandcode-catalog.ts"
export { MODEL_EFFORTS, MODEL_INPUT_MODALITIES }
export { MODEL_EFFORTS, MODEL_INPUT_MODALITIES, MODEL_MAX_OUTPUT_TOKENS, MODEL_REASONING }
export type { CommandCodeInputType }
export const DEFAULT_PROVIDER_API_BASE = "https://api.commandcode.ai/provider/v1"
@@ -55,7 +57,7 @@ export function thinkingLevelMapForEfforts(
export interface ThinkingMetadata {
thinkingLevelMap: Partial<Record<PiThinkingLevel, string | null>>
thinking: {
thinking?: {
mode: "effort"
effortMap: Partial<Record<CommandCodeReasoningEffort, string>>
efforts: readonly CommandCodeReasoningEffort[]
@@ -64,19 +66,26 @@ export interface ThinkingMetadata {
export function thinkingMetadataForModel(modelId: string): ThinkingMetadata | undefined {
const efforts = MODEL_EFFORTS[modelId]
if (!efforts) return undefined
return {
thinkingLevelMap: thinkingLevelMapForEfforts(efforts),
thinking: {
mode: "effort",
effortMap: Object.fromEntries(efforts.map((effort) => [effort, effort])),
efforts,
},
if (efforts) {
return {
thinkingLevelMap: thinkingLevelMapForEfforts(efforts),
thinking: {
mode: "effort",
effortMap: Object.fromEntries(efforts.map((effort) => [effort, effort])),
efforts,
},
}
}
if (!isReasoningModel(modelId)) return undefined
return { thinkingLevelMap: thinkingLevelMapForEfforts([]) }
}
function isReasoningModel(modelId: string): boolean {
return MODEL_EFFORTS[modelId] !== undefined
return MODEL_REASONING[modelId] === true
}
function maxOutputTokensForModel(modelId: string, contextLength: number): number {
return Math.min(contextLength, MODEL_MAX_OUTPUT_TOKENS[modelId] ?? DEFAULT_MAX_OUTPUT_TOKENS)
}
interface ApiModel {
@@ -162,13 +171,15 @@ function parseCachedModel(value: unknown): CommandCodeModel {
const id = stringField(value, "id")
booleanField(value, "reasoning")
positiveNumberField(value, "maxTokens")
const contextWindow = positiveNumberField(value, "contextWindow")
return {
id,
name: stringField(value, "name"),
api: apiForModelId(id),
reasoning: isReasoningModel(id),
contextWindow: positiveNumberField(value, "contextWindow"),
maxTokens: positiveNumberField(value, "maxTokens"),
contextWindow,
maxTokens: maxOutputTokensForModel(id, contextWindow),
}
}
@@ -273,7 +284,7 @@ export function commandCodeModelsFromApiResponse(value: unknown): readonly Comma
api: apiForModelId(model.id),
reasoning: isReasoningModel(model.id),
contextWindow: model.contextLength,
maxTokens: Math.min(model.contextLength, DEFAULT_MAX_OUTPUT_TOKENS),
maxTokens: maxOutputTokensForModel(model.id, model.contextLength),
}))
}
+33 -10
View File
@@ -18,7 +18,8 @@ import { startAuthServer } from "./auth-server.ts"
const STUDIO_BASE_URL = "https://commandcode.ai"
const TEN_YEARS_MS = 10 * 365 * 24 * 60 * 60 * 1000 // API keys don't expire
const DEFAULT_AUTH_TIMEOUT_MS = 15_000
const DEFAULT_AUTH_TIMEOUT_MS = 120_000
const DEFAULT_API_BASE = "https://api.commandcode.ai"
export interface OAuthLoginCallbacks {
onAuth(params: { url: string }): void
@@ -95,9 +96,34 @@ export function sanitizeApiKey(input: string): string {
.trim()
}
export async function validateApiKey(
apiKey: string,
options: { fetchImpl?: typeof fetch; apiBase?: string } = {},
): Promise<void> {
let response: Response
try {
response = await (options.fetchImpl ?? fetch)(
`${options.apiBase ?? DEFAULT_API_BASE}/alpha/whoami`,
{
headers: { Authorization: `Bearer ${apiKey}` },
},
)
} catch (error) {
throw new Error(
`Could not validate the Command Code API key: ${error instanceof Error ? error.message : String(error)}`,
)
}
if (response.status === 401) throw new Error("Invalid Command Code API key")
if (!response.ok) {
throw new Error(`Could not validate the Command Code API key (${response.status})`)
}
}
async function promptForApiKey(callbacks: OAuthLoginCallbacks, message: string) {
const apiKey = sanitizeApiKey(await callbacks.onPrompt({ message }))
if (!apiKey) throw new Error("No Command Code API key provided")
await validateApiKey(apiKey)
return credentialsFromApiKey(apiKey)
}
@@ -130,9 +156,10 @@ async function chooseLoginFlow(callbacks: OAuthLoginCallbacks): Promise<LoginCho
}
async function browserLogin(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
const stateToken = generateStateToken()
let authServer
try {
authServer = await startAuthServer()
authServer = await startAuthServer({ expectedState: stateToken })
} catch {
return promptForApiKey(
callbacks,
@@ -140,7 +167,6 @@ async function browserLogin(callbacks: OAuthLoginCallbacks): Promise<OAuthCreden
)
}
const stateToken = generateStateToken()
const callbackUrl = `http://localhost:${authServer.port}/callback`
const authUrl = `${STUDIO_BASE_URL}/studio/auth/cli?callback=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(stateToken)}`
@@ -164,12 +190,6 @@ async function browserLogin(callbacks: OAuthLoginCallbacks): Promise<OAuthCreden
throw error
}
// Validate state token to prevent CSRF.
if (callback.state !== stateToken) {
authServer.server.close()
throw new Error("State token mismatch. Authentication may have been tampered with.")
}
return credentialsFromApiKey(callback.apiKey)
}
@@ -182,7 +202,10 @@ async function browserLogin(callbacks: OAuthLoginCallbacks): Promise<OAuthCreden
export async function login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
const choice = await chooseLoginFlow(callbacks)
if (choice.type === "apiKey") return credentialsFromApiKey(choice.apiKey)
if (choice.type === "apiKey") {
await validateApiKey(choice.apiKey)
return credentialsFromApiKey(choice.apiKey)
}
if (choice.type === "prompt") {
return promptForApiKey(callbacks, "Paste your Command Code API key:")
}
+42 -2
View File
@@ -20,7 +20,7 @@ export interface TemporaryPricing {
}
export const PRICING_SOURCE_URL = "https://commandcode.ai/docs/resources/pricing-limits"
export const PRICING_LAST_VERIFIED = "2026-08-22"
export const PRICING_LAST_VERIFIED = "2026-08-25"
export const ZERO_MODEL_COST: CommandCodeModelCost = {
input: 0,
@@ -40,7 +40,7 @@ export const ZERO_MODEL_COST: CommandCodeModelCost = {
export const MODEL_COSTS: Readonly<Record<string, CommandCodeModelCost>> = {
// Free models
"poolside/laguna-s-2.1-free": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
"inclusionai/ling-3.0-flash-free": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
"stealth/ox-alpha": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
// Open and open-weight models
"tencent/hy3-paid": { input: 0.14, output: 0.58, cacheRead: 0.035, cacheWrite: 0 },
@@ -76,7 +76,14 @@ export const MODEL_COSTS: Readonly<Record<string, CommandCodeModelCost>> = {
cacheRead: 0.007,
cacheWrite: 0,
},
"deepseek/deepseek-v4-flash-vision-exp": {
input: 0.22,
output: 0.66,
cacheRead: 0.007,
cacheWrite: 0,
},
"Qwen/Qwen3.8-Max": { input: 2, output: 6, cacheRead: 0.25, cacheWrite: 2.5 },
"Qwen/Qwen3.8-27B": { input: 0.4, output: 3, cacheRead: 0.04, cacheWrite: 0 },
"Qwen/Qwen3.7-Max": { input: 2.5, output: 7.5, cacheRead: 0.5, cacheWrite: 3.13 },
"Qwen/Qwen3.7-Plus": {
input: 0.4,
@@ -142,6 +149,13 @@ export const MODEL_COSTS: Readonly<Record<string, CommandCodeModelCost>> = {
cacheWrite: 0,
},
"meta/muse-spark-1.1": { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 },
"meta/muse-spark-1.2": { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 },
"meta/muse-spark-1.2-contributor": {
input: 0.1,
output: 0.2,
cacheRead: 0.002,
cacheWrite: 0,
},
// Anthropic
// Introductory pricing through 2026-08-31.
@@ -168,6 +182,12 @@ export const MODEL_COSTS: Readonly<Record<string, CommandCodeModelCost>> = {
"gpt-5.4-mini": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
// Google and xAI
"google/gemini-3.7-flash": {
input: 0.75,
output: 3.75,
cacheRead: 0.075,
cacheWrite: 0.04167,
},
"google/gemini-3.6-flash": { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0 },
"google/gemini-3.5-flash": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 },
"google/gemini-3.5-flash-lite": {
@@ -183,6 +203,21 @@ export const MODEL_COSTS: Readonly<Record<string, CommandCodeModelCost>> = {
cacheWrite: 0,
},
"xai/grok-4.5": { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 },
"xai/grok-4.6": {
input: 2,
output: 6,
cacheRead: 0.5,
cacheWrite: 0,
tiers: [
{
inputTokensAbove: 200_000,
input: 4,
output: 12,
cacheRead: 1,
cacheWrite: 0,
},
],
},
}
export const TEMPORARY_PRICING: readonly TemporaryPricing[] = [
@@ -191,4 +226,9 @@ export const TEMPORARY_PRICING: readonly TemporaryPricing[] = [
expiresOn: "2026-08-31",
description: "introductory pricing",
},
{
models: ["google/gemini-3.7-flash"],
expiresOn: "2026-12-31",
description: "50% promotional pricing",
},
]
+1 -1
View File
@@ -45,7 +45,7 @@ export function registerCommandCodeQuota(
const apiKey = pickCommandCodeApiKey(registryKey, getConfiguredKey())
if (!apiKey) {
ctx.ui.notify(
"Command Code quota requires an API key. Run /login and select Command Code, or set COMMANDCODE_API_KEY.",
"Command Code quota requires an API key. Run /login and select Command Code, or set COMMAND_CODE_API_KEY.",
"warning",
)
return
+2
View File
@@ -112,6 +112,8 @@ export interface StreamOptions {
headers?: Record<string, string>
fetch?: typeof fetch
maxTokens?: number
temperature?: number
sessionId?: string
/** Resolved pi thinking level; forwarded only through the model's map. */
reasoning?: string
onPayload?: (payload: unknown, model: ModelLike) => unknown | Promise<unknown>
+9 -3
View File
@@ -1,5 +1,5 @@
{
"fetchedAt": "2026-08-22T21:19:37.782Z",
"fetchedAt": "2026-08-25T13:32:11.631Z",
"source": "https://api.commandcode.ai/provider/v1/models",
"modelIds": [
"claude-sonnet-5",
@@ -18,6 +18,7 @@
"gpt-5.4-mini",
"deepseek/deepseek-v4-pro",
"deepseek/deepseek-v4-flash",
"deepseek/deepseek-v4-flash-vision-exp",
"moonshotai/Kimi-K3",
"moonshotai/Kimi-K2.7-Code",
"moonshotai/Kimi-K2.7-Code-Highspeed",
@@ -34,6 +35,7 @@
"xiaomi/mimo-v2.5-pro",
"xiaomi/mimo-v2.5",
"Qwen/Qwen3.8-Max",
"Qwen/Qwen3.8-27B",
"Qwen/Qwen3.7-Max",
"Qwen/Qwen3.7-Plus",
"Qwen/Qwen3.7-Flash",
@@ -42,6 +44,7 @@
"stepfun/Step-3.7-Flash",
"stepfun/Step-3.5-Flash",
"tencent/hy3-paid",
"google/gemini-3.7-flash",
"google/gemini-3.6-flash",
"google/gemini-3.5-flash",
"google/gemini-3.5-flash-lite",
@@ -50,9 +53,12 @@
"nvidia/nemotron-3-ultra-550b-a55b",
"thinkingmachines/inkling",
"thinkingmachines/inkling-small",
"stealth/ox-alpha",
"poolside/laguna-s-2.1-free",
"inclusionai/ling-3.0-flash-free",
"meta/muse-spark-1.1",
"xai/grok-4.5"
"meta/muse-spark-1.2",
"meta/muse-spark-1.2-contributor",
"xai/grok-4.5",
"xai/grok-4.6"
]
}
+19 -12
View File
@@ -1,5 +1,5 @@
{
"verifiedAt": "2026-08-22",
"verifiedAt": "2026-08-25",
"source": "https://commandcode.ai/docs/resources/pricing-limits",
"tierPolicy": "Use request-wide input tiers; the highest threshold exceeded by input plus cache tokens applies to the full request.",
"tiers": {
@@ -7,12 +7,13 @@
"Qwen/Qwen3.7-Flash": [
[32000, 0.1, 0.4, 0.02, 0.125],
[256000, 0.2, 0.8, 0.04, 0.25]
]
],
"xai/grok-4.6": [[200000, 4, 12, 1, 0]]
},
"costs": {
"poolside/laguna-s-2.1-free": [0, 0, 0, 0],
"inclusionai/ling-3.0-flash-free": [0, 0, 0, 0],
"tencent/hy3-paid": [0.14, 0.58, 0.035, 0],
"deepseek/deepseek-v4-pro": [0.66, 1.98, 0.022, 0],
"deepseek/deepseek-v4-flash": [0.22, 0.66, 0.007, 0],
"deepseek/deepseek-v4-flash-vision-exp": [0.22, 0.66, 0.007, 0],
"moonshotai/Kimi-K3": [3, 15, 0.3, 0],
"moonshotai/Kimi-K2.7-Code": [0.95, 4, 0.19, 0],
"moonshotai/Kimi-K2.7-Code-Highspeed": [1.9, 8, 0.38, 0],
@@ -26,9 +27,10 @@
"MiniMaxAI/MiniMax-M3": [0.3, 1.2, 0.06, 0],
"MiniMaxAI/MiniMax-M2.7": [0.3, 1.2, 0.06, 0],
"MiniMaxAI/MiniMax-M2.5": [0.3, 1.2, 0.03, 0],
"deepseek/deepseek-v4-pro": [0.66, 1.98, 0.022, 0],
"deepseek/deepseek-v4-flash": [0.22, 0.66, 0.007, 0],
"xiaomi/mimo-v2.5-pro": [0.435, 0.87, 0.0036, 0],
"xiaomi/mimo-v2.5": [0.14, 0.28, 0.0028, 0],
"Qwen/Qwen3.8-Max": [2, 6, 0.25, 2.5],
"Qwen/Qwen3.8-27B": [0.4, 3, 0.04, 0],
"Qwen/Qwen3.7-Max": [2.5, 7.5, 0.5, 3.13],
"Qwen/Qwen3.7-Plus": [0.4, 1.6, 0.08, 0.5],
"Qwen/Qwen3.7-Flash": [0.03, 0.13, 0.006, 0.038],
@@ -36,13 +38,12 @@
"Qwen/Qwen3.6-Plus": [0.5, 3, 0.1, 0],
"stepfun/Step-3.7-Flash": [0.2, 1.15, 0.04, 0],
"stepfun/Step-3.5-Flash": [0.1, 0.3, 0.02, 0],
"xiaomi/mimo-v2.5-pro": [0.435, 0.87, 0.0036, 0],
"xiaomi/mimo-v2.5": [0.14, 0.28, 0.0028, 0],
"tencent/hy3-paid": [0.14, 0.58, 0.035, 0],
"nvidia/nemotron-3-ultra-550b-a55b": [0.6, 2.4, 0.12, 0],
"sakana/fugu-ultra": [5, 30, 0.5, 0],
"thinkingmachines/inkling": [1, 4.05, 0.17, 0],
"thinkingmachines/inkling-small": [0.5, 1.2, 0.1, 0],
"meta/muse-spark-1.1": [1.25, 4.25, 0.15, 0],
"poolside/laguna-s-2.1-free": [0, 0, 0, 0],
"stealth/ox-alpha": [0, 0, 0, 0],
"claude-sonnet-5": [2, 10, 0.2, 2.5],
"claude-sonnet-4-6": [3, 15, 0.3, 3.75],
"claude-fable-5": [10, 50, 1, 12.5],
@@ -57,10 +58,16 @@
"gpt-5.4": [2.5, 15, 0.25, 0],
"gpt-5.3-codex": [2, 8, 0.5, 0],
"gpt-5.4-mini": [0.75, 4.5, 0.075, 0],
"google/gemini-3.7-flash": [0.75, 3.75, 0.075, 0.04167],
"google/gemini-3.6-flash": [1.5, 7.5, 0.15, 0],
"google/gemini-3.5-flash": [1.5, 9, 0.15, 0],
"google/gemini-3.5-flash-lite": [0.3, 2.5, 0.03, 0],
"google/gemini-3.1-flash-lite": [0.25, 1.5, 0.03, 0],
"xai/grok-4.5": [2, 6, 0.5, 0]
"sakana/fugu-ultra": [5, 30, 0.5, 0],
"meta/muse-spark-1.1": [1.25, 4.25, 0.15, 0],
"meta/muse-spark-1.2": [1.25, 4.25, 0.15, 0],
"meta/muse-spark-1.2-contributor": [0.1, 0.2, 0.002, 0],
"xai/grok-4.5": [2, 6, 0.5, 0],
"xai/grok-4.6": [2, 6, 0.5, 0]
}
}
+10 -3
View File
@@ -21,10 +21,17 @@ async function withAuthFile(
}
describe("getConfiguredApiKey()", () => {
it("prefers the environment variable", () => {
it("prefers the official environment variable and keeps the legacy alias", () => {
assert.equal(
getConfiguredApiKey({ env: { COMMANDCODE_API_KEY: "env-key" }, authPaths: [] }),
"env-key",
getConfiguredApiKey({
env: { COMMAND_CODE_API_KEY: "official-key", COMMANDCODE_API_KEY: "legacy-key" },
authPaths: [],
}),
"official-key",
)
assert.equal(
getConfiguredApiKey({ env: { COMMANDCODE_API_KEY: "legacy-key" }, authPaths: [] }),
"legacy-key",
)
})
+130 -14
View File
@@ -27,7 +27,20 @@ const extensionPath = join(projectDir, "index.ts")
const testModel = process.env.COMMANDCODE_E2E_MODEL ?? "deepseek/deepseek-v4-flash"
const testProfile = process.env.COMMANDCODE_E2E_PROFILE
const expectedTransport =
testProfile === "go" ? "generate" : testProfile === "provider" ? "provider" : undefined
testProfile === "go"
? "generate"
: testProfile === "goat" || testProfile === "provider"
? "provider"
: undefined
const expectedPlan =
testProfile === "go"
? "go"
: testProfile === "goat"
? "goat"
: testProfile === "provider"
? "provider"
: undefined
const goatVisionModel = process.env.COMMANDCODE_E2E_GOAT_VISION_MODEL ?? "google/gemini-3.7-flash"
const marker = "commandcode-live-e2e-ok"
function findPiBinary() {
@@ -48,6 +61,7 @@ function findPiBinary() {
function hasAuthMetadata() {
return (
Boolean(process.env.COMMAND_CODE_API_KEY) ||
Boolean(process.env.COMMANDCODE_API_KEY) ||
existsSync(join(homedir(), ".commandcode", "auth.json")) ||
existsSync(join(homedir(), ".pi", "agent", "auth.json"))
@@ -70,6 +84,7 @@ function safeEnv(overrides = {}) {
env.PI_CODING_AGENT_DIR = profileAgentDir
env.COMMANDCODE_MODELS_CACHE = join(profileAgentDir, "commandcode-models.json")
} else {
delete env.COMMAND_CODE_API_KEY
delete env.COMMANDCODE_API_KEY
}
return env
@@ -102,7 +117,7 @@ function run(command, args, options = {}) {
})
}
async function runRpc(extension, action, timeoutMs = 120_000) {
async function runRpc(extension, action, timeoutMs = 120_000, model = testModel) {
const child = spawn(
piBin,
[
@@ -114,7 +129,9 @@ async function runRpc(extension, action, timeoutMs = 120_000) {
"--provider",
"commandcode",
"--model",
testModel,
model,
"--thinking",
"high",
],
{ cwd: projectDir, env: safeEnv(), stdio: ["pipe", "pipe", "pipe"] },
)
@@ -222,8 +239,19 @@ try {
await waitFor(
(event) => event.type === "response" && event.id === "reasoning-turn-1" && event.success,
)
await waitFor((event) => event.type === "agent_settled")
const firstThinkingDeltas = countThinkingDeltas(firstStart)
const firstSettled = await waitFor(
(event) => event.type === "agent_settled" && events.indexOf(event) >= firstStart,
)
const firstSettledIndex = events.indexOf(firstSettled)
const firstThinkingDeltas = events
.slice(firstStart, firstSettledIndex + 1)
.filter(
(event) =>
event.type === "message_update" &&
event.assistantMessageEvent?.type === "thinking_delta" &&
typeof event.assistantMessageEvent.delta === "string" &&
event.assistantMessageEvent.delta.length > 0,
).length
const secondStart = events.length
send({
@@ -235,15 +263,16 @@ try {
await waitFor(
(event) => event.type === "response" && event.id === "reasoning-turn-2" && event.success,
)
await waitFor((event) => event.type === "agent_settled" && events.indexOf(event) >= secondStart)
const secondSettled = await waitFor(
(event) => event.type === "agent_settled" && events.indexOf(event) >= secondStart,
)
const secondThinkingDeltas = countThinkingDeltas(secondStart)
assert.ok(events.indexOf(secondSettled) >= secondStart)
return { firstThinkingDeltas, secondThinkingDeltas, stderr: getStderr() }
})
if (testProfile !== "provider") {
assert.ok(multiTurn.firstThinkingDeltas > 0, "first turn should stream reasoning")
assert.ok(multiTurn.secondThinkingDeltas > 0, "follow-up turn should stream fresh reasoning")
}
assert.ok(multiTurn.firstThinkingDeltas > 0, "first turn should stream reasoning")
assert.ok(multiTurn.secondThinkingDeltas > 0, "follow-up turn should stream fresh reasoning")
assert.doesNotMatch(multiTurn.stderr, /Bearer\s+\S+/i)
console.log("[live-e2e] live runtime refresh/status commands")
@@ -281,15 +310,66 @@ try {
typeof event.message === "string" &&
event.message.includes("source:"),
)
return { names, refresh: refresh.message, status: status.message, stderr: getStderr() }
send({ id: "quota", type: "prompt", message: "/commandcode-quota" })
await waitFor((event) => event.type === "response" && event.id === "quota" && event.success)
const quota = await waitFor(
(event) =>
event.type === "extension_ui_request" &&
event.method === "notify" &&
typeof event.message === "string" &&
event.message.includes("Plan:"),
)
return {
names,
refresh: refresh.message,
status: status.message,
quota: quota.message,
stderr: getStderr(),
}
})
assert.ok(runtime.names.includes("commandcode-refresh"))
assert.ok(runtime.names.includes("commandcode-status"))
assert.ok(runtime.names.includes("commandcode-quota"))
assert.match(runtime.refresh, /model catalog (?:refreshed|unchanged)/)
if (expectedTransport) assert.match(runtime.status, new RegExp(`transport: ${expectedTransport}`))
assert.match(runtime.status, /source: (?:live|cache)/)
assert.match(runtime.status, /model count: [1-9][0-9]*/)
assert.doesNotMatch(`${runtime.refresh}\n${runtime.status}\n${runtime.stderr}`, /Bearer\s+\S+/i)
if (expectedPlan) assert.match(runtime.quota, new RegExp(`Plan:.*\\b${expectedPlan}\\b`, "i"))
assert.doesNotMatch(
`${runtime.refresh}\n${runtime.status}\n${runtime.quota}\n${runtime.stderr}`,
/Bearer\s+\S+/i,
)
console.log("[live-e2e] live abort through real RPC host")
const abortResult = await runRpc(extensionPath, async ({ send, waitFor, events, getStderr }) => {
const startIndex = events.length
send({
id: "abort-turn",
type: "prompt",
message: "Write a very long detailed explanation of every integer from 1 to 10000.",
})
await waitFor(
(event) => event.type === "response" && event.id === "abort-turn" && event.success,
)
await waitFor((event) => event.type === "message_update" && events.indexOf(event) >= startIndex)
send({ id: "abort", type: "abort" })
await waitFor((event) => event.type === "response" && event.id === "abort" && event.success)
await waitFor((event) => event.type === "agent_settled" && events.indexOf(event) >= startIndex)
return {
aborted: events
.slice(startIndex)
.some(
(event) =>
event.type === "message_end" &&
event.message?.role === "assistant" &&
event.message?.stopReason === "aborted",
),
stderr: getStderr(),
}
})
assert.equal(abortResult.aborted, true)
assert.doesNotMatch(abortResult.stderr, /Bearer\s+\S+/i)
console.log("[live-e2e] live tool-call round trip")
const toolRoot = join(tempRoot, "tool-roundtrip")
@@ -317,9 +397,45 @@ try {
)
assert.equal(toolResult.code, 0, toolResult.stderr)
assert.match(toolResult.stdout, new RegExp(marker))
assert.equal(readFileSync(targetPath, "utf-8"), marker)
assert.equal(readFileSync(targetPath, "utf-8").trimEnd(), marker)
if (testProfile !== "provider") {
if (testProfile === "goat") {
console.log("[live-e2e] live vision request through Provider API")
const vision = await runRpc(
extensionPath,
async ({ send, waitFor, events, getStderr }) => {
const startIndex = events.length
send({
id: "vision",
type: "prompt",
message: "Describe the attached image briefly.",
images: [
{
type: "image",
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
mimeType: "image/png",
},
],
})
await waitFor(
(event) => event.type === "response" && event.id === "vision" && event.success,
)
await waitFor(
(event) => event.type === "agent_settled" && events.indexOf(event) >= startIndex,
)
const messageEnd = events
.slice(startIndex)
.find((event) => event.type === "message_end" && event.message?.role === "assistant")
return { messageEnd, stderr: getStderr() }
},
180_000,
goatVisionModel,
)
assert.notEqual(vision.messageEnd?.message?.stopReason, "error")
assert.doesNotMatch(vision.stderr, /Bearer\s+\S+/i)
}
if (testProfile === "go") {
console.log("[live-e2e] image rejection through real RPC host")
const image = await runRpc(extensionPath, async ({ send, waitFor, events }) => {
send({
+38 -9
View File
@@ -5,6 +5,7 @@ import {
commandCodeModelMetadataFromContents,
diffModelMetadata,
hasModelMetadataDiff,
parseBundleModelCapabilities,
parseKnownTextOnlyModelIds,
parseModelsReference,
parsePackageVersion,
@@ -21,7 +22,7 @@ const MODELS_REFERENCE = `
`
const CLI_BUNDLE =
'const catalog=new Set(["text-model"]),__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")'
'const V={id:"vision-model",inputModalities:["text","image"],reasoning:!0,reasoningEfforts:["low","high"],maxOutputTokens:32768},T={id:"text-model",inputModalities:["text"]},catalog=new Set(["text-model"]),__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")'
describe("Command Code model metadata checker", () => {
it("parses model ids and reasoning efforts from the generated reference", () => {
@@ -42,29 +43,39 @@ describe("Command Code model metadata checker", () => {
assert.throws(() => parsePackageVersion("latest"), /one semantic version/)
})
it("derives image support by excluding known text-only models", () => {
it("derives image, reasoning, effort, and output-limit metadata", () => {
assert.deepEqual(parseBundleModelCapabilities(CLI_BUNDLE, ["text-model", "vision-model"]), {
reasoningModelIds: ["vision-model"],
maxOutputTokens: { "vision-model": 32_768 },
})
assert.deepEqual(commandCodeModelMetadataFromContents(MODELS_REFERENCE, CLI_BUNDLE), {
imageModelIds: ["vision-model"],
reasoningModelIds: ["vision-model"],
reasoningEfforts: { "vision-model": ["low", "high"] },
maxOutputTokens: { "vision-model": 32_768 },
})
})
it("reports additions, removals, and changed reasoning efforts", () => {
const current: CommandCodeModelMetadata = {
imageModelIds: ["removed-image", "stable-image"],
reasoningModelIds: ["removed-reasoning", "stable-reasoning"],
reasoningEfforts: {
"changed-reasoning": ["low"],
"removed-reasoning": ["high"],
"stable-reasoning": ["low", "high"],
"changed-effort": ["low"],
"removed-effort": ["high"],
"stable-effort": ["low", "high"],
},
maxOutputTokens: { "changed-output": 1, "removed-output": 2, "stable-output": 3 },
}
const upstream: CommandCodeModelMetadata = {
imageModelIds: ["added-image", "stable-image"],
reasoningModelIds: ["added-reasoning", "stable-reasoning"],
reasoningEfforts: {
"added-reasoning": ["max"],
"changed-reasoning": ["low", "high"],
"stable-reasoning": ["low", "high"],
"added-effort": ["max"],
"changed-effort": ["low", "high"],
"stable-effort": ["low", "high"],
},
maxOutputTokens: { "added-output": 4, "changed-output": 5, "stable-output": 3 },
}
const diff = diffModelMetadata(current, upstream)
@@ -75,7 +86,12 @@ describe("Command Code model metadata checker", () => {
removedImageModelIds: ["removed-image"],
addedReasoningModelIds: ["added-reasoning"],
removedReasoningModelIds: ["removed-reasoning"],
changedReasoningModelIds: ["changed-reasoning"],
addedEffortModelIds: ["added-effort"],
removedEffortModelIds: ["removed-effort"],
changedEffortModelIds: ["changed-effort"],
addedMaxOutputModelIds: ["added-output"],
removedMaxOutputModelIds: ["removed-output"],
changedMaxOutputModelIds: ["changed-output"],
})
assert.equal(hasModelMetadataDiff(diff), true)
})
@@ -83,7 +99,9 @@ describe("Command Code model metadata checker", () => {
it("reports CLI version drift even when model metadata is unchanged", () => {
const metadata: CommandCodeModelMetadata = {
imageModelIds: ["vision-model"],
reasoningModelIds: ["vision-model"],
reasoningEfforts: { "vision-model": ["low"] },
maxOutputTokens: { "vision-model": 32_768 },
}
const diff = diffModelMetadata(metadata, metadata, "1.32.2", "1.33.0")
@@ -96,10 +114,12 @@ describe("Command Code model metadata checker", () => {
assert.equal(
renderCommandCodeCatalog("1.33.0", {
imageModelIds: ["b-model", "a-model"],
reasoningModelIds: ["c-model", "a-model"],
reasoningEfforts: {
"b-model": ["high", "max"],
"a-model": ["low"],
},
maxOutputTokens: { "b-model": 32_768 },
}),
`export const COMMAND_CODE_CLI_VERSION = "1.33.0"
@@ -115,10 +135,19 @@ export const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCod
"b-model": ["text", "image"],
}
export const MODEL_REASONING: Readonly<Record<string, true>> = {
"a-model": true,
"c-model": true,
}
export const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {
"a-model": ["low"],
"b-model": ["high", "max"],
}
export const MODEL_MAX_OUTPUT_TOKENS: Readonly<Record<string, number>> = {
"b-model": 32_768,
}
`,
)
assert.equal(
+44 -3
View File
@@ -16,6 +16,8 @@ import {
loadCommandCodeModels,
MODEL_EFFORTS,
MODEL_INPUT_MODALITIES,
MODEL_MAX_OUTPUT_TOKENS,
MODEL_REASONING,
modelSupportsImageInput,
thinkingLevelMapForEfforts,
thinkingMetadataForModel,
@@ -41,7 +43,7 @@ const EXPECTED_MODELS: readonly CommandCodeModel[] = [
id: "Qwen/Qwen3.7-Max",
name: "Qwen 3.7 Max (CC)",
api: "openai-completions",
reasoning: false,
reasoning: true,
contextWindow: 1_000_000,
maxTokens: 65_536,
},
@@ -124,17 +126,55 @@ describe("commandCodeModelsFromApiResponse()", () => {
}
})
it("marks only known reasoning models as reasoning-capable", () => {
it("tracks reasoning independently from selectable effort levels", () => {
const models = commandCodeModelsFromApiResponse({
object: "list",
data: [
{ ...API_RESPONSE.data[0], id: "deepseek/deepseek-v4-flash" },
{ ...API_RESPONSE.data[0], id: "moonshotai/Kimi-K3" },
{ ...API_RESPONSE.data[0], id: "new-model-without-metadata" },
],
})
assert.equal(models[0]?.reasoning, true)
assert.equal(models[1]?.reasoning, false)
assert.equal(models[1]?.reasoning, true)
assert.deepEqual(thinkingMetadataForModel("moonshotai/Kimi-K3"), {
thinkingLevelMap: {
minimal: null,
low: null,
medium: null,
high: null,
xhigh: null,
max: null,
},
})
assert.equal(models[2]?.reasoning, false)
assert.equal(Object.keys(MODEL_REASONING).length, 48)
})
it("uses model-specific output limits from the CLI catalog", () => {
const models = commandCodeModelsFromApiResponse({
object: "list",
data: [
{ ...API_RESPONSE.data[0], id: "Qwen/Qwen3.8-27B", context_length: 262_144 },
{ ...API_RESPONSE.data[0], id: "stealth/ox-alpha", context_length: 1_048_576 },
{
...API_RESPONSE.data[0],
id: "poolside/laguna-s-2.1-free",
context_length: 256_000,
},
],
})
assert.deepEqual(
models.map(({ id, maxTokens }) => ({ id, maxTokens })),
[
{ id: "Qwen/Qwen3.8-27B", maxTokens: 32_768 },
{ id: "stealth/ox-alpha", maxTokens: 131_072 },
{ id: "poolside/laguna-s-2.1-free", maxTokens: 32_768 },
],
)
assert.equal(Object.keys(MODEL_MAX_OUTPUT_TOKENS).length, 3)
})
it(`uses the command-code@${COMMAND_CODE_CLI_VERSION} reasoning effort catalog`, () => {
@@ -151,6 +191,7 @@ describe("commandCodeModelsFromApiResponse()", () => {
for (const [modelId, efforts] of Object.entries(MODEL_EFFORTS)) {
const metadata = thinkingMetadataForModel(modelId)
assert.ok(metadata, `${modelId} should have reasoning metadata`)
assert.ok(metadata.thinking)
assert.equal(metadata.thinking.mode, "effort")
assert.deepEqual(metadata.thinking.efforts, efforts)
assert.deepEqual(
+119 -39
View File
@@ -9,7 +9,7 @@ import assert from "node:assert/strict"
import { describe, it } from "node:test"
import { startAuthServer, type AuthCallback } from "../src/auth-server.ts"
import { getApiKey, login, refreshToken, sanitizeApiKey } from "../src/oauth.ts"
import { getApiKey, login, refreshToken, sanitizeApiKey, validateApiKey } from "../src/oauth.ts"
/**
* Helper: wait for an HTTP server to close, or resolve immediately if already closed.
@@ -24,9 +24,27 @@ function waitForClose(server: {
})
}
async function withValidApiKeyFetch<T>(run: () => Promise<T>): Promise<T> {
const originalFetch = globalThis.fetch
globalThis.fetch = (input, init) => {
if (String(input).endsWith("/alpha/whoami")) {
return Promise.resolve(new Response(JSON.stringify({ user: {} }), { status: 200 }))
}
return originalFetch(input, init)
}
try {
return await run()
} finally {
globalThis.fetch = originalFetch
}
}
describe("startAuthServer()", () => {
it("starts on a localhost port and accepts a valid callback POST", async () => {
const { server, port, waitForCallback } = await startAuthServer({ startPort: 0 })
const { server, port, waitForCallback } = await startAuthServer({
startPort: 0,
expectedState: "test-state-token",
})
const callbackData: AuthCallback = {
apiKey: "user_testKey123",
@@ -57,6 +75,42 @@ describe("startAuthServer()", () => {
await waitForClose(server)
})
it("rejects a mismatched state without closing the callback server", async () => {
const { server, port, waitForCallback } = await startAuthServer({
startPort: 0,
expectedState: "correct-state",
})
const invalidResponse = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "POST",
headers: { "Content-Type": "application/json", Origin: "https://commandcode.ai" },
body: JSON.stringify({
apiKey: "user_badState",
state: "wrong-state",
userId: "user_789",
userName: "Attacker",
keyName: "evil-key",
}),
})
assert.equal(invalidResponse.status, 403)
assert.equal(server.listening, true)
const validResponse = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "POST",
headers: { "Content-Type": "application/json", Origin: "https://commandcode.ai" },
body: JSON.stringify({
apiKey: "user_valid",
state: "correct-state",
userId: "user_123",
userName: "Valid User",
keyName: "valid-key",
}),
})
assert.equal(validResponse.status, 200)
assert.equal((await waitForCallback).apiKey, "user_valid")
await waitForClose(server)
})
it("rejects when the callback indicates access_denied", async () => {
const { server, port, waitForCallback } = await startAuthServer({ startPort: 0 })
@@ -176,6 +230,18 @@ describe("OAuth functions", () => {
it("sanitizeApiKey removes paste markers, control chars, and whitespace", () => {
assert.equal(sanitizeApiKey("\u001b[200~ user_manualKey\n\u001b[201~"), "user_manualKey")
})
it("validates manual API keys through whoami", async () => {
await validateApiKey("valid-key", {
fetchImpl: () => Promise.resolve(new Response(JSON.stringify({ user: {} }), { status: 200 })),
})
await assert.rejects(
validateApiKey("invalid-key", {
fetchImpl: () => Promise.resolve(new Response("unauthorized", { status: 401 })),
}),
/Invalid Command Code API key/,
)
})
})
describe("login()", () => {
@@ -242,15 +308,17 @@ describe("login()", () => {
const promptMessages: string[] = []
try {
const result = await login({
onAuth(params: { url: string }) {
authUrl = params.url
},
async onPrompt(params: { message: string }): Promise<string> {
promptMessages.push(params.message)
return promptMessages.length === 1 ? "" : "\u001b[200~ user_manualApiKey\n\u001b[201~"
},
})
const result = await withValidApiKeyFetch(() =>
login({
onAuth(params: { url: string }) {
authUrl = params.url
},
async onPrompt(params: { message: string }): Promise<string> {
promptMessages.push(params.message)
return promptMessages.length === 1 ? "" : "\u001b[200~ user_manualApiKey\n\u001b[201~"
},
}),
)
assert.match(authUrl, /^https:\/\/commandcode\.ai\/studio\/auth\/cli\?/)
assert.match(promptMessages[1] ?? "", /Paste your Command Code API key/)
@@ -265,14 +333,16 @@ describe("login()", () => {
it("accepts a directly pasted API key", async () => {
let authOpened = false
const result = await login({
onAuth() {
authOpened = true
},
onPrompt(): Promise<string> {
return Promise.resolve("user_directApiKey")
},
})
const result = await withValidApiKeyFetch(() =>
login({
onAuth() {
authOpened = true
},
onPrompt(): Promise<string> {
return Promise.resolve("user_directApiKey")
},
}),
)
assert.equal(authOpened, false)
assert.equal(result.access, "user_directApiKey")
@@ -280,21 +350,23 @@ describe("login()", () => {
it("offers an explicit API key prompt", async () => {
let promptCount = 0
const result = await login({
onAuth() {
throw new Error("browser should not open")
},
onPrompt(): Promise<string> {
promptCount += 1
return Promise.resolve(promptCount === 1 ? "key" : "user_promptedApiKey")
},
})
const result = await withValidApiKeyFetch(() =>
login({
onAuth() {
throw new Error("browser should not open")
},
onPrompt(): Promise<string> {
promptCount += 1
return Promise.resolve(promptCount === 1 ? "key" : "user_promptedApiKey")
},
}),
)
assert.equal(result.access, "user_promptedApiKey")
assert.equal(promptCount, 2)
})
it("rejects on state token mismatch", async () => {
it("keeps waiting after a state mismatch and accepts the legitimate callback", async () => {
let authUrl = ""
const callbacks = {
onAuth(params: { url: string }) {
@@ -305,12 +377,7 @@ describe("login()", () => {
},
}
const loginPromise: Promise<string> = login(callbacks).then(
() => {
throw new Error("Expected login to reject")
},
(e: Error) => e.message,
)
const loginPromise = login(callbacks)
// Wait for onAuth to be called asynchronously
while (!authUrl) await new Promise((resolve) => setTimeout(resolve, 10))
@@ -318,8 +385,8 @@ describe("login()", () => {
const url = new URL(authUrl)
const port = parseInt(url.searchParams.get("callback")?.match(/localhost:(\d+)/)?.[1] ?? "0")
// Post back with a wrong state token
await fetch(`http://127.0.0.1:${port}/callback`, {
// Post back with a wrong state token.
const invalidResponse = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "POST",
headers: { "Content-Type": "application/json", Origin: "https://commandcode.ai" },
body: JSON.stringify({
@@ -331,7 +398,20 @@ describe("login()", () => {
}),
})
const errorMsg = await loginPromise
assert.match(errorMsg, /State token mismatch/)
assert.equal(invalidResponse.status, 403)
const validResponse = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "POST",
headers: { "Content-Type": "application/json", Origin: "https://commandcode.ai" },
body: JSON.stringify({
apiKey: "user_goodState",
state: url.searchParams.get("state"),
userId: "user_123",
userName: "Real User",
keyName: "real-key",
}),
})
assert.equal(validResponse.status, 200)
assert.equal((await loginPromise).access, "user_goodState")
})
})
+1 -1
View File
@@ -133,7 +133,7 @@ function runOmp(args, timeoutMs = 30_000) {
HOME: tempHome,
USERPROFILE: tempHome,
PI_CODING_AGENT_DIR: join(tempHome, ".omp", "agent"),
COMMANDCODE_API_KEY: "mock-key",
COMMAND_CODE_API_KEY: "mock-key",
COMMANDCODE_API_BASE: `${apiBase}/provider/v1`,
COMMANDCODE_MODELS_URL: `${apiBase}/provider/v1/models`,
},
+3 -2
View File
@@ -22,7 +22,7 @@ const { writeFileSync } = require("node:fs")
writeFileSync(process.env.FAKE_PI_LOG, JSON.stringify({
args: process.argv.slice(2),
agentDir: process.env.PI_CODING_AGENT_DIR ?? null,
apiKey: process.env.COMMANDCODE_API_KEY ?? null,
apiKey: process.env.COMMAND_CODE_API_KEY ?? process.env.COMMANDCODE_API_KEY ?? null,
skipVersionCheck: process.env.PI_SKIP_VERSION_CHECK,
}))
NODE
@@ -38,7 +38,8 @@ NODE
PATH: `${fakeBin}${delimiter}${process.env.PATH ?? ""}`,
FAKE_PI_LOG: logPath,
PI_CODING_AGENT_DIR: "/existing/pi-agent",
COMMANDCODE_API_KEY: "existing-key",
COMMAND_CODE_API_KEY: "official-existing-key",
COMMANDCODE_API_KEY: "legacy-existing-key",
},
encoding: "utf8",
})
+4 -2
View File
@@ -26,7 +26,8 @@ appendFileSync(process.env.FAKE_PI_LOG, JSON.stringify({
skipVersionCheck: process.env.PI_SKIP_VERSION_CHECK,
home: process.env.HOME,
userProfile: process.env.USERPROFILE,
inheritedApiKey: process.env.COMMANDCODE_API_KEY ?? null,
inheritedApiKey:
process.env.COMMAND_CODE_API_KEY ?? process.env.COMMANDCODE_API_KEY ?? null,
}) + "\\n")
NODE
if [ "$1" = "install" ]; then exit 0; fi
@@ -42,7 +43,8 @@ exit ${exitStatus}
...process.env,
PATH: `${fakeBin}${delimiter}${process.env.PATH ?? ""}`,
FAKE_PI_LOG: logPath,
COMMANDCODE_API_KEY: "must-not-leak",
COMMAND_CODE_API_KEY: "must-not-leak-official",
COMMANDCODE_API_KEY: "must-not-leak-legacy",
},
encoding: "utf8",
})
+2 -2
View File
@@ -215,8 +215,8 @@ const env = {
PI_CODING_AGENT_DIR: agentDir,
PI_CODING_AGENT_SESSION_DIR: join(tempHome, "sessions"),
COMMANDCODE_API_BASE: `${apiBase}/provider/v1`,
COMMANDCODE_API_KEY: "mock-key",
COMMANDCODE_ZDR: "1",
COMMAND_CODE_API_KEY: "mock-key",
CMD_ZDR: "1",
COMMANDCODE_MODELS_URL: `${apiBase}/provider/v1/models`,
}
+30 -3
View File
@@ -27,7 +27,7 @@ const fixtureUrl = new URL("./fixtures/commandcode-model-ids.json", import.meta.
const fixture = JSON.parse(await readFile(fixtureUrl, "utf-8")) as ModelCatalogSnapshot
const pricingFixtureUrl = new URL("./fixtures/commandcode-pricing.json", import.meta.url)
const pricingFixture = JSON.parse(await readFile(pricingFixtureUrl, "utf-8")) as PricingSnapshot
const freeModels = new Set(["poolside/laguna-s-2.1-free", "inclusionai/ling-3.0-flash-free"])
const freeModels = new Set(["poolside/laguna-s-2.1-free", "stealth/ox-alpha"])
function assertCost(
modelId: string,
@@ -50,7 +50,7 @@ function assertCost(
describe("MODEL_COSTS pricing overlay", () => {
it("covers the current Command Code model catalog snapshot", () => {
assert.equal(fixture.source, "https://api.commandcode.ai/provider/v1/models")
assert.match(fixture.fetchedAt, /^2026-08-22T/)
assert.match(fixture.fetchedAt, /^2026-08-25T/)
const catalogIds = [...fixture.modelIds].sort()
const pricedIds = Object.keys(MODEL_COSTS).sort()
@@ -138,6 +138,24 @@ describe("MODEL_COSTS pricing overlay", () => {
cacheRead: 0.03,
cacheWrite: 0,
})
assertCost("Qwen/Qwen3.8-27B", {
input: 0.4,
output: 3,
cacheRead: 0.04,
cacheWrite: 0,
})
assertCost("google/gemini-3.7-flash", {
input: 0.75,
output: 3.75,
cacheRead: 0.075,
cacheWrite: 0.04167,
})
assertCost("meta/muse-spark-1.2-contributor", {
input: 0.1,
output: 0.2,
cacheRead: 0.002,
cacheWrite: 0,
})
})
it("uses the documented base rates for context-dependent models", () => {
@@ -165,11 +183,20 @@ describe("MODEL_COSTS pricing overlay", () => {
cacheRead: 0.02,
cacheWrite: 0.25,
})
assert.deepEqual(MODEL_COSTS["xai/grok-4.6"]?.tiers, [
{
inputTokensAbove: 200_000,
input: 4,
output: 12,
cacheRead: 1,
cacheWrite: 0,
},
])
})
it("tracks pricing provenance", () => {
assert.equal(PRICING_SOURCE_URL, "https://commandcode.ai/docs/resources/pricing-limits")
assert.equal(PRICING_LAST_VERIFIED, "2026-08-22")
assert.equal(PRICING_LAST_VERIFIED, "2026-08-25")
})
it("fails once temporary pricing needs review", () => {
+45 -4
View File
@@ -27,8 +27,18 @@ import { redactCommandCodeErrorText } from "../src/overflow.ts"
import { objectAt } from "./helpers.ts"
describe("getApiKey()", () => {
it("uses COMMANDCODE_API_KEY from provided env", () => {
assert.equal(getApiKey({ env: { COMMANDCODE_API_KEY: "env-key" }, authPaths: [] }), "env-key")
it("uses the official API key env var before the legacy alias", () => {
assert.equal(
getApiKey({
env: { COMMAND_CODE_API_KEY: "official-key", COMMANDCODE_API_KEY: "legacy-key" },
authPaths: [],
}),
"official-key",
)
assert.equal(
getApiKey({ env: { COMMANDCODE_API_KEY: "legacy-key" }, authPaths: [] }),
"legacy-key",
)
})
it("reads apiKey, commandcode, pi OAuth, and official CLI credential fields", () => {
@@ -109,11 +119,14 @@ describe("error redaction", () => {
describe("pickCommandCodeApiKey()", () => {
it("falls back to the host key for a placeholder registry value", () => {
assert.equal(pickCommandCodeApiKey("$COMMAND_CODE_API_KEY", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey("COMMAND_CODE_API_KEY", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey("$COMMANDCODE_API_KEY", "file-key"), "file-key")
assert.equal(pickCommandCodeApiKey("COMMANDCODE_API_KEY", "file-key"), "file-key")
})
it("returns undefined when only a placeholder is provided (no fallback)", () => {
assert.equal(pickCommandCodeApiKey("$COMMAND_CODE_API_KEY", undefined), undefined)
assert.equal(pickCommandCodeApiKey("$COMMANDCODE_API_KEY", undefined), undefined)
})
@@ -199,6 +212,12 @@ describe("textContent()", () => {
)
})
it("normalizes malformed string and object content", () => {
assert.equal(textContent({ content: "raw result" }), "raw result")
assert.equal(textContent({ content: { ok: true } }), '{"ok":true}')
assert.equal(textContent({ content: null }), "")
})
it("handles empty or missing content", () => {
assert.equal(textContent({ content: [] }), "")
assert.equal(textContent({}), "")
@@ -531,6 +550,23 @@ describe("messagesToCC()", () => {
assert.equal(objectAt(result, ["2", "content", "0", "output", "value"]), "hello\nworld")
})
it("preserves malformed string tool results instead of sending empty output", () => {
const result = messagesToCC([
{
role: "assistant",
content: [{ type: "toolCall", id: "c1", name: "read", arguments: {} }],
},
{
role: "toolResult",
toolCallId: "c1",
toolName: "read",
content: "raw result",
},
])
assert.equal(objectAt(result, ["1", "content", "0", "output", "value"]), "raw result")
})
it("serializes image inputs in the current Command Code wire format", () => {
assert.deepEqual(
messagesToCC(
@@ -632,7 +668,7 @@ describe("messagesToCC()", () => {
])
})
it("drops orphaned tool calls that have no matching tool result", () => {
it("synthesizes missing results for orphaned tool calls", () => {
const result = messagesToCC([
{ role: "user", content: "edit a file" },
{
@@ -651,7 +687,12 @@ describe("messagesToCC()", () => {
assert.equal(objectAt(result, ["1", "role"]), "assistant")
assert.equal(objectAt(result, ["1", "content", "0", "type"]), "text")
assert.equal(objectAt(result, ["1", "content", "1"]), undefined)
assert.equal(objectAt(result, ["1", "content", "1", "type"]), "tool-call")
assert.equal(objectAt(result, ["2", "role"]), "tool")
assert.match(
String(objectAt(result, ["2", "content", "0", "output", "value"])),
/did not complete/,
)
})
it("handles empty conversations", () => {
+1 -1
View File
@@ -68,7 +68,7 @@ describe("commandcode-quota command", () => {
})
assert.ok(pi.handler)
const ctx = context("$COMMANDCODE_API_KEY")
const ctx = context("$COMMAND_CODE_API_KEY")
await pi.handler("", ctx.value)
assert.equal(ctx.waited(), true)
assert.equal(requestKey, "fallback-key")
+2 -1
View File
@@ -7,7 +7,7 @@
* 3. Can complete a simple prompt (requires Command Code auth)
*
* Run with: node tests/test-smoke.mjs
* Requires: pi on PATH plus COMMANDCODE_API_KEY or live pi auth files.
* Requires: pi on PATH plus COMMAND_CODE_API_KEY (or legacy COMMANDCODE_API_KEY) or live pi auth files.
*/
import { spawn } from "node:child_process"
@@ -48,6 +48,7 @@ const RPC_QUERY_TIMEOUT = 60_000
function hasCommandCodeAuth() {
return (
!!process.env.COMMAND_CODE_API_KEY ||
!!process.env.COMMANDCODE_API_KEY ||
existsSync(join(homedir(), ".commandcode", "auth.json")) ||
existsSync(join(homedir(), ".pi", "agent", "auth.json"))
+119 -2
View File
@@ -77,6 +77,23 @@ describe("streamCommandCode — auth", () => {
)
})
it("accepts the official CLI API key environment variable", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
})
const { streamCommandCode } = createTestDeps({
apiBase: server.baseUrl(),
env: { COMMAND_CODE_API_KEY: "official-env-key" },
})
await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "$COMMAND_CODE_API_KEY" }),
)
assert.equal(server.lastRequestHeaders().authorization, "Bearer official-env-key")
})
it("uses options.apiKey in the Authorization header", async () => {
server.mockResponse({
type: "success",
@@ -653,7 +670,7 @@ describe("streamCommandCode — request serialization", () => {
assert.equal(objectAt(body, ["params", "stream"]), true)
assert.equal(objectAt(body, ["params", "max_tokens"]), 500)
assert.equal(objectAt(body, ["params", "reasoning_effort"]), undefined)
assert.equal(objectAt(body, ["params", "temperature"]), 0.3)
assert.equal(objectAt(body, ["params", "temperature"]), undefined)
assert.equal(objectAt(body, ["params", "system"]), "You are a test assistant.")
assert.equal(objectAt(body, ["memory"]), null)
assert.equal(objectAt(body, ["taste"]), null)
@@ -671,10 +688,53 @@ describe("streamCommandCode — request serialization", () => {
assert.equal(headers["x-command-code-version"], COMMAND_CODE_CLI_VERSION)
assert.equal(headers["x-project-slug"], "repo")
assert.equal(headers["x-taste-learning"], "true")
assert.equal(headers["x-co-flag"], "false")
assert.equal(headers["user-agent"], "cli")
assert.equal(headers["x-co-flag"], undefined)
assert.equal(headers["x-session-id"], undefined)
})
it("forwards explicit temperature and stable session metadata", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
await collectEvents(
streamCommandCode(makeModel(), makeContext(), {
apiKey: "mock-key",
temperature: 0.7,
sessionId: "11111111-1111-4111-8111-111111111111",
}),
)
const body = server.lastRequestBody()
assert.equal(objectAt(body, ["params", "temperature"]), 0.7)
assert.equal(objectAt(body, ["threadId"]), "11111111-1111-4111-8111-111111111111")
assert.equal(
server.lastRequestHeaders()["x-session-id"],
"11111111-1111-4111-8111-111111111111",
)
})
it("omits non-UUID session ids from the generate thread id", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
await collectEvents(
streamCommandCode(makeModel(), makeContext(), {
apiKey: "mock-key",
sessionId: "human-readable-session",
}),
)
assert.equal(objectAt(server.lastRequestBody(), ["threadId"]), undefined)
assert.equal(server.lastRequestHeaders()["x-session-id"], "human-readable-session")
})
it("accepts the legacy OMP nested reasoning map", async () => {
server.mockResponse({
type: "success",
@@ -917,6 +977,63 @@ describe("streamCommandCode — upstream errors and malformed streams", () => {
assert.equal(error.error.errorMessage, "provider failed")
})
it("rejects a truncated stream without a finish event", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "text-delta", text: "truncated" })],
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
)
const error = events.at(-1)
assert.equal(error?.type, "error")
if (error?.type !== "error") throw new Error("expected error")
assert.match(error.error.errorMessage ?? "", /no finish event/i)
})
it("maps an upstream abort event to an aborted request", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "abort" })],
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
)
const error = events.at(-1)
assert.equal(error?.type, "error")
if (error?.type !== "error") throw new Error("expected error")
assert.equal(error.reason, "aborted")
})
it("rejects terminal upstream network failure reasons", async () => {
server.mockResponse({
type: "success",
events: [
JSON.stringify({
type: "finish",
finishReason: "stop",
rawFinishReason: "upstream_error",
}),
],
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
)
const error = events.at(-1)
assert.equal(error?.type, "error")
if (error?.type !== "error") throw new Error("expected error")
assert.match(error.error.errorMessage ?? "", /upstream connection failed/i)
})
it("handles SSE lines, malformed lines, split chunks, and final line without newline", async () => {
const textEvent = `data: ${JSON.stringify({ type: "text-delta", text: "split" })}\n`
const finishEvent = JSON.stringify({