PR #12158 enforced read permissions for file mentions by routing
directory attachments through the permission resolver with
denyDirectory: true. That flag is set for every prompt-mention
attachment, so the read tool denied all directory listings, including
directories inside the current workspace.
Only deny directory attachments whose canonical path changed after
permission approval. A symlink swap during the permission wait moves
the resolved target, so the approved permission no longer applies and
the listing is denied. Unchanged in-workspace directories are listed
as before.
Fixes#12241
* feat: add AI image generation tool
Port the legacy generate_image tool to the opencode-based CLI as a
Kilo-owned tool gated by experimental.image_generation config flag.
- New generate_image tool with prompt/path/image/model params
- Routes through Kilo Gateway (zero-config) or BYO OpenRouter key
- Supports text-to-image generation and image editing
- Dynamic model discovery via GET /kilo/models/images endpoint
- VS Code settings toggle + live model dropdown in Experimental tab
- Writes image to disk and returns inline FilePart attachment
- Fallback model catalog for offline resilience
* refactor: change default image model to openrouter/auto
* fix: address bot review feedback
- Remove unused fetchKiloImageModels import in tool
- Normalize jpg→jpeg MIME in parser, input image, and attachment
- Replace mismatched extensions in ensureExtension (not just append)
- Add assertExternalDirectoryEffect for output path traversal guard
- Map unauthorized errors to 401 (not 400) in image models handler
- Keep last known model list on fetch failure (don't overwrite with empty)
- Fix tool description (remove false web search claim, fix grammar)
- Remove duplicated provider resolver tests
* fix: add retry for image models request to handle backend startup race
* fix(image-generation): address kilo bot review comments
- ensureExtension replaces mismatched image extensions instead of
appending (photo.jpg + PNG -> photo.png, not photo.jpg.png)
- Gateway /models/images normalizes errors to 400/401 matching every
other gateway route (was leaking undeclared upstream statuses)
- Add 401 response to openapi.json + SDK types for /kilo/models/images
to match the gateway's errors(400, 401) declaration
* fix(gateway): align /models/images error handling with other gateway routes
* refactor: switch image generation to effect HttpClient
* test: cover kilo models images endpoint in httpapi exercise scenarios
* Exclude POST-only and parameterized API endpoints from link checker
* Add ImageModelsProvider to agent manager context tree
* chore(deps): bump @openrouter/ai-sdk-provider to 2.10.0
Switches imageModel() to OpenRouter's POST /api/v1/images endpoint for
proper image usage/billing and image-specific params.
* fix: address image generation PR review feedback
- revert @openrouter/ai-sdk-provider 2.9.0->2.10.0 bump (image tool uses raw HTTP, not the SDK)
- translate image generation settings strings across all locales
- use central KILO_OPENROUTER_BASE instead of hardcoded URL fallback
- remove completed plan file
* chore: refresh source-links.md after URL refactor
* feat(memory): opt-in project memory — capture, recall, CLI + TUI integration
Add project memory: the standalone @kilocode/kilo-memory effect layer plus the
opencode CLI/server/TUI integration. Memory is disabled by default, so it is a
no-op until enabled (no behavior change when off).
Capture (turn-close consolidation): per-op parse salvage, secret redaction that
skips the offending op instead of aborting the batch, supersede-only auto-updates
(never model-driven deletes), correction-aware echo handling, non-LLM fallback
digests on interrupted/error turns, a shared interval throttle with idle-flush.
Recall + injection: keyword tokenizer with camelCase/compound splitting, light
stemming, and an English-first stopword filter (Unicode-aware; non-English falls
back to plain token-overlap), a live relevance floor, a budget-reserved startup
index, a session-digest catalog, and per-session prompt-cache pinning of the
injected memory block.
Surfaces: kilo_memory_save / kilo_memory_recall tools, the memory HTTP API
(contract schemas live in the package), and a status-focused TUI sidebar showing
auto-save, loaded context, and active recall, plus the /memory dialog.
* fix(memory): address PR review feedback
- C1: bump @kilocode/kilo-memory in the changeset
- C2: redact secrets before they hit the audit log (skip + salvage paths);
redact before truncating in salvageTyped so a secret straddling the
500-char cap can't leak an unmatched fragment; opText -> salvageText
- C3: de-abbreviate savedOperations, "changes" wording, ops.ts -> operations.ts
- C4: log.warn on the remaining silent-catch fallbacks (turn diff, memory
context injection, tool-visibility check)
- C5: relocate memory storage from ~/.kilo to Global.Path.data, delete the
now-dead needsDependencyInstall guard, add /memory status (root path) and
/memory edit ($VISUAL/$EDITOR + auto-rebuild)
- C6: replace the hardcoded English stopword list with corpus-derived
ubiquitous-term filtering (df across the user's own entries) and the
English suffix stemmer with suffix-tolerant term matching, so recall
noise-filtering works in any language
- C8: delete the CORRECTION_INTENT English regex; echo turns now run typed
capture (digest stays echo-gated), bounded by the interval throttle, with
the typed prompt as the language-agnostic content filter
- C9: exclude generated paths (dist/build/coverage/*.gen.*/*.map/snapshots)
from the durable-diff churn fallback so generated churn can't burn a
consolidation call
- C11: fix duplicated assert in httpapi-memory test; assert the error body
- kilo-code-bot batch: clause-boundary regex fix, byte-safe catalog
truncation, max-length guards on remember/correct/forget payloads (text,
query, key, sessionID), trim consistency in reconcile, param-shadowing
rename, missing doc entry for kilo_memory_recall, dead-code removal,
dialog UI fixes, memoryEnabledCache eviction bound, dedicated Configure
schema, recall permission renderer, covered-session pointer cap, redact
chat transcript before the consolidation model call, split configProtected
metadata from disableAlways so memory-save prompts don't show config-file
copy, drop unused MemoryService.layer provide from tool registry
- redact colon-separated low-entropy secrets too (password: hunterx),
accepting the prose false-positive tradeoff (secret: enabled) in favor of
not missing a real secret
- rename lastConsolidatedAt -> lastTypedConsolidationAt to make its narrow
scope (typed-consolidation throttle clock) explicit; regen openapi/SDK
- drop now-dead home/config fields from MemoryPaths.Host after the data-dir
relocation; add Process.splitCommand for quoted $EDITOR/$VISUAL paths with
spaces, used by /memory edit and the pre-existing Editor.open utility
* refactor(memory): shared client helpers, capture hardening, /memory UX rework
- extract client-side derivations into kilo-memory so both frontends share
one implementation: MemoryDecisions.summarize (decision-log summary),
MemoryAutosaveStatus.summarize (autosave-status semantics), and
MemoryMarkerMeta (marker wire contract encode/decode)
- match exact-key upserts via the canonical stored id (slugged key,
normalized section) so a re-emitted spaced/uppercase key updates the
entry instead of falling to fuzzy dedupe
- salvageTyped throws on valid JSON without an operations array so the
caller's fallback path records a parse error instead of a silent
zero-op success
- rename memory tool metadata files -> sources (stripPartMetadata rewrites
tool-part metadata.files assuming apply_patch records, mangling string[])
- read state instead of status for tool enabled checks; dedupe TUI helpers
(errorMessage, shared route(), Locale.number, relativeTime)
- /memory UX: bare /memory opens a help modal driven by a structured
command catalog in kilo-memory; /memory on|off become the canonical
toggle verbs (enable/disable kept as quiet aliases); /memory status opens
a clean overview dialog (root path, autosave, startup context, source
counts, index size) instead of a toast; /memory show is the single full
audit view (inspect removed)
Adds .trim() before .toLowerCase() so tool names like " bash" are
repaired to "bash" instead of falling through to the invalid handler.
Adds an integration test that sends a space-padded tool name through
the full AI SDK stream path and asserts the correct tool executes.
Fixes#10140
Co-authored-by: Johnny Amancio <johnnyeric@gmail.com>
Consolidate `/local-review` and `/local-review-uncommitted` into a single `/review` command that uses subcommands (`uncommitted` or `branch`) to determine the review scope.
- Replace deprecated `/local-review-*` slash commands with `/review [scope]`
- Update documentation to reflect new command syntax
- Update telemetry to track the unified `review` command
- Update test suites to validate new command parsing and behavior
- Refactor review prompt templates and logic to support the new structure
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
Provider plugins (llmgateway, nvidia, openrouter, vercel, zenmux) now
verify the provider ID before applying attribution headers, preventing
custom-defined providers sharing the same endpoint URL from being
mutated by built-in plugin logic.
Additional changes:
- Add auth-v2.json migration path for multi-account store continuity
- Remove obsolete @ai-sdk/xai patch and ConsoleCommand registration
- Add native LLM session recordings for anthropic, openai-oauth, and zen
- Update CLI help snapshots to reflect Kilo branding
- Expand i18n with usage-exceeded dialog strings (it, nl, uk)
Introduce a `metadata` method on SessionProcessor.Handle that buffers
metadata emitted before tool-call registration, then applies it on the
running transition. This decouples metadata emission timing from
tool-call lifecycle.
Downgrade virtua from 0.49.1 to 0.42.3 and migrate the virtualizer API:
- Replace `findItemIndex(scrollOffset)` with `findStartIndex()`
- Replace `bufferSize` prop with `overscan` (count-based)
Additional changes:
- Change Permission.reply return type from Promise<boolean> to Promise<void>
- Make Ruleset type readonly and remove unnecessary array spreads
- Update nvidia provider headers to reference Kilo branding
- Reorder SDK event type definitions for installation events
- Reduce promise facade allowlist in check script
Adapt Kilo-specific code to breaking changes introduced by the v1.15.9
merge:
- Migrate LLM test helpers to use LLMEvent factory constructors instead
of raw event object literals
- Update provider cost extraction from `usage.raw` to native
`usage.providerMetadata` keyed by provider name
- Preserve AI SDK raw usage in providerMetadata for billing continuity
- Replace `findStartIndex()` with `findItemIndex(scrollOffset)` in
virtualizer calls
- Replace `overscan` prop with pixel-based `bufferSize` in virtual lists
- Change provider `all` data shape from array to Map
- Remove `useCommandPalette` dependency, replaced by mode stack pattern
- Fix ToolResultValue schema to avoid circular inference with tsgo
- Add missing test layer dependencies (HttpClient, RepositoryCache,
Command)
- Remove obsolete models-snapshot files
- Fix Permission reply routing tests to expect NotFoundError instead of
boolean false
- Normalize config references and fix nested type narrowing
Convert the remaining `Effect.sleep(50)` synchronization hacks in
`prompt.test.ts` to either `Effect.yieldNow` or `waitFor` against
`SessionStatus`, mirroring the pattern already used elsewhere in the
file. Same motivation as the previous commit: stop making correctness
depend on how quickly CI schedules fibers.
- "concurrent loop callers all receive same error result": yieldNow
before resolving the gate, so the queued caller joins the run on a
scheduler turn instead of a 50ms window.
- "loop waits while shell runs ..." / "shell completion resumes
queued loop callers": yieldNow before asserting `llm.calls === 0`
while the shell is still running. The shell is already gated on
`waitFor("shell busy", ...)`.
- "cancel interrupts loop queued behind shell": waitFor shell busy
before forking the loop, then yieldNow before cancelling.
- "shell rejects when another shell is already running": waitFor
shell busy before issuing the second shell call.
The `Effect.sleep(20)` at line 93 is the poll interval inside the
`waitFor` helper itself and is left unchanged.