When the consumer (readLines) hits the line/byte cap and destroys the
PassThrough, the underlying createReadStream had no link back and would
keep reading chunks to EOF in the background, defeating the early-exit
optimisation for large files.
Optimistically stream the file as UTF-8 -- the common case -- using a
fatal-mode TextDecoder so the read tool can stop pulling bytes from
disk once the line / 50KB byte cap is hit. Only fall back to a
full-buffer iconv decode when the bytes turn out not to be valid UTF-8.
The streaming + retry logic lives in a new kilo helper
(packages/opencode/src/kilocode/text-stream.ts) so the read tool's
`lines` function stays close to upstream OpenCode shape.
Bom.readFile uses a hard-coded UTF-8 TextDecoder, so the oldContent
captured for the diff, additions/deletions counts, and permission-prompt
metadata was mojibake for any non-UTF-8 file. The actual bytes written
were correct because deriveNewContentsFromChunks already does
encoding-aware reads, but the user-visible diff did not reflect the real
file contents. Switch to EncodedIO.read to match.
Kilo isn't always BYOK; what matters here is that Kilo doesn't charge a
per-request fee on top of the upstream inference cost. So for the Kilo
provider, the gateway/marketplace `cost` would understate the user's true
spend — always prefer the upstream/market cost when present.
* feat: support Kilo-hosted indexing embeddings
* fix: format indexing settings
* fix: serialize local model persistence
* fix: annotate local model persistence
* fix: stabilize MCP OAuth browser test
* fix: annotate MCP OAuth test wait
* fix: keep KiloProvider under line cap
* fix: align Kilo indexing auth defaults
* fix(vscode): add missing indexing translations
* feat(indexing): fetch Kilo embedding model catalog
* fix(indexing): align Kilo embedding metadata fallback
* fix(gateway): remove unused indexing dependency
* fix(indexing): keep fallback catalog webview safe
* fix(agent-manager): include embedding model provider
* fix(vscode): provide embedding models in stories
* refactor(indexing): rely on Cloud embedding catalog
* fix(indexing): hydrate Kilo model metadata from Cloud
* fix(indexing): clear model when switching provider
Refactor providerCost to read `usage.raw.cost_details.upstream_inference_cost`,
which both `@ai-sdk/anthropic` (`convertAnthropicUsage`) and `@ai-sdk/openai`
(`convertOpenAIResponsesUsage`) populate with the verbatim OpenRouter usage
object. This unifies the Anthropic Messages and OpenAI Responses cost paths
through a single branch and removes the redundant `metadata.anthropic.usage`
branch (the SDK exposes the same data under both keys for Anthropic, and only
under `raw` for Responses).
They're upstream opencode comments, not kilo changes, so they
shouldn't live inside kilocode_change markers — and outside the
markers they add noise to this PR's diff. Drop them.
Add missing kilocode_change marker to the editorContext param in
the environment Effect.fn implementation; the interface signature
already had a marker but the implementation didn't.
For the Anthropic Messages API (via OpenRouter or Vercel AI Gateway), the
top-level `cost` field is the gateway fee that Kilo paid (typically 0 for
BYOK), not the user's true spend. Only use `upstream_inference_cost` /
`marketCost`; fall back to the token-based calculation when those are
absent.
Allow Azure provider connections to capture resource names or endpoint URLs so GPT-5 deployments can use the native Azure path instead of generic OpenAI-compatible setup.
Cost reporting already worked when Kilo used OpenRouter chat completions
internally. Extend providerCost to also read cost from the Anthropic
Messages API stream metadata, covering both OpenRouter (Anthropic-style
`usage.cost` / `cost_details.upstream_inference_cost`) and Vercel AI
Gateway (`gateway.cost` / `gateway.marketCost`).
Revert the file-wide marker shape fixes from fix-kilocode-markers.ts
since they touch unrelated code. Scope this PR to the two review
findings in the gpt-5 block:
- Replace nativeOpenAI refactor with upstream's if (===||===) form
plus inline // kilocode_change markers on the added npm entries;
restores the upstream reasoningSummary comment.
- Restore the upstream textVerbosity comments, keep the new provider
gate wrapped in kilocode_change start/end.
- Reverts the nativeOpenAI refactor; restores upstream's reasoningSummary
guard structure + comment so added npm entries stay inside a single
marker block.
- Includes the opencode textVerbosity comment that was dropped.
- Also fixes pre-existing marker shape issues elsewhere in the same file
flagged by script/upstream/fix-kilocode-markers.ts.
Minimizes the diff from upstream opencode by restoring upstream's
original reasoningSummary guard structure and comment, and scoping
kilocode_change markers to only the Kilo-specific additions:
two extra provider npm names and the new textVerbosity provider gate.
Keep upstream's `question` tool binding and `questionEnabled` boolean
names, restore upstream's `tool.plan` position at the end of the builtin
array, and group Kilo-only additions in a single marker block. Removes
outer markers that were wrapping unchanged upstream lines as Kilo-owned
scope.
Replaces the plugin approach with a narrow conditional at the single call
site in session/llm.ts where maxOutputTokens is passed to chat.params.
No new file, no new test file, one kilocode_change block.
gpt-5 models rejected via openai-compatible proxies (e.g. LiteLLM) because
the SDK emits max_tokens while OpenAI requires max_completion_tokens and
the compatible SDK cannot rename the field. Clear the cap so the upstream
default output budget applies.
* fix(vscode): restore inline tool diffs
* fix(vscode): render tool patches in kilo ui
* style: format long regex assignments and add change marker comment
Reformat multi-line regex match assignments in kilo-ui-contract test
to satisfy line length limits, and annotate the `contents(diff)` call
in session-diff with a kilocode_change tracking comment.
* fix(kilo-vscode): append trailing newlines to expected diff content assertions
Update test expectations in diff-session-source to include trailing
newlines in before/after content, matching actual file content behavior.
* fix(vscode): guard empty-patch diffs in session turn accordion
Match diff-session-source.ts:99 behavior by short-circuiting contents()
when the patch is empty (binary or summarized files), so the accordion
content stays empty instead of rendering a confusing whitespace-only
diff.
---------
Co-authored-by: Imanol Maiztegui <imanol.mzd@gmail.com>