Add Mercury Next Edit (NES) as an opt-in autocomplete mode - #10536
Conversation
| if (debugEnabled()) console.warn(`[NES] ${message}`) | ||
| } | ||
|
|
||
| export function disposeLog(): void { |
There was a problem hiding this comment.
WARNING: disposeLog() is exported but never called anywhere in the codebase. The OutputChannel created by getChannel() will never be disposed when the extension deactivates, leaking a VS Code UI resource.
The dispose() method on AutocompleteServiceManager and NextEditSuggestionManager both clean up their own resources, but nothing calls disposeLog(). Either:
- Call
disposeLog()fromAutocompleteServiceManager.dispose(), or - Make the channel instance-owned (pass it in or create it inside the manager) rather than module-level so it naturally participates in the disposal chain.
| import type { NextEditSuggestionManager } from "./NextEditSuggestionManager" | ||
| import type { MercuryEditRequestContext, MercuryRecentSnippet } from "./types" | ||
|
|
||
| const INLINE_COMPLETION_ACCEPTED_COMMAND = "kilocode.autocomplete.next-edit.accepted" |
There was a problem hiding this comment.
WARNING: Command ID naming inconsistency — "kilocode.autocomplete.next-edit.accepted" uses kilocode (no dash) while every other command in this extension uses the kilo-code.* prefix (e.g. kilo-code.next-edit.acceptOrJump, kilo-code.next-edit.dismiss).
This means the accepted-command fires under a different namespace than the jump/dismiss commands, breaking the naming convention established in AGENTS.md (All VSCode commands must use kilo-code.new. prefix). Since this command is attached to InlineCompletionItem and not user-facing, it won't break UX, but it will cause confusion and inconsistency in telemetry/logs.
Suggestion: rename to "kilo-code.new.autocomplete.next-edit.accepted" (or at minimum "kilo-code.next-edit.accepted") and update the NEXT_EDIT_ACCEPTED_COMMAND constant + package.json command declaration.
| if (document.uri.scheme !== "file") return undefined | ||
| const apiKey = this.deps.getApiKey() | ||
| if (!apiKey) { | ||
| nesLog("skip — no API key resolved") |
There was a problem hiding this comment.
SUGGESTION: nesLog("skip — no API key resolved") fires on every keystroke when mercury-next-edit is selected but no API key is configured, eagerly creating the OutputChannel and flooding it with messages.
Consider a silent early return here, or at minimum rate-limiting/de-duplicating this log message. A single warning on first skip (e.g. via a hasWarnedNoKey flag) would be less noisy for users who haven't finished configuring the integration.
Code Review SummaryStatus: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING| File | Line | Issue | Resolved Issues (from previous reviews)| File | Issue | Status | Files Reviewed (incremental — commit 7437f77)Changed files in this commit reviewed:
Fix these issues in Kilo Cloud Reviewed by claude-4.6-sonnet-20260217 · 656,260 tokens Review guidance: REVIEW.md from base branch |
|
Since this is a big change, I think it's best to first give a high over review, with the main points where this PR differs from where we want to take our autocomplete implementation. I understand this isnt all clear from existing code so I'll try to sketch some context where code should liveIdeally most of the code lives in the sdk/backend, not the vscode extension. Since autocomplete inherently needs to use the editor we can't move all the code to the backend, of course, but if possible it should go there. Main reason is that we want to share this code between the VS Code extension, the Jetbrains extension (and possibly the TUI, for chat autocomplete) This means that at least the request handling should move to the backend. how providers are chosenIn the old extension we did this all through magic and fallbacks, and we got our users very confused, so I think it's important the user makes the explicit choice of what model and what provider to use. Therefore I don't think we should change behaviour based on discovery of env vars. However, there was no design yet for other providers than kilo gateway, so I added that here: configurationConfiguration should be stored as much as possible via the backend, and not in vscode. autocomplete is a bad example here as we store a lot of config still in vscode, but we want to move that, we should not add new config however, and specifically not an api key. Ignoring filesWe use the FileIgnoreController to make sure the autocomplete functionality does not send .env files etc to a server, the NES should use this as well OtherI've had some agent sessions review this PR, and they had some other comments, but I think the comments above are more broad, and it would be good to do a more detailed review after adressing those |
Addresses @markijbema's architectural feedback on Kilo-Org#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 Kilo-Org#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>
47c952c to
fe60a46
Compare
| function extractFencedBody(message: string): string { | ||
| if (!message) return "" | ||
| const fenceOpen = message.indexOf("```") | ||
| if (fenceOpen === -1) return message |
There was a problem hiding this comment.
WARNING: extractFencedBody returns the raw message when no code fence is found, but the old parseMercuryEditReply (still present in editCompletionParser.ts) returned null for the same case.
If Mercury responds without a triple-backtick fence — e.g. a rate-limit prose message, a refusal, or an unexpected format — the full response text will propagate to parseSuccess as replacement, pass the replacement.length === 0 guard, and be inserted directly into the user's document as code.
Consider returning "" instead so the gateway's response schema sends content: "", which parseSuccess already treats as a no-op (replacement.length === 0 → return null):
| if (fenceOpen === -1) return message | |
| if (fenceOpen === -1) return "" |
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>
| } | ||
|
|
||
| const signal = AbortSignal.any([c.req.raw.signal, AbortSignal.timeout(EDIT_TIMEOUT_MS)]) | ||
| console.info(`[EDIT] request provider=${target.provider} model=${target.model} url=${target.url} chars=${content.length}`) |
There was a problem hiding this comment.
WARNING: console.info fires on every NES edit request via the gateway server's stdout. This is the same class of issue previously noted for fim.ts. Gateway logs should use a structured logger or be gated on a debug flag — unconditional console.info will appear in server logs on every keystroke trigger. Consider removing or replacing with the nesLog/debug-gated pattern used on the VSCode side.
| : AbortSignal.timeout(FIM_TIMEOUT_MS) | ||
|
|
||
| const response = yield* Effect.promise(async () => { | ||
| console.info(`[EDIT] request provider=${target.provider} model=${target.model} chars=${ctx.payload.content.length}`) |
There was a problem hiding this comment.
WARNING: Same console.info on every NES edit request in the Effect handler path. Both the Hono (packages/kilo-gateway/src/server/edit.ts:31) and this Effect handler log unconditionally. If either fires in production, every NES trigger produces a server-side log line. Consider removing or moving behind a debug flag.
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>
|
|
||
| // Never send an ignored file (.env, secrets, etc.) to the model. | ||
| if (this.deps.isFileAllowed && !(await this.deps.isFileAllowed(document.uri.fsPath))) { | ||
| nesLog("skip — file is gitignore/kilocodeignore-excluded") |
There was a problem hiding this comment.
WARNING: nesLog(...) fires unconditionally on every keystroke in an ignored file
For any file matching .gitignore or .kilocodeignore (e.g. .env), every single keystroke triggers provideInlineCompletionItems, the isFileAllowed check returns false, and this nesLog call appends a timestamped line to the VS Code OutputChannel. Over a few minutes of editing a .env file the channel will accumulate thousands of entries.
The previous nesLog("skip — no API key resolved") was removed for exactly this reason. The same fix applies here: guard behind debugEnabled() or remove the log entirely (the early return undefined is sufficient; the skip is observable by the absence of suggestions).
3bb55b3
|
@tfiras could you please link your GitHub account to the Kilo account? This way, we can grant you credits for the merged PRs. |
Addresses @markijbema's architectural feedback on Kilo-Org#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 Kilo-Org#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>
Summary
Adds Inception Labs' Mercury Edit 2 as a Next Edit Suggestion provider alongside the existing Codestral FIM and gateway-routed Mercury Edit 2 (FIM) options. Selecting Mercury Next Edit (Inception) from the autocomplete model dropdown routes through a new pipeline; every other choice continues through the classic FIM provider unchanged.
This PR was prepared by Inception Labs as part of our integration partnership with Kilo Code.
How to test
A self-contained HTML walk-through with 20 test scenarios is included at:
Open that file in any browser and work through the cases — it covers Python, TypeScript, Go, Rust, JavaScript, SQL, and a Markdown negative case. The same-line ghost-text path and the off-cursor decoration / jump-to-edit path are both exercised.
Quick enable
kilo-code.new.autocompletemodelto Mercury Next Edit (Inception) (not Mercury Edit 2, which keeps its original FIM-via-gateway behaviour)nextEdit.apiKeyto your Inception API key (or exportINCEPTION_API_KEY)What's new
New module at
packages/kilo-vscode/src/services/autocomplete/next-edit/:MercuryEditProvider.tsPOST /v1/edit/completions(single user message,max_tokens: 512)mercuryPromptTemplate.ts<|!@#IS_NEXT_EDIT!@#|>tokeneditCompletionParser.tseditableRegion.ts[cursor − 5, cursor + 10]window, capped at 25 linesEditHistoryTracker.tsrecentSnippetsAdapter.tsRecentlyVisitedRangesServiceoutput into Mercury's snippet shape (3–5 snippets × 20 lines)NextEditInlineCompletionProvider.tsvscode.InlineCompletionItemNextEditSuggestionManager.tslog.tsKilo Code · Next EditOutputChannel with an opt-in console mirrorBehavioural additions:
AUTOCOMPLETE_LLM_*event names withmode: \"next-edit\"— no schema change.Backwards compatibility
inception/mercury-edit-2keeps its original FIM-via-gateway behaviour. The new NES path is opt-in via a separate model id (inception/mercury-next-edit).recentlyVisitedRangesServiceonAutocompleteInlineCompletionProviderpublic readonlyso the new NES provider can read from the same LRU instead of double-tracking.Settings added
kilo-code.new.autocomplete.nextEdit.apiKeyINCEPTION_API_KEYenv var)kilo-code.new.autocomplete.nextEdit.baseUrlhttps://api.inceptionlabs.ai/v1); useful for self-hosted gatewayskilo-code.new.autocomplete.nextEdit.debugfalsePlus a new entry in the existing
kilo-code.new.autocomplete.modelenum:inception/mercury-next-edit→ label "Mercury Next Edit (Inception)".Two new keybindings, both gated on
kilo-code.nextEdit.hasPendingSuggestion:Tab→kilo-code.next-edit.acceptOrJumpEscape→kilo-code.next-edit.dismissTest plan
bun run check-types:extensioncleanbun run lint srccleanbun test src/services/autocomplete/next-edit/__tests__/— 23/23 pass (prompt template, response parser, region selector, snippets adapter)docs/mercury-next-edit-testing.htmlin the Extension Development Host with a real Inception API keymodel = inception/mercury-edit-2ormistralai/codestral-2508Notes for reviewers
/v1/edit/completionsendpoint — Mercury bakes its system prompt server-side and the endpoint actively rejects requests with arole: \"system\"message (returns HTTP 400). The constants file documents this so it doesn't get re-introduced.api.inceptionlabs.aidirectly (not via the Kilo gateway) when in NES mode. If you'd prefer to route through the gateway, thenextEdit.baseUrlsetting andMercuryEditProvider.options.modelalready make that swappable.Kilo Code · Next Editoutput channel; nothing goes to DevTools console unlessnextEdit.debug = true.Feedback we're hoping for
Reach out: firas@inceptionlabs.ai
🤖 Generated with Claude Code