@volter/supercode-frontend-vscode 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -373,3 +373,14 @@ routing: stock code broadcasts changes to all input states of a provider. The
373
373
  Volter overlay guards this fix with a native regression test. The former legacy
374
374
  option-notification queue is no longer required. The narrow reveal/focus bridge
375
375
  remains because the native model catalogue and participant identity are provider-wide.
376
+
377
+ ### Native approval cards
378
+
379
+ A host advertising `volter.chat.inTurnApprovalCapability` version 1 presents
380
+ actual approval requests in its native confirmation card. The original offered
381
+ labels, order and response payloads are preserved. Clicking answers the pending
382
+ request inside the current turn through `supercode.frontend.respondInTurnApproval`;
383
+ it never submits a new chat prompt. The host guards the addressed conversation,
384
+ disables the choices during dispatch and retires them only after an acknowledged
385
+ response or an already-resolved result. Hosts without this capability retain
386
+ stable command buttons. Other request kinds retain their existing presentation.
package/dist/extension.js CHANGED
@@ -184,6 +184,7 @@ class SupercodeFrontends {
184
184
  this.#changed.dispose();
185
185
  }
186
186
  }
187
+ let nativeInTurnApprovalCards = false;
187
188
  function writeProjected(stream, projected, granted, state) {
188
189
  for (const candidate of projected) {
189
190
  const parts = resolveParts([candidate], granted);
@@ -235,6 +236,12 @@ function writePart(stream, part, state) {
235
236
  return;
236
237
  }
237
238
  case 'answers':
239
+ if (part.approval && nativeInTurnApprovalCards && part.answers.length > 0) {
240
+ stream.confirmation(part.title, part.message, {
241
+ kind: 'supercode.inTurnApproval', version: 1, answers: part.answers,
242
+ }, part.answers.map(answer => answer.label));
243
+ return;
244
+ }
238
245
  // Stable API on purpose: a command button runs INSIDE the live turn,
239
246
  // which is when a supercode approval is raised. See `view.ts`.
240
247
  stream.markdown(`\n\n**${part.title}**\n\n${part.message}\n\n`);
@@ -300,6 +307,20 @@ export async function activate(context) {
300
307
  // description, so this is the grant, not the request.
301
308
  const granted = grantedProposals(context.extension.packageJSON);
302
309
  log.info(`Volter Harness: proposals requested ${REQUESTED_PROPOSALS.join(', ')}; granted ${granted.size ? [...granted].join(', ') : 'none (stable chat parts only)'}`);
310
+ // Explicit workbench support is required: stock confirmation clicks send
311
+ // a new prompt. A missing capability keeps the stable in-turn buttons.
312
+ if (granted.has('chatParticipantAdditions')) {
313
+ try {
314
+ const commands = await vscode.commands.getCommands(true);
315
+ if (commands.includes('volter.chat.inTurnApprovalCapability')) {
316
+ const capability = await vscode.commands.executeCommand('volter.chat.inTurnApprovalCapability');
317
+ nativeInTurnApprovalCards = capability?.version === 1;
318
+ }
319
+ }
320
+ catch (error) {
321
+ log.warn(`Native in-turn approval cards unavailable: ${String(error)}`);
322
+ }
323
+ }
303
324
  const controls = await ChatHostControls.fromEnvironment();
304
325
  const offerChatGPT = await chatGPTAvailable();
305
326
  let signingInWithChatGPT;
@@ -1059,34 +1080,36 @@ export async function activate(context) {
1059
1080
  */
1060
1081
  async function answerRequest(answer) {
1061
1082
  if (!answer || typeof answer.requestId !== 'number')
1062
- return;
1083
+ return { status: 'unavailable' };
1063
1084
  const connection = frontends.get(answer.connectionId);
1064
1085
  if (!connection) {
1065
1086
  void vscode.window.showWarningMessage('Volter Harness is no longer attached to that runtime.');
1066
- return;
1087
+ return { status: 'unavailable' };
1067
1088
  }
1068
1089
  const request = connection.session.pendingRequests.get(answer.requestId);
1069
1090
  if (!request) {
1070
1091
  void vscode.window.showInformationMessage(`Volter Harness: request #${answer.requestId} is already resolved.`);
1071
- return;
1092
+ return { status: 'resolved' };
1072
1093
  }
1073
1094
  let response = answer.response;
1074
1095
  try {
1075
1096
  if (answer.promptForContent) {
1076
1097
  const content = await requestContent(request);
1077
1098
  if (content === undefined)
1078
- return;
1099
+ return { status: 'cancelled' };
1079
1100
  response = { ...response, content };
1080
1101
  }
1081
1102
  await connection.session.respond(response);
1082
1103
  void vscode.window.setStatusBarMessage(`Volter Harness: ${resolutionSentence(response, request, answer.requestId)}`, 4000);
1104
+ return { status: 'responded' };
1083
1105
  }
1084
1106
  catch (error) {
1085
1107
  if (error instanceof RequestNotPendingError) {
1086
1108
  void vscode.window.showInformationMessage(`Volter Harness: request #${answer.requestId} is already resolved.`);
1087
- return;
1109
+ return { status: 'resolved' };
1088
1110
  }
1089
1111
  void vscode.window.showErrorMessage(`Volter Harness could not answer request #${answer.requestId}: ${String(error?.message ?? error)}`);
1112
+ return { status: 'failed' };
1090
1113
  }
1091
1114
  }
1092
1115
  const secretKey = (baseUrl) => `supercode.frontend.token:${baseUrl}`;
@@ -1132,7 +1155,7 @@ export async function activate(context) {
1132
1155
  // The one door an answer button uses. It answers the runtime's pending
1133
1156
  // request through `frontend.v2.respond` and narrates itself, because a
1134
1157
  // click whose effect is invisible is a click nobody can trust.
1135
- vscode.commands.registerCommand('supercode.frontend.respond', (answer) => answerRequest(answer)), vscode.commands.registerCommand('supercode.frontend.status', async (inspect) => {
1158
+ vscode.commands.registerCommand('supercode.frontend.respond', async (answer) => { await answerRequest(answer); }), vscode.commands.registerCommand('supercode.frontend.respondInTurnApproval', (answer) => answerRequest(answer)), vscode.commands.registerCommand('supercode.frontend.status', async (inspect) => {
1136
1159
  if (controls)
1137
1160
  await controls.refresh();
1138
1161
  if (inspect === true)
package/dist/view.d.ts CHANGED
@@ -15,15 +15,10 @@ export declare const VSCODE_CHAT_LIMITS: Readonly<{
15
15
  /**
16
16
  * What one answer button sends back to the runtime.
17
17
  *
18
- * MEASURED: `ChatResponseConfirmationPart` is the wrong mechanism here. The
19
- * editor answers a confirmation by sending a NEW chat request carrying the
20
- * part's data (`chatConfirmationContentPart`), and a supercode approval is
21
- * raised WHILE the turn is still running — `decide_approval` in
22
- * crates/harness/src/server.rs blocks the agent loop until it is answered — so
23
- * the editor refuses that second request and the click does nothing. An
24
- * answer is therefore a COMMAND button: it runs inside the live turn, it is
25
- * stable API (no proposal), and each button carries its own answer instead of
26
- * the one payload a confirmation splits across accepted/rejected.
18
+ * Stock confirmations send a new chat request and cannot answer a running
19
+ * harness turn. Hosts advertising the versioned in-turn approval capability
20
+ * reuse their native confirmation card while dispatching this exact answer.
21
+ * Other hosts retain stable command buttons inside the running turn.
27
22
  */
28
23
  export interface ChatAnswer {
29
24
  connectionId: string;
@@ -67,6 +62,7 @@ export type ChatPart = {
67
62
  resultText?: string;
68
63
  } | {
69
64
  part: 'answers';
65
+ approval: boolean;
70
66
  title: string;
71
67
  message: string;
72
68
  answers: ChatAnswer[];
package/dist/view.js CHANGED
@@ -386,28 +386,12 @@ export function projectEvent(event, context) {
386
386
  const summary = requestSummary(request);
387
387
  const answers = requestAnswers(request, context.connectionId);
388
388
  const subject = requestSubject(request);
389
- const title = subject
390
- ? `Approve ${subject}`
391
- : `${request.kind === 'approval' ? 'Approval' : 'Assistant'} request #${request.id}`;
392
- // Command buttons, and MEASURED rather than assumed. Two richer
393
- // mechanisms were tried against a live runtime in the editor's own Chat
394
- // view and both failed, so neither ships:
395
- // `ChatResponseConfirmationPart` retires its own buttons on click, but
396
- // the click answers by sending a NEW chat request, and a supercode
397
- // approval is raised while the turn still runs (`decide_approval`
398
- // blocks the agent loop) — the editor refuses that request, so the
399
- // click does nothing and the runtime waits forever.
400
- // `stream.questionCarousel` resolves inside the running turn and the
401
- // widget retires itself — the right shape on paper. Observed: the
402
- // widget renders with its options, Submit retires it, and the
403
- // promise never delivers an answer this frontend can read, so the
404
- // runtime is left blocked. A preferred path that silently swallows
405
- // the answer is worse than a plain one that works.
406
- // A command button runs inside the live turn and carries its own answer,
407
- // so the runtime's offered answers remain usable. It cannot be withdrawn
408
- // once pushed (`chatCommandContentPart` enables on `!isStale` and has no
409
- // used-state), so `supercode.frontend.respond` refuses a request that is
410
- // no longer pending and SAYS so instead of sending a stale answer.
389
+ const title = request.kind === 'approval'
390
+ ? 'Approval required'
391
+ : subject ? `Approve ${subject}` : `Assistant request #${request.id}`;
392
+ // The host capability decides whether these original answers can use
393
+ // a native in-turn confirmation card. Stock confirmation clicks start a
394
+ // new chat request, so stable command buttons remain the fallback.
411
395
  if (context.canRespond === false || (request.payload && typeof request.payload === 'object' && !Array.isArray(request.payload) && request.payload.observe_only === true)) {
412
396
  return [stable({
413
397
  part: 'markdown',
@@ -416,6 +400,7 @@ export function projectEvent(event, context) {
416
400
  }
417
401
  return [stable({
418
402
  part: 'answers',
403
+ approval: request.kind === 'approval',
419
404
  title: clamp(title, VSCODE_CHAT_LIMITS.titleCharacters),
420
405
  message: summary,
421
406
  answers,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@volter/supercode-frontend-vscode",
3
3
  "displayName": "Volter Harness",
4
- "version": "0.1.32",
4
+ "version": "0.1.34",
5
5
  "type": "module",
6
6
  "description": "VS Code chat-view frontend for an existing Volter Harness SDK runtime",
7
7
  "publisher": "volter-ai-dev",