* refactor(opencode): remove legacy Provider promise wrappers in favor of Effect service usage
Replace static `Provider.list`, `Provider.getModel`, `Provider.getLanguage`,
`Provider.getSmallModel`, and `Provider.defaultModel` promise helpers with
direct `Provider.Service.use()` calls through `AppRuntime.runPromise`. This
eliminates the `makeRuntime` import and the wrapper functions that bypassed
the Effect dependency injection system.
- Remove legacy promise helpers from provider.ts
- Update kilo-sessions, roll-call, commit-message, enhance-prompt, and
task tool to use Provider.Service via AppRuntime directly
- Thread Provider.Interface into KiloTask.select for proper DI
- Add Provider.defaultLayer to test layers that exercise TaskTool
- Update commit-message tests to spy on CommitMessageRuntime.model()
* docs(sdk): regenerate v2 SDK types and update edit endpoint description
Extract KiloModelsService from model-cache to enable dependency injection
and rewrite test files to use Layer-based test harnesses instead of
process-wide mock.module() calls that caused flaky cross-test pollution
on CI.
* refactor(opencode): remove raw LLM interface in favor of stream-based text helper
Introduce KiloLLM.text() to consume Effect streams and extract text
while propagating error/abort events. Replace all usages of the removed
LLM.raw() method and the legacy LLM.stream() async wrapper with
dedicated runtime helpers (CommitMessageRuntime.generate,
PlanFollowupRuntime.handover) that leverage the new stream-based
approach.
- Add KiloLLM.text() stream combinator in kilocode/session/llm.ts
- Remove Interface.raw and the exported LLM.stream() async function
- Refactor commit-message generation to use CommitMessageRuntime
- Refactor plan-followup handover to use PlanFollowupRuntime.handover
- Update all test LLM mocks to remove raw() stubs
- Add unit tests for KiloLLM.text() covering text joining, error
propagation, and abort handling
* feat(skill): migrate Skill module to Effect service pattern and remove legacy promise wrappers
Remove the makeRuntime-based promise helpers (Skill.all, Skill.get,
Skill.dirs) from the skill module and convert the builtin-skills test
suite to use Effect generators with the testEffect harness.
- Delete legacy runPromise wrappers from packages/opencode/src/skill/index.ts
- Rewrite builtin-skills.test.ts to use testEffect and Effect.gen
- Replace WithInstance.provide/tmpdir with TestInstance yield pattern
- Use Skill.Service directly within Effect generators for all assertions
* refactor(session-status): drop makeRuntime promise helpers and wire Service through Effect context
Replace the standalone `SessionStatus.list`, `.get`, `.set` promise
wrappers with direct `SessionStatus.Service` usage via Effect generators
and `AppRuntime.runPromise` at Kilo callsites that remain imperative.
- Remove makeRuntime-based exports from session/status.ts
- Update kilo-sessions.ts and plan-followup.ts to use AppRuntime.runPromise
with SessionStatus.Service
- Thread SessionStatus.Service as a dependency through SuggestTool and
ToolRegistry layers
- Replace spy-based mocks in suggestion tool tests with an in-memory
service stub for deterministic assertions
* refactor(opencode): migrate ModelCache and Config to effect-native services
Remove legacy async wrapper functions from Config module and convert
ModelCache from a stateful namespace with module-level Maps into a
proper Effect service with Context/Layer semantics.
Key changes:
- Delete Config's `makeRuntime`-based async wrappers (get, getGlobal,
update, warnings, etc.) — all callsites now use
`Config.Service.use(...)` through AppRuntime
- Rewrite ModelCache as an Effect service with HttpClient dependency
injection, replacing imperative Map-based caching with Effect-native
Ref cells and TTL logic
- Convert KiloSessions.init and KilocodeBootstrap.init into proper
Effect services with Layer-based dependency injection
- Wire ModelCache.Service into AppLayer, ProviderAuth, ModelsDev, and
HTTP API handler layers
- Update Permission.layer to depend on Config.Service directly instead
of calling Config async wrappers
- Add new test files for KiloSessions and ModelCache Effect integration
- Remove stale Config.get spyOn mocks from tests that no longer need
them (experimental-session-list, recall)
- Fix indexing-auth to use typed IndexingConfig parameter instead of
untyped record access
* fix(model-cache): resolve race conditions in concurrent fetch and cache invalidation
Introduce versioned cache cells with proper key derivation to prevent
stale responses from overwriting fresher data during concurrent fetches.
- Add version tracking to detect and discard outdated fetch results
- Derive cache keys from provider-specific options (baseURL, token, apiKey)
to isolate concurrent requests with different credentials
- Make ModelCache.clear async to properly await invalidation across layers
- Update OrganizationDeps.clear signature to allow Promise<void> return
- Add concurrency and ordering tests for fetch/refresh race scenarios
- Rename local variable from `state` to `entry` in kilo-sessions sync loop
* chore(opencode): remove duplicate imports and fix test layer composition
Remove duplicate `AppRuntime` imports introduced during merge and update
kilo-sessions tests to use Effect-native Auth service instead of static
module calls.
- Remove duplicate `AppRuntime` import in index.ts and instance.ts
- Add Auth.defaultLayer to test layer helper
- Refactor test to yield Auth.Service and use instance methods
- Reorder Effect.provide/Effect.ensuring for correct resource cleanup
* style(opencode): normalize kilocode_change marker comments to block format
Standardize inline `// kilocode_change` annotations across source and
test files to use consistent `// kilocode_change start` / `// kilocode_change end`
block delimiters, improving readability and grep-ability of custom
modifications.
Addresses the two remaining points from @markijbema's review.
Prompt templating now lives in the gateway
-------------------------------------------
The Mercury sentinel-prompt assembly moved out of the VSCode extension
into packages/kilo-gateway/src/edit-prompt.ts. Clients now send
structured editor context (currentFileContent, cursor position,
editable region, recently-viewed snippets, edit-diff history) and the
gateway builds the sentinel-tagged prompt. This keeps the entire
Mercury contract — endpoint, auth, prompt format, response parsing — in
one place that VS Code, JetBrains, and the TUI can all share, instead
of each editor re-implementing the templating.
- New EditBody is the structured context (was: a pre-built `content`
string). Updated the opencode HttpApi schema, the hono zod validator,
both handlers, and regenerated the SDK.
- Deleted the VSCode-side mercuryPromptTemplate.ts (+ spec); the tests
moved to packages/kilo-gateway/test/edit-prompt.test.ts.
- VSCode constants.ts now holds only the editable-region sizing; the
sentinel tokens live in the gateway.
FileIgnoreController
--------------------
NES must not send ignored files (.env, secrets, anything matched by
.gitignore/.kilocodeignore) to the server. The NES provider now:
- skips the request entirely if the active document fails
ignoreController.validateAccess(), and
- filters recently-viewed snippets through the same controller before
they go into the prompt.
It reuses the classic provider's FileIgnoreController instance (now
public) rather than building a second one.
Also: dropped the implicit nextEdit.debug config read in log.ts (debug
is env-only via KILO_NES_DEBUG) so no VSCode autocomplete config is
added — per the "config should move to the backend" guidance.
Validation: typecheck clean across kilo-gateway, opencode, kilo-vscode;
lint clean; gateway 46 tests, vscode next-edit 10 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-agent review pass on the gateway-routed NES change. Fixes:
Correctness (🔴)
- MercuryEditProvider read the HTTP status off the parsed error body
(`error.status`, always undefined), so 401/402 never reached
onFatalError and NES had no credit-exhausted/auth backoff. Now reads
`response.status` from the SDK result.
- NextEditSuggestionManager applied "insert" suggestions without the
apply-time drift re-validation the "replace" path already had — edits
between the anchor and insertion point could land the insert in the
wrong place. Now re-checks the anchor line before inserting.
- The opencode Effect edit handler collapsed every upstream failure to
HTTP 400; now passes the real status through (mirrors the FIM handler)
so 401/402/429/5xx are distinguishable under the experimental backend.
Conciseness / DRY (🔴)
- Deleted dead module editCompletionParser.ts (+ spec): the gateway
unwraps the fence server-side now, so the VSCode-side parser was unused.
- Hoisted the triplicated extractFencedBody into a single exported
function in kilo-gateway/src/edit.ts; both the hono and Effect handlers
import it. Added a shared EditUpstreamResponse type to replace three
inline copies.
Robustness (🟡)
- extractFencedBody now keeps the body when the closing fence is missing
(truncated/max_tokens output) instead of dropping the suggestion.
- EditHistoryTracker seeds snapshots on document open so the first edit
in a freshly-opened file is captured (was previously dropped).
- Single-line inserts that span non-blank lines below the cursor (a
multi-line→single-line collapse) now route to the decoration path
instead of emitting a ghost item VSCode can't render.
Cleanup (🟡)
- Removed unused constants (MERCURY_EDIT_MODEL_ID, INCEPTION_API_BASE_URL,
INCEPTION_EDIT_PATH).
- getProviderKey typed to DirectAutocompleteProviderID (matches FIM).
- resolveEditTarget keys on kind==="edit" defensively, so a future
FIM-only Inception model can't resolve to the edit endpoint.
- AutocompleteModelDef doc comments made endpoint-neutral (not "FIM").
- Declared the internal accept command in contributes.commands.
Tests
- New packages/kilo-gateway/test/edit.test.ts: resolveEditTarget routing
(incl. the mercury-edit-2 FIM model must NOT reach the edit endpoint)
and extractFencedBody variants (lang tag, sentinels, truncation, blank
lines, no-fence).
- typecheck clean across kilo-gateway, opencode, kilo-vscode; lint clean;
all unit tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses @markijbema's architectural feedback on #10536: moves the HTTP
edit-completion call to the gateway, removes the standalone API-key
setting, and aligns with the provider/model selection design introduced
in #10559.
Wire-level changes
------------------
* New `/kilo/edit` endpoint added to the opencode HttpApi contract
(`packages/opencode/src/kilocode/server/httpapi/groups/kilo-gateway.ts`)
and mirrored into `packages/kilo-gateway/src/server/routes.ts` for the
hono surface. SDK regenerated; `client.kilo.edit({ content, provider,
model, maxTokens })` is now available.
* `packages/kilo-gateway/src/edit.ts` — `EditTarget` resolver mirroring
the FIM pattern. Only the Inception provider is wired today (Mistral
doesn't expose a comparable surface); Kilo Gateway has a placeholder
branch that returns 400 until a server-side proxy exists.
* `packages/kilo-gateway/src/server/edit.ts` — `createEditHandler` reads
the Inception BYOK key from `Auth.get("inception")` and falls back to
`INCEPTION_API_KEY` from env, exactly like the FIM handler.
* The gateway unwraps Mercury's triple-backtick fence (and any
`<|code_to_edit|>` sentinels) server-side so the VSCode response is
just the rewritten code.
* `AutocompleteModelDef` gains an optional `kind: "fim" | "edit"`
discriminator; new entry `inception/mercury-next-edit` (label "Mercury
Next Edit") sets `kind: "edit"` and shares the wire model
`mercury-edit-2`.
VSCode-side
-----------
* `MercuryEditProvider` no longer does its own HTTP — it's now a thin
wrapper around `client.kilo.edit(...)` via `KiloConnectionService`.
* Dropped `kilo-code.new.autocomplete.nextEdit.apiKey` and
`.nextEdit.baseUrl` settings. Auth and routing live in the gateway.
* The `AutocompleteServiceManager` dispatch now switches on the model's
`kind` field (set by `getAutocompleteModel(provider, model)`) instead
of string-comparing a model id, matching Mark's provider+model split.
* The `NextEditInlineCompletionProvider`,
`NextEditSuggestionManager`, prompt template, parser, editable-region
selector, edit-history tracker, recently-viewed-snippets adapter, and
decoration-based jump-to-edit UX remain in the VSCode extension since
they need editor-specific APIs (`InlineCompletionItem`,
`TextEditorDecorationType`, keybinding context keys).
Bot review nits resolved
------------------------
* `INLINE_COMPLETION_ACCEPTED_COMMAND` renamed `kilocode.*` →
`kilo-code.*` to match the project convention.
* `kilo-code.next-edit.acceptOrJump` and `.dismiss` now declared in
`contributes.commands` so VS Code can resolve them in the palette.
* `disposeLog()` wired into `AutocompleteServiceManager.dispose()` so
the dedicated "Kilo Code · Next Edit" OutputChannel doesn't leak.
* Per-keystroke "skip — no API key resolved" log removed (the entire
API-key code path is gone).
Tests
-----
* `bun run check-types:extension` clean
* `bun run lint src` clean
* `bun test src/services/autocomplete/next-edit/__tests__/` — 23/23
pass
Docs
----
* The partner walkthrough at
`packages/kilo-vscode/docs/mercury-next-edit-testing.html` and the
20-test playground under
`packages/kilo-vscode/docs/nes-examples/` survive from the prior
iteration. The walkthrough's "Install the PR locally" section still
applies (the model dropdown choice is now "Mercury Next Edit
(Inception)" — the API-key step is gone since BYOK is plumbed through
the gateway's Auth store).
Known follow-ups (not in this commit)
-------------------------------------
* `FileIgnoreController` plumbing through the NES context builder so
`.env`-style files don't get sent. Hook point identified in
`NextEditInlineCompletionProvider.buildRequestContext`.
* Settings UI changes in the webview to expose Mercury Next Edit as a
selectable provider/model pair alongside the FIM entries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `urls: string[]` with provider-specific shapes: kilo and inception
each carry a single `url`, mistral carries no urls (its dual-endpoint
fallback is encapsulated in requestMistralFim). Removes the non-null
assertion on `urls[0]` and the dead "No FIM endpoint configured" branch.
Also tightens `getProviderKey` to take `DirectAutocompleteProviderID`,
which now follows naturally from the narrowed union and prevents the
`DIRECT_FIM_ENV["kilo"]` undefined-lookup footgun.
* refactor(opencode): migrate agent promise helpers to kilocode module and adopt Effect-based tests
Remove legacy promise-based helpers (`get`, `list`, `defaultAgent`, `remove`)
from the core agent module and update callsites to use the dedicated
`@/kilocode/agent` module directly. Migrate kilocode-specific tests from
`WithInstance.provide` patterns to the `testEffect` helper with Effect
generators for cleaner, more idiomatic test code.
- Remove `makeRuntime` import and exported promise helpers from agent.ts
- Update HTTP API handlers to import from `@/kilocode/agent` instead of
re-exported `Agent` namespace
- Rewrite agent tests to use `load()` helper or Effect service access
- Convert agent-global-config-dirs and agent-skill-permissions tests to
`testEffect` pattern
- Reorder SDK type definitions (BackgroundProcessLogs/WorkspaceWarpError)
* chore(auth): remove legacy promise helpers and replace with direct Effect service access
Eliminate the exported promise-based convenience functions (`get`, `all`,
`set`, `remove`) from the Auth module and replace all callsites with
explicit `AppRuntime.runPromise` or `makeRuntime` invocations that access
`Auth.Service` directly through Effect's service pattern.
- Delete `makeRuntime`-backed promise helpers from auth/index.ts
- Update CLI entrypoint, kilo-sessions, indexing, and server instance to
use `AppRuntime.runPromise(Auth.Service.use(...))` for auth access
- Inject `Auth.Service` as a dependency into the ModelsDev layer and
consume it via Effect generator instead of promise wrapper
- Create a local `makeRuntime` instance in model-cache.ts for non-Effect
callsites that still need promise-based auth access
- Rewrite tests to manipulate auth.json directly on disk with proper
save/restore semantics instead of relying on removed helpers
* refactor(suggestion): convert suggest tool to Effect-native with injected Command dependency
Transform the suggestion tool from async/promise-based implementation to
idiomatic Effect generators with explicit dependency injection of the
Command service rather than importing and calling module-level helpers.
- Convert `resolvePrompt` from async function to Effect generator that
accepts a `Command.Interface` parameter
- Refactor `SuggestTool` definition to yield `Command.Service` from the
Effect context and thread it through to `resolvePrompt`
- Add `Command.Service` as a dependency to the tool registry layer and
provide `Command.defaultLayer` in both production and test wiring
- Remove unused `makeRuntime` import and exported `get` helper from
command/index.ts
- Add explicit type annotations to Auth delegate in server instance
- Rewrite suggestion tests to use `testEffect` helper with a mock
`Command.Service` layer instead of spying on module exports
* feat(git): migrate WorktreeFamily to Effect service and wire Git.Service as dependency
Convert WorktreeFamily.list from an async function using legacy promise
helpers to an Effect generator that yields Git.Service from context,
eliminating the need for the removed `run` promise wrapper in git/index.
- Replace `WorktreeFamily.list()` async function with Effect.fn generator
that obtains Git.Service and InstanceState from the Effect context
- Remove legacy `makeRuntime`/`run`/`runPromise` exports from git module
- Update RecallTool to thread Git.Service through to WorktreeFamily calls
via EffectBridge
- Add Git.Service as a required dependency in tool registry and HTTP
server route layers
- Update all test layers to provide Git.defaultLayer
* chore(mcp): replace legacy promise helpers with Effect-native AppRuntime calls
Remove exported promise-based `status`, `connect`, and `disconnect`
helpers from MCP module and convert the network recovery callsite in
SessionNetwork to use AppRuntime.runPromise with Effect.gen directly.
* refactor(auth): adopt makeRuntime helper for Auth service resolution in kilo modules
Replace AppRuntime.runPromise with locally scoped makeRuntime instances
in kilo-sessions and kilocode/indexing modules, removing the dependency
on the global AppRuntime singleton for Auth service access.
* fix(test): simplify cleanup error handling in provider test
Replace try-catch block with promise .catch() for file unlink operation
during test teardown.