feat(chat): Comments and review in VS Code, like the OpenChamber desktop app (#1724)

* feat(chat): render code comments as cards instead of fenced text

* fix(vscode): route Add Comment to the active session editor panel

* fix(chat): persist queued inline comments and tighten file-chip path matching

* feat(vscode): comment on code from the editor

* fix(chat): keep attached context in the message and broadcast comment removal

* fix(vscode): hold every pending comment and gate both entry points on the workspace

* fix(vscode): let only the owning surface decide its comment threads

* fix(vscode): drop a comment removed while its delivery was still in flight

* test(vscode): cover the in-flight comment removal guard

* test(vscode): cover comment removal reaching every chat surface

* fix(vscode): give up on a comment the chat never confirmed holding

* fix(vscode): retract a comment everywhere before reporting it discarded

* fix(chat): preserve queued comment cards

* fix: preserve inline comment context across send paths

* fix(chat): preserve command routing with context

* fix(chat): keep unavailable actions on normal send path

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
Felipe Gené
2026-09-05 12:28:17 +03:00
committed by GitHub
co-authored by Bohdan Triapitsyn
parent d323b51a0a
commit a12b9be443
35 changed files with 2192 additions and 131 deletions
+33
View File
@@ -1,5 +1,6 @@
import { beforeEach, describe, expect, mock, test } from 'bun:test';
import type { Message, Part, Session } from '@opencode-ai/sdk/v2';
import type { StartBtwInput } from './btw';
let forkSessionImpl: (sessionId: string, messageId?: string, directory?: string | null) => Promise<Session>;
let getSessionMessagesImpl: (id: string, limit?: number, directory?: string | null) => Promise<Array<{ info: Message; parts: Part[] }>>;
@@ -213,6 +214,38 @@ describe('startBtwSession', () => {
expect(sentParts).toEqual([[{ text: BTW_BOUNDARY_INSTRUCTION, synthetic: true }]]);
});
test('the first question keeps inline comment context', async () => {
forkSessionImpl = () => Promise.resolve(makeSession('fork-1', '/project'));
const commentPart: NonNullable<StartBtwInput['additionalParts']>[number] = {
text: 'Comment on `src/auth.ts` lines 4-4:\n```ts\nauth();\n```\n\ncheck this',
synthetic: true,
metadata: {
openchamberContext: {
kind: 'code-comment',
source: 'file',
fileLabel: 'src/auth.ts',
startLine: 4,
endLine: 4,
language: 'ts',
code: 'auth();',
text: 'check this',
},
},
};
let sentParts: unknown;
sendMessageImpl = (...args) => {
sentParts = args[6];
return Promise.resolve();
};
await startBtwSession({ ...startInput, additionalParts: [commentPart] });
expect(sentParts).toEqual([
{ text: BTW_BOUNDARY_INSTRUCTION, synthetic: true },
commentPart,
]);
});
test('an empty parent produces a marker without a boundary', async () => {
forkSessionImpl = () => Promise.resolve(makeSession('fork-1', '/project'));
getSessionMessagesImpl = () => Promise.resolve([]);