diff --git a/src/extension.ts b/src/extension.ts index 58c4d7c..a42d6b0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -138,8 +138,9 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef ), ); - // F3 SLICE-5: the live turn (PUC-2) — selection + instruction → one - // claude-code SDK turn (liveTurn.ts, INV-8) → the applyAgentEdit seam (INV-9). + // F3 SLICE-5 → F4 SLICE-4: the live turn — selection + instruction → one + // claude-code SDK turn (liveTurn.ts, INV-8) → a PENDING PROPOSAL (F4, + // INV-10); the applyAgentEdit seam (INV-9) now fires only on accept. context.subscriptions.push( vscode.commands.registerCommand("cowriting.editSelection", async () => { const editor = vscode.window.activeTextEditor; @@ -163,8 +164,13 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef } const document = editor.document; const selection = editor.selection; - const expectedVersion = document.version; const selectedText = document.getText(selection); + // Capture the anchor BEFORE the turn (spec §6.5 PUC-1): mid-turn edits + // can't skew it — the proposal renders wherever the target re-resolves. + const fp = buildFingerprint(document.getText(), { + start: document.offsetAt(selection.start), + end: document.offsetAt(selection.end), + }); const turnId = `turn-${Date.now().toString(36)}`; try { await vscode.window.withProgress( @@ -174,24 +180,32 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef const turn = await runEditTurn(instruction, selectedText); if (turn.replacement === "") { void vscode.window.showWarningMessage( - "Cowriting: Claude returned an empty replacement — nothing was applied.", + "Cowriting: Claude returned an empty replacement — nothing was proposed.", ); return; } - const ok = await attributionController.applyAgentEdit( + if (turn.replacement === selectedText) { + void vscode.window.showInformationMessage( + "Cowriting: Claude proposed no change to the selection.", + ); + return; + } + // F4 (INV-10): the turn ends in a PROPOSAL, not a buffer mutation. + // The seam now fires only on accept (ProposalController, INV-9). + const id = await proposalController.propose( document, - new vscode.Range(selection.start, selection.end), + fp, turn.replacement, { kind: "agent", id: "claude", agent: { sdk: "@cline/sdk", model: turn.model, sessionId: turn.sessionId }, }, - { expectedVersion, turnId }, + { turnId, instruction }, ); - if (!ok) { - void vscode.window.showWarningMessage( - "Cowriting: the edit could not be applied (the document changed during the turn, or the editor rejected the edit) — nothing was changed.", + if (id) { + void vscode.window.showInformationMessage( + "Cowriting: Claude proposed an edit — review the diff at the highlighted range (✓ accept / ✗ reject).", ); } },