@volter/supercode-frontend-vscode 0.1.39 → 0.1.40

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
@@ -152,8 +152,10 @@ owns no processes.
152
152
  The permission group offers **Ask for approval** and, on an answerable interactive attachment,
153
153
  **Auto approve**. Policies are scoped by conversation and read throughout the turn: switching to
154
154
  **Auto approve** while a turn runs answers the approvals already waiting and every later one.
155
- A lifecycle host may declare `defaultPermission: "autoApprove"` for conversations no one has
156
- picked a mode for (Model Editor does); it applies to Claude Code and Codex where the runtime answers approvals
155
+ A lifecycle host may start the conversations it creates in **Auto approve**: it records
156
+ `defaultPermission: "autoApprove"` on each such conversation and declares `newChatPermission` for the new
157
+ chat's picker (Model Editor does). A conversation without its own recorded default asks, so one that existed
158
+ before a host began doing this keeps its behaviour. It applies to Claude Code and Codex where the runtime answers approvals
157
159
  through Chat, every other harness keeps its own prompts, and a person's explicit pick always
158
160
  wins. Only a pick that differs from the default (or replaces an earlier pick) is saved, so the
159
161
  default is never stored as a choice (a pick equal to the default follows the default if it later
package/dist/extension.js CHANGED
@@ -13,6 +13,8 @@ import { acceptedDecisions, opaqueText, projectEvent, projectPendingRequests, pr
13
13
  const SESSION_SCHEME = 'supercode';
14
14
  /** The harnesses whose model and reasoning effort a new chat can set (the host passes them to the CLI). */
15
15
  const CONFIGURABLE_HARNESSES = ['claude-code', 'codex'];
16
+ /** How long an automatic approval may go unanswered before its card is shown for the person to answer. */
17
+ const AUTO_ANSWER_SETTLE_MS = 15_000;
16
18
  const PARTICIPANT_ID = 'supercode.chat';
17
19
  const DEFAULT_PARTICIPANT_ID = 'supercode.chat.default';
18
20
  /** `package.json` gates the `isDefault` participant on this key. */
@@ -372,18 +374,22 @@ export async function activate(context) {
372
374
  // The approval sweeps of each conversation's running turns, so a picker change reaches a turn already running.
373
375
  const runningTurns = new Map();
374
376
  const answeredRequests = new Map();
375
- // The host's default for a conversation no one has chosen for (Model Editor declares Auto approve).
376
- const hostDefault = () => controls?.state.defaultPermission === 'autoApprove' ? 'autoApprove' : 'ask';
377
+ // The mode a NEW chat's picker shows (`HostState.newChatPermission`; Model Editor declares Auto approve). It is
378
+ // never applied to a conversation that exists: that reads its own recorded default below.
379
+ const newChatDefault = () => controls?.state.newChatPermission === 'autoApprove' ? 'autoApprove' : 'ask';
377
380
  const canAnswer = (connection) => Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve'));
378
- // The host's default for one conversation: Auto approve only for a conversation the host manages and only
379
- // where this Chat can answer the runtime's approvals, read from its live connection, or, while it is not
380
- // connected, from the harness the host records for it (hosted Claude Code and Codex answer through Chat).
381
- // A runtime attached by address and every other harness keep their own prompts.
381
+ // The default of one conversation: the one the host recorded for THAT conversation when it created it
382
+ // (`ManagedChatSession.defaultPermission`), so a conversation that existed before a host began starting chats
383
+ // in Auto approve keeps asking. Auto approve only for a conversation the host manages and only where this Chat
384
+ // can answer the runtime's approvals, read from its live connection, or, while it is not connected, from its
385
+ // harness (hosted Claude Code and Codex answer through Chat). A runtime attached by address and every other
386
+ // harness keep their own prompts.
382
387
  const defaultFor = (id) => {
383
- if (hostDefault() !== 'autoApprove' || attachedIds.has(id))
388
+ const session = controls?.state.sessions.find(entry => entry.id === id);
389
+ if (session?.defaultPermission !== 'autoApprove' || attachedIds.has(id))
384
390
  return 'ask';
385
391
  const connection = frontends.get(id);
386
- const harness = controls?.state.sessions.find(session => session.id === id)?.selection.harness ?? '';
392
+ const harness = session.selection.harness;
387
393
  // Only the harnesses whose approvals this default was decided for; a runtime that answers through another
388
394
  // protocol (an ACP agent) keeps asking until a host names it.
389
395
  return ['claude-code', 'codex'].includes(harness) && (!connection || canAnswer(connection)) ? 'autoApprove' : 'ask';
@@ -491,7 +497,7 @@ export async function activate(context) {
491
497
  }
492
498
  const currentOptions = (resource) => {
493
499
  const id = resource ? boundId(resource) : controls?.state.activeSession;
494
- return { permissions: id ? approvalFor(id) : permission ?? hostDefault() };
500
+ return { permissions: id ? approvalFor(id) : permission ?? newChatDefault() };
495
501
  };
496
502
  const savedConnections = new Map();
497
503
  const saveManagedHistory = async (connection) => {
@@ -873,7 +879,7 @@ export async function activate(context) {
873
879
  const connection = await pending;
874
880
  // The picker shows the host's default until someone changes it; only a change is the person's choice.
875
881
  const picked = turnPermission(inputState.groups.find(group => group.id === 'permissions')?.selected?.id);
876
- choosePermission(connection.id, picked, permission !== undefined ? null : hostDefault());
882
+ choosePermission(connection.id, picked, permission !== undefined ? null : newChatDefault());
877
883
  permission = undefined;
878
884
  if (pendingDraft === draft) {
879
885
  pendingDraft = undefined;
@@ -1034,25 +1040,36 @@ export async function activate(context) {
1034
1040
  catch {
1035
1041
  log.info(text.trim());
1036
1042
  } };
1043
+ const label = opaqueText(named ?? `request #${pending.id}`, 200);
1044
+ // A card this watch hid is shown when its automatic answer fails or does not settle: a typed reply cannot
1045
+ // reach a running turn, its buttons can. With the response closed, the person is told an approval waits.
1046
+ const showCard = () => {
1047
+ if (!unshown.delete(pending.id) || !connection.session.pendingRequests.has(pending.id))
1048
+ return;
1049
+ try {
1050
+ writeProjected(stream, projectPendingRequests([pending], {
1051
+ connectionId: connection.id,
1052
+ canRespond: connection.session.descriptor?.actions.respond !== false,
1053
+ }), granted, { toolInputs: new Map(), toolNames: new Map() });
1054
+ }
1055
+ catch {
1056
+ void vscode.window.showWarningMessage(`${connection.label}: ${label} is waiting for approval. Open its chat to answer it.`);
1057
+ }
1058
+ };
1059
+ const unsettled = setTimeout(() => {
1060
+ note(`\nAuto approve has had no answer for ${label} for ${AUTO_ANSWER_SETTLE_MS / 1000} s. Use the approval buttons to answer.\n`);
1061
+ showCard();
1062
+ }, AUTO_ANSWER_SETTLE_MS);
1037
1063
  void connection.session.respond({ kind: 'approval', request_id: pending.id, decision: 'allow' }).then(() => {
1064
+ clearTimeout(unsettled);
1038
1065
  // A card that was never shown needs no note: its resolution line says it was allowed.
1039
1066
  if (unshown.has(pending.id))
1040
1067
  return;
1041
- note(`\n_auto-approved: ${opaqueText(named ?? `request #${pending.id}`, 200)}_\n`);
1068
+ note(`\n_auto-approved: ${label}_\n`);
1042
1069
  }, error => {
1070
+ clearTimeout(unsettled);
1043
1071
  note(`\nApproval failed: ${String(error.message || error)}. Use the approval buttons to answer.\n`);
1044
- // A card this watch hid is shown now: a typed reply cannot reach a running turn, its buttons can.
1045
- if (unshown.delete(pending.id) && connection.session.pendingRequests.has(pending.id)) {
1046
- try {
1047
- writeProjected(stream, projectPendingRequests([pending], {
1048
- connectionId: connection.id,
1049
- canRespond: connection.session.descriptor?.actions.respond !== false,
1050
- }), granted, { toolInputs: new Map(), toolNames: new Map() });
1051
- }
1052
- catch {
1053
- log.info(`Approval card for request #${pending.id} could not be shown: the response has closed`);
1054
- }
1055
- }
1072
+ showCard();
1056
1073
  });
1057
1074
  }
1058
1075
  };
@@ -13,12 +13,14 @@ export interface ChatChoice {
13
13
  autoStart?: boolean;
14
14
  efforts?: string[];
15
15
  }
16
+ /** `defaultPermission`: the approval mode the host started this conversation in when no one picked one; absent, it asks. */
16
17
  export interface ManagedChatSession {
17
18
  id: string;
18
19
  selection: ChatSelection;
19
20
  identity: string | null;
20
21
  title: string;
21
22
  created: number;
23
+ defaultPermission?: 'ask' | 'autoApprove';
22
24
  }
23
25
  /** Host-owned operations. The host revalidates commands before native terminal execution. */
24
26
  export interface ChatSetupAction {
@@ -51,9 +53,9 @@ export interface HostState {
51
53
  actualModel?: string | null;
52
54
  configurable: boolean;
53
55
  setup?: ChatSetupState;
54
- /** The approval mode of a conversation no one has picked one for. `autoApprove` takes effect only
55
- * where the runtime can answer approvals through Chat; absent, every conversation asks. */
56
- defaultPermission?: 'ask' | 'autoApprove';
56
+ /** The mode a new chat's picker shows. A conversation's own default is its `ManagedChatSession.defaultPermission`;
57
+ * this is never applied to one that exists. `autoApprove` takes effect only where the runtime can answer approvals. */
58
+ newChatPermission?: 'ask' | 'autoApprove';
57
59
  }
58
60
  export interface HostSelectionResult extends HostState {
59
61
  connection: Record<string, string>;
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.39",
4
+ "version": "0.1.40",
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",