@volter/supercode-frontend-vscode 0.1.37 → 0.1.39
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 +2 -1
- package/dist/extension.js +90 -31
- package/package.json +1 -1
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: …").
|
|
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
|
@@ -238,6 +238,8 @@ function writePart(stream, part, state) {
|
|
|
238
238
|
return;
|
|
239
239
|
}
|
|
240
240
|
case 'answers':
|
|
241
|
+
if (part.approval && part.answers.length > 0 && state.quiet?.(part.answers[0].requestId))
|
|
242
|
+
return;
|
|
241
243
|
if (part.approval && nativeInTurnApprovalCards && part.answers.length > 0) {
|
|
242
244
|
stream.confirmation(part.title, part.message, {
|
|
243
245
|
kind: 'supercode.inTurnApproval', version: 1, answers: part.answers,
|
|
@@ -369,6 +371,7 @@ export async function activate(context) {
|
|
|
369
371
|
};
|
|
370
372
|
// The approval sweeps of each conversation's running turns, so a picker change reaches a turn already running.
|
|
371
373
|
const runningTurns = new Map();
|
|
374
|
+
const answeredRequests = new Map();
|
|
372
375
|
// The host's default for a conversation no one has chosen for (Model Editor declares Auto approve).
|
|
373
376
|
const hostDefault = () => controls?.state.defaultPermission === 'autoApprove' ? 'autoApprove' : 'ask';
|
|
374
377
|
const canAnswer = (connection) => Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve'));
|
|
@@ -882,6 +885,7 @@ export async function activate(context) {
|
|
|
882
885
|
attached: (connection) => attachedIds.has(connection.id),
|
|
883
886
|
catalogue: () => controls?.state.sessions ?? [],
|
|
884
887
|
bindings: boundId,
|
|
888
|
+
watchApprovals: (connection, stream, token) => watchApprovals(connection, stream, token, () => approvalFor(connection.id)),
|
|
885
889
|
};
|
|
886
890
|
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.activateSession', async (resource) => { await connectionFor(vscode.Uri.parse(resource)); }));
|
|
887
891
|
// The host's native focus event is authoritative even when a cached model
|
|
@@ -986,25 +990,30 @@ export async function activate(context) {
|
|
|
986
990
|
});
|
|
987
991
|
}));
|
|
988
992
|
context.subscriptions.push({ dispose: () => frontends.dispose() });
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
993
|
+
// Auto-answers a conversation's approvals while a response is written into `stream`: read at every event
|
|
994
|
+
// (`sweep`) and on every picker change for that conversation, so switching to Auto approve mid-turn answers
|
|
995
|
+
// the approvals already waiting and the ones after them. Used by a turn this window runs and by a turn it
|
|
996
|
+
// re-joins after a reload (the session's active response), whose original handler is gone.
|
|
997
|
+
const watchApprovals = (connection, stream, token, approval) => {
|
|
998
|
+
// One answered set per conversation, shared by every watch on it, so a re-joined response and a live turn
|
|
999
|
+
// never both answer the same request (the second answer would be refused and noted as a failure).
|
|
1000
|
+
const answered = answeredRequests.get(connection.id) ?? new Set();
|
|
1001
|
+
answeredRequests.set(connection.id, answered);
|
|
1002
|
+
// Requests whose card was not shown because this watch was going to answer them.
|
|
1003
|
+
const unshown = new Set();
|
|
1004
|
+
const answers = (requestId) => {
|
|
1005
|
+
if (approval() !== 'autoApprove' || token.isCancellationRequested || !canAnswer(connection))
|
|
1006
|
+
return false;
|
|
1007
|
+
const pending = connection.session.pendingRequests.get(requestId);
|
|
1008
|
+
// Hidden only when this watch's own sweep will answer it: one another watch claimed keeps its card here.
|
|
1009
|
+
if (!pending || pending.kind !== 'approval' || answered.has(requestId))
|
|
1010
|
+
return false;
|
|
1011
|
+
const decisions = acceptedDecisions(pending);
|
|
1012
|
+
if (decisions && !decisions.includes('allow'))
|
|
1013
|
+
return false;
|
|
1014
|
+
unshown.add(requestId);
|
|
1015
|
+
return true;
|
|
999
1016
|
};
|
|
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.
|
|
1008
1017
|
const sweep = () => {
|
|
1009
1018
|
if (approval() !== 'autoApprove' || token.isCancellationRequested || !canAnswer(connection))
|
|
1010
1019
|
return;
|
|
@@ -1026,26 +1035,65 @@ export async function activate(context) {
|
|
|
1026
1035
|
log.info(text.trim());
|
|
1027
1036
|
} };
|
|
1028
1037
|
void connection.session.respond({ kind: 'approval', request_id: pending.id, decision: 'allow' }).then(() => {
|
|
1038
|
+
// A card that was never shown needs no note: its resolution line says it was allowed.
|
|
1039
|
+
if (unshown.has(pending.id))
|
|
1040
|
+
return;
|
|
1029
1041
|
note(`\n_auto-approved: ${opaqueText(named ?? `request #${pending.id}`, 200)}_\n`);
|
|
1030
1042
|
}, error => {
|
|
1031
1043
|
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
|
+
}
|
|
1032
1056
|
});
|
|
1033
1057
|
}
|
|
1034
1058
|
};
|
|
1035
1059
|
const sweeps = runningTurns.get(connection.id) ?? new Set();
|
|
1036
1060
|
runningTurns.set(connection.id, sweeps.add(sweep));
|
|
1061
|
+
return { sweep, answers, dispose: () => {
|
|
1062
|
+
sweeps.delete(sweep);
|
|
1063
|
+
if (!sweeps.size && runningTurns.get(connection.id) === sweeps) {
|
|
1064
|
+
runningTurns.delete(connection.id);
|
|
1065
|
+
answeredRequests.delete(connection.id);
|
|
1066
|
+
}
|
|
1067
|
+
} };
|
|
1068
|
+
};
|
|
1069
|
+
const runTurnInto = async (connection, prompt, stream, token, approval) => {
|
|
1070
|
+
if (approval() === 'autoApprove' && !canAnswer(connection)) {
|
|
1071
|
+
throw new Error('This harness does not support automatic approval through Chat. Select Harness permissions.');
|
|
1072
|
+
}
|
|
1073
|
+
const state = { toolInputs: new Map(), toolNames: new Map() };
|
|
1074
|
+
const projection = {
|
|
1075
|
+
connectionId: connection.id,
|
|
1076
|
+
pendingRequest: (requestId) => connection.session.pendingRequests.get(requestId) ?? connection.session.resolvedRequests.get(requestId),
|
|
1077
|
+
canRespond: connection.session.descriptor?.actions.respond !== false,
|
|
1078
|
+
};
|
|
1079
|
+
const cancellation = token.onCancellationRequested(() => {
|
|
1080
|
+
void connection.session.interrupt().catch(() => {
|
|
1081
|
+
// The runtime may already be idle; interrupting twice is not an error
|
|
1082
|
+
// this view reports.
|
|
1083
|
+
});
|
|
1084
|
+
});
|
|
1085
|
+
const watch = watchApprovals(connection, stream, token, approval);
|
|
1086
|
+
state.quiet = watch.answers;
|
|
1037
1087
|
let completed = false;
|
|
1038
1088
|
try {
|
|
1039
1089
|
await connection.session.runTurn(prompt, (event) => {
|
|
1040
1090
|
writeProjected(stream, projectEvent(event, projection), granted, state);
|
|
1041
|
-
sweep();
|
|
1091
|
+
watch.sweep();
|
|
1042
1092
|
});
|
|
1043
1093
|
completed = true;
|
|
1044
1094
|
}
|
|
1045
1095
|
finally {
|
|
1046
|
-
|
|
1047
|
-
if (!sweeps.size && runningTurns.get(connection.id) === sweeps)
|
|
1048
|
-
runningTurns.delete(connection.id);
|
|
1096
|
+
watch.dispose();
|
|
1049
1097
|
cancellation.dispose();
|
|
1050
1098
|
if (controls && !attachedIds.has(connection.id)) {
|
|
1051
1099
|
if (connection.session.descriptor?.session_id)
|
|
@@ -1686,18 +1734,29 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
1686
1734
|
requestHandler: boundHandler,
|
|
1687
1735
|
activeResponseCallback: busy
|
|
1688
1736
|
? async (stream, token) => {
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
writeProjected(stream,
|
|
1737
|
+
// A turn re-joined after a reload has lost the handler that ran it, so its approvals are
|
|
1738
|
+
// answered here under the conversation's mode (t_39dac582): Auto approve answers the cards
|
|
1739
|
+
// already waiting and later ones; Ask leaves them for the person, as before.
|
|
1740
|
+
const watch = controls && !controls.attached(connection) ? controls.watchApprovals(connection, stream, token) : undefined;
|
|
1741
|
+
try {
|
|
1742
|
+
const state = { toolInputs: new Map(), toolNames: new Map(), quiet: watch?.answers };
|
|
1743
|
+
writeProjected(stream, projectPendingRequests(connection.session.pendingRequests.values(), {
|
|
1696
1744
|
connectionId: connection.id,
|
|
1697
|
-
pendingRequest: (id) => connection.session.pendingRequests.get(id) ?? connection.session.resolvedRequests.get(id),
|
|
1698
1745
|
canRespond: connection.session.descriptor?.actions.respond !== false,
|
|
1699
1746
|
}), granted, state);
|
|
1700
|
-
|
|
1747
|
+
watch?.sweep();
|
|
1748
|
+
await observeActiveResponse(connection.session, token, (event) => {
|
|
1749
|
+
writeProjected(stream, projectEvent(event, {
|
|
1750
|
+
connectionId: connection.id,
|
|
1751
|
+
pendingRequest: (id) => connection.session.pendingRequests.get(id) ?? connection.session.resolvedRequests.get(id),
|
|
1752
|
+
canRespond: connection.session.descriptor?.actions.respond !== false,
|
|
1753
|
+
}), granted, state);
|
|
1754
|
+
watch?.sweep();
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
finally {
|
|
1758
|
+
watch?.dispose();
|
|
1759
|
+
}
|
|
1701
1760
|
}
|
|
1702
1761
|
: undefined,
|
|
1703
1762
|
};
|
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.
|
|
4
|
+
"version": "0.1.39",
|
|
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",
|