@volter/supercode-frontend-vscode 0.1.37 → 0.1.38

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
@@ -160,7 +160,8 @@ default is never stored as a choice (a pick equal to the default follows the def
160
160
  changes), and an Auto approve is never stored for a conversation that cannot answer. Conversations opened before 0.1.36 saved their picker's
161
161
  **Ask** when opened and keep it. Auto approve approves the requests the harness would ask you about, including requests to run
162
162
  outside its sandbox (Codex's approval requests are its sandbox escalations); the harness's deny
163
- rules still apply. Each request it answers is noted in the turn ("auto-approved: …"). Unsupported modes are refused. Unanswerable
163
+ rules still apply. Each request it answers is noted in the turn ("auto-approved: …"). A turn
164
+ re-joined after a page reload (the conversation's active response) is answered the same way. Unsupported modes are refused. Unanswerable
164
165
  harnesses show **Harness permissions**. Previously answered requests cannot affect another
165
166
  connection. Native command-button fallback limitations are documented below.
166
167
 
package/dist/extension.js CHANGED
@@ -369,6 +369,7 @@ export async function activate(context) {
369
369
  };
370
370
  // The approval sweeps of each conversation's running turns, so a picker change reaches a turn already running.
371
371
  const runningTurns = new Map();
372
+ const answeredRequests = new Map();
372
373
  // The host's default for a conversation no one has chosen for (Model Editor declares Auto approve).
373
374
  const hostDefault = () => controls?.state.defaultPermission === 'autoApprove' ? 'autoApprove' : 'ask';
374
375
  const canAnswer = (connection) => Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve'));
@@ -882,6 +883,7 @@ export async function activate(context) {
882
883
  attached: (connection) => attachedIds.has(connection.id),
883
884
  catalogue: () => controls?.state.sessions ?? [],
884
885
  bindings: boundId,
886
+ watchApprovals: (connection, stream, token) => watchApprovals(connection, stream, token, () => approvalFor(connection.id)),
885
887
  };
886
888
  context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.activateSession', async (resource) => { await connectionFor(vscode.Uri.parse(resource)); }));
887
889
  // The host's native focus event is authoritative even when a cached model
@@ -986,25 +988,15 @@ export async function activate(context) {
986
988
  });
987
989
  }));
988
990
  context.subscriptions.push({ dispose: () => frontends.dispose() });
989
- const runTurnInto = async (connection, prompt, stream, token, approval) => {
990
- if (approval() === 'autoApprove' && !canAnswer(connection)) {
991
- throw new Error('This harness does not support automatic approval through Chat. Select Harness permissions.');
992
- }
993
- const answered = new Set();
994
- const state = { toolInputs: new Map(), toolNames: new Map() };
995
- const projection = {
996
- connectionId: connection.id,
997
- pendingRequest: (requestId) => connection.session.pendingRequests.get(requestId) ?? connection.session.resolvedRequests.get(requestId),
998
- canRespond: connection.session.descriptor?.actions.respond !== false,
999
- };
1000
- const cancellation = token.onCancellationRequested(() => {
1001
- void connection.session.interrupt().catch(() => {
1002
- // The runtime may already be idle; interrupting twice is not an error
1003
- // this view reports.
1004
- });
1005
- });
1006
- // Read at every event and on every picker change, so switching to Auto approve mid-turn
1007
- // answers the approvals already waiting and the ones after them.
991
+ // Auto-answers a conversation's approvals while a response is written into `stream`: read at every event
992
+ // (`sweep`) and on every picker change for that conversation, so switching to Auto approve mid-turn answers
993
+ // the approvals already waiting and the ones after them. Used by a turn this window runs and by a turn it
994
+ // re-joins after a reload (the session's active response), whose original handler is gone.
995
+ const watchApprovals = (connection, stream, token, approval) => {
996
+ // One answered set per conversation, shared by every watch on it, so a re-joined response and a live turn
997
+ // never both answer the same request (the second answer would be refused and noted as a failure).
998
+ const answered = answeredRequests.get(connection.id) ?? new Set();
999
+ answeredRequests.set(connection.id, answered);
1008
1000
  const sweep = () => {
1009
1001
  if (approval() !== 'autoApprove' || token.isCancellationRequested || !canAnswer(connection))
1010
1002
  return;
@@ -1034,18 +1026,41 @@ export async function activate(context) {
1034
1026
  };
1035
1027
  const sweeps = runningTurns.get(connection.id) ?? new Set();
1036
1028
  runningTurns.set(connection.id, sweeps.add(sweep));
1029
+ return { sweep, dispose: () => {
1030
+ sweeps.delete(sweep);
1031
+ if (!sweeps.size && runningTurns.get(connection.id) === sweeps) {
1032
+ runningTurns.delete(connection.id);
1033
+ answeredRequests.delete(connection.id);
1034
+ }
1035
+ } };
1036
+ };
1037
+ const runTurnInto = async (connection, prompt, stream, token, approval) => {
1038
+ if (approval() === 'autoApprove' && !canAnswer(connection)) {
1039
+ throw new Error('This harness does not support automatic approval through Chat. Select Harness permissions.');
1040
+ }
1041
+ const state = { toolInputs: new Map(), toolNames: new Map() };
1042
+ const projection = {
1043
+ connectionId: connection.id,
1044
+ pendingRequest: (requestId) => connection.session.pendingRequests.get(requestId) ?? connection.session.resolvedRequests.get(requestId),
1045
+ canRespond: connection.session.descriptor?.actions.respond !== false,
1046
+ };
1047
+ const cancellation = token.onCancellationRequested(() => {
1048
+ void connection.session.interrupt().catch(() => {
1049
+ // The runtime may already be idle; interrupting twice is not an error
1050
+ // this view reports.
1051
+ });
1052
+ });
1053
+ const watch = watchApprovals(connection, stream, token, approval);
1037
1054
  let completed = false;
1038
1055
  try {
1039
1056
  await connection.session.runTurn(prompt, (event) => {
1040
1057
  writeProjected(stream, projectEvent(event, projection), granted, state);
1041
- sweep();
1058
+ watch.sweep();
1042
1059
  });
1043
1060
  completed = true;
1044
1061
  }
1045
1062
  finally {
1046
- sweeps.delete(sweep);
1047
- if (!sweeps.size && runningTurns.get(connection.id) === sweeps)
1048
- runningTurns.delete(connection.id);
1063
+ watch.dispose();
1049
1064
  cancellation.dispose();
1050
1065
  if (controls && !attachedIds.has(connection.id)) {
1051
1066
  if (connection.session.descriptor?.session_id)
@@ -1686,18 +1701,29 @@ function registerSessions(context, frontends, participant, granted, handler, his
1686
1701
  requestHandler: boundHandler,
1687
1702
  activeResponseCallback: busy
1688
1703
  ? async (stream, token) => {
1689
- const state = { toolInputs: new Map(), toolNames: new Map() };
1690
- writeProjected(stream, projectPendingRequests(connection.session.pendingRequests.values(), {
1691
- connectionId: connection.id,
1692
- canRespond: connection.session.descriptor?.actions.respond !== false,
1693
- }), granted, state);
1694
- await observeActiveResponse(connection.session, token, (event) => {
1695
- writeProjected(stream, projectEvent(event, {
1704
+ // A turn re-joined after a reload has lost the handler that ran it, so its approvals are
1705
+ // answered here under the conversation's mode (t_39dac582): Auto approve answers the cards
1706
+ // already waiting and later ones; Ask leaves them for the person, as before.
1707
+ const watch = controls && !controls.attached(connection) ? controls.watchApprovals(connection, stream, token) : undefined;
1708
+ try {
1709
+ const state = { toolInputs: new Map(), toolNames: new Map() };
1710
+ writeProjected(stream, projectPendingRequests(connection.session.pendingRequests.values(), {
1696
1711
  connectionId: connection.id,
1697
- pendingRequest: (id) => connection.session.pendingRequests.get(id) ?? connection.session.resolvedRequests.get(id),
1698
1712
  canRespond: connection.session.descriptor?.actions.respond !== false,
1699
1713
  }), granted, state);
1700
- });
1714
+ watch?.sweep();
1715
+ await observeActiveResponse(connection.session, token, (event) => {
1716
+ writeProjected(stream, projectEvent(event, {
1717
+ connectionId: connection.id,
1718
+ pendingRequest: (id) => connection.session.pendingRequests.get(id) ?? connection.session.resolvedRequests.get(id),
1719
+ canRespond: connection.session.descriptor?.actions.respond !== false,
1720
+ }), granted, state);
1721
+ watch?.sweep();
1722
+ });
1723
+ }
1724
+ finally {
1725
+ watch?.dispose();
1726
+ }
1701
1727
  }
1702
1728
  : undefined,
1703
1729
  };
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.37",
4
+ "version": "0.1.38",
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",