@volter/supercode-frontend-vscode 0.1.57 → 0.1.60
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/NOTICE.md +1 -1
- package/README.md +119 -10
- package/dist/chat-setup.js +3 -2
- package/dist/extension.js +241 -45
- package/dist/host-controls.d.ts +4 -0
- package/dist/host-controls.js +4 -1
- package/dist/native-session.d.ts +2 -2
- package/dist/native-session.js +10 -1
- package/dist/session.d.ts +9 -0
- package/dist/session.js +134 -22
- package/dist/setup-flow.js +10 -2
- package/dist/view.d.ts +5 -3
- package/dist/view.js +103 -16
- package/media/anthropic.svg +1 -1
- package/media/google.svg +1 -1
- package/media/openai.svg +1 -1
- package/media/xai.svg +1 -0
- package/package.json +34 -9
- package/vendor/supercode-frontend/README.md +27 -0
- package/vendor/supercode-frontend/client.d.mts +7 -1
- package/vendor/supercode-frontend/client.mjs +85 -11
- package/vendor/supercode-frontend/contract.json +7 -2
- package/vendor/supercode-frontend/generated-client.d.mts +1 -1
- package/vendor/supercode-frontend/generated-client.mjs +1 -1
- package/vendor/supercode-frontend/generated.d.mts +7 -0
- package/vendor/supercode-frontend/package.json +2 -1
package/dist/extension.js
CHANGED
|
@@ -108,8 +108,10 @@ class SupercodeFrontends {
|
|
|
108
108
|
const { baseUrl, token } = options;
|
|
109
109
|
const id = options.connectionId ?? options.clientId ?? `vscode-${randomUUID()}`;
|
|
110
110
|
const existing = this.#connections.get(id);
|
|
111
|
-
if (existing)
|
|
112
|
-
|
|
111
|
+
if (existing) {
|
|
112
|
+
await existing.controlling;
|
|
113
|
+
return this.#connections.get(id) === existing && existing.session.connection !== 'closed' ? existing : undefined;
|
|
114
|
+
}
|
|
113
115
|
const permissions = normalizePermissions(options.permissions, ['observe']);
|
|
114
116
|
const client = createFrontendClient({
|
|
115
117
|
baseUrl,
|
|
@@ -121,9 +123,9 @@ class SupercodeFrontends {
|
|
|
121
123
|
// The generated client opens SSE before the snapshot RPC; `attach` is
|
|
122
124
|
// therefore the whole race-free attachment and `follow` only drains it.
|
|
123
125
|
await session.attach();
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
|
|
126
|
+
// Taking control must not hold up observation of an attached snapshot.
|
|
127
|
+
// Actions share this acquisition; connect still awaits it before returning.
|
|
128
|
+
const controlling = session.takeControlAndHold().catch((error) => {
|
|
127
129
|
log.warn(`Volter Harness could not take control of ${baseUrl}: ${String(error?.message ?? error)}`);
|
|
128
130
|
});
|
|
129
131
|
// The runtime's stream can end under a live attachment: an HTTP client's
|
|
@@ -135,14 +137,17 @@ class SupercodeFrontends {
|
|
|
135
137
|
const stillWanted = () => connection !== undefined && this.#connections.get(id) === connection && session.connection !== 'closed';
|
|
136
138
|
const following = (async () => {
|
|
137
139
|
for (let attempt = 0;; attempt += 1) {
|
|
140
|
+
let failure;
|
|
138
141
|
try {
|
|
139
142
|
await session.follow();
|
|
140
143
|
}
|
|
141
144
|
catch (error) {
|
|
145
|
+
failure = error;
|
|
142
146
|
log.warn(`Volter Harness event stream ended: ${String(error?.message ?? error)}`);
|
|
143
147
|
}
|
|
144
148
|
if (!stillWanted())
|
|
145
149
|
return;
|
|
150
|
+
session.reportFailure(failure ?? new Error('The conversation event stream ended before this attachment closed. Reconnecting to the runtime; its turn may still be running.'));
|
|
146
151
|
await new Promise((resolve) => setTimeout(resolve, Math.min(15_000, 500 * 2 ** Math.min(attempt, 5))));
|
|
147
152
|
if (!stillWanted())
|
|
148
153
|
return;
|
|
@@ -162,6 +167,7 @@ class SupercodeFrontends {
|
|
|
162
167
|
baseUrl,
|
|
163
168
|
session,
|
|
164
169
|
following,
|
|
170
|
+
controlling,
|
|
165
171
|
};
|
|
166
172
|
session.onChange(() => this.#changed.fire());
|
|
167
173
|
this.#connections.set(id, connection);
|
|
@@ -169,6 +175,9 @@ class SupercodeFrontends {
|
|
|
169
175
|
this.setUnavailable(undefined);
|
|
170
176
|
log.info(`Volter Harness attached: ${connection.label} (${baseUrl}) as ${permissions.join(',')}` +
|
|
171
177
|
` · ${session.history.length} history messages`);
|
|
178
|
+
await controlling;
|
|
179
|
+
if (this.#connections.get(id) !== connection || session.connection === 'closed')
|
|
180
|
+
return undefined;
|
|
172
181
|
return connection;
|
|
173
182
|
}
|
|
174
183
|
async disconnect(id) {
|
|
@@ -255,7 +264,10 @@ function writePart(stream, part, state) {
|
|
|
255
264
|
}
|
|
256
265
|
// Stable API on purpose: a command button runs INSIDE the live turn,
|
|
257
266
|
// which is when a supercode approval is raised. See `view.ts`.
|
|
258
|
-
|
|
267
|
+
// Native confirmation titles are plain text; escape only when the same
|
|
268
|
+
// title is embedded in the Markdown fallback (including Windows paths).
|
|
269
|
+
const title = new vscode.MarkdownString().appendText(part.title).value;
|
|
270
|
+
stream.markdown(`\n\n**${title}**\n\n${part.message}\n\n`);
|
|
259
271
|
for (const answer of part.answers) {
|
|
260
272
|
stream.button({
|
|
261
273
|
title: answer.label,
|
|
@@ -380,13 +392,26 @@ export async function activate(context) {
|
|
|
380
392
|
// protocol (an ACP agent) keeps asking until a host names it.
|
|
381
393
|
return ['claude-code', 'codex'].includes(harness) && (!connection || canAnswer(connection)) ? 'autoApprove' : 'ask';
|
|
382
394
|
};
|
|
383
|
-
//
|
|
384
|
-
const
|
|
395
|
+
// Runtime YOLO is the host's recorded launch policy, never an approval-picker choice.
|
|
396
|
+
const yoloFor = (id) => {
|
|
397
|
+
const session = controls?.state.sessions.find(entry => entry.id === id);
|
|
398
|
+
return !attachedIds.has(id) && session?.runtimePolicy === 'yolo'
|
|
399
|
+
&& ['claude-code', 'codex'].includes(session.selection.harness);
|
|
400
|
+
};
|
|
401
|
+
// A saved Ask choice cannot turn prompts back on in a runtime already launched with bypass flags.
|
|
402
|
+
const approvalFor = (id) => {
|
|
403
|
+
if (!yoloFor(id))
|
|
404
|
+
return permissions.get(id) ?? defaultFor(id);
|
|
405
|
+
const connection = frontends.get(id);
|
|
406
|
+
return !connection || canAnswer(connection) ? 'autoApprove' : 'ask';
|
|
407
|
+
};
|
|
385
408
|
// A picker value becomes the person's choice only when it is one: it differs from the default the picker showed
|
|
386
409
|
// (`shown`; null when the person already picked it), or replaces an earlier choice. Echoes of the displayed default
|
|
387
410
|
// (the picker re-applied when a conversation opens) are not saved, and neither is an Auto approve the conversation
|
|
388
411
|
// cannot answer: the picker offers it only where it can, so there it is a default shown before the harness was known.
|
|
389
412
|
const choosePermission = (id, value, shown = defaultFor(id)) => {
|
|
413
|
+
if (yoloFor(id))
|
|
414
|
+
return;
|
|
390
415
|
const connection = frontends.get(id);
|
|
391
416
|
if (value === 'autoApprove' && connection && !canAnswer(connection))
|
|
392
417
|
return;
|
|
@@ -402,6 +427,11 @@ export async function activate(context) {
|
|
|
402
427
|
let focusedResource;
|
|
403
428
|
let focusGeneration = 0;
|
|
404
429
|
const activeDraft = () => pendingDraft?.resource && pendingDraft.resource.toString() === focusedResource?.toString() ? pendingDraft : undefined;
|
|
430
|
+
const draftChoiceRefusal = () => {
|
|
431
|
+
const draft = activeDraft();
|
|
432
|
+
return draft && (draft.choosing || !draft.choice || draft.choice.kind === 'cancelled')
|
|
433
|
+
? 'Choose your coding agent before sending. Your draft is saved; send it after choosing.' : undefined;
|
|
434
|
+
};
|
|
405
435
|
const draftSelection = () => {
|
|
406
436
|
const choice = activeDraft()?.choice;
|
|
407
437
|
return choice?.kind === 'agent' ? choice.selection : undefined;
|
|
@@ -621,9 +651,11 @@ export async function activate(context) {
|
|
|
621
651
|
provideTokenCount: async () => 0,
|
|
622
652
|
}));
|
|
623
653
|
}
|
|
654
|
+
const newChatYolo = () => controls?.state.newChatRuntimePolicy === 'yolo'
|
|
655
|
+
&& ['claude-code', 'codex'].includes(draftSelection()?.harness ?? controls.state.selection.harness);
|
|
624
656
|
const currentOptions = (resource) => {
|
|
625
657
|
const id = resource ? boundId(resource) : activeDraft() ? undefined : controls?.state.activeSession;
|
|
626
|
-
return { permissions: id ? approvalFor(id) : permission ?? newChatDefault() };
|
|
658
|
+
return { permissions: id ? yoloFor(id) ? 'yolo' : approvalFor(id) : newChatYolo() ? 'yolo' : permission ?? newChatDefault() };
|
|
627
659
|
};
|
|
628
660
|
const savedConnections = new Map();
|
|
629
661
|
const saveManagedHistory = async (connection) => {
|
|
@@ -655,6 +687,12 @@ export async function activate(context) {
|
|
|
655
687
|
return connection;
|
|
656
688
|
};
|
|
657
689
|
let setupHandoff;
|
|
690
|
+
let canceledSetupReveal;
|
|
691
|
+
const revealCanceledFor = (basis, connection) => Boolean(canceledSetupReveal
|
|
692
|
+
&& canceledSetupReveal.id === basis.id && canceledSetupReveal.baseUrl === basis.baseUrl
|
|
693
|
+
&& canceledSetupReveal.generation === basis.generation && canceledSetupReveal.focusResource === basis.focusResource
|
|
694
|
+
&& canceledSetupReveal.live === basis.live && canceledSetupReveal.connection.deref() === connection
|
|
695
|
+
&& connection.session.connection === 'connected');
|
|
658
696
|
const setupHandoffComplete = () => {
|
|
659
697
|
const id = controls?.state.activeSession;
|
|
660
698
|
const connection = id ? frontends.get(id) : undefined;
|
|
@@ -722,6 +760,7 @@ export async function activate(context) {
|
|
|
722
760
|
}
|
|
723
761
|
else {
|
|
724
762
|
setupHandoff = undefined;
|
|
763
|
+
canceledSetupReveal = undefined;
|
|
725
764
|
controlsChanged.fire();
|
|
726
765
|
return { kind: 'setup', generation };
|
|
727
766
|
}
|
|
@@ -742,8 +781,27 @@ export async function activate(context) {
|
|
|
742
781
|
if ((controls.state.setup && !controls.state.setup.ready) || controls.state.activeSession !== target.id
|
|
743
782
|
|| controls.state.connection?.SUPERCODE_FRONTEND_URL !== target.baseUrl)
|
|
744
783
|
return;
|
|
745
|
-
if (!setupHandoffComplete())
|
|
746
|
-
|
|
784
|
+
if (!setupHandoffComplete()) {
|
|
785
|
+
const connection = frontends.get(target.id);
|
|
786
|
+
const basis = { ...target, live };
|
|
787
|
+
if (!connection || revealCanceledFor(basis, connection))
|
|
788
|
+
return;
|
|
789
|
+
try {
|
|
790
|
+
await revealManaged(target, { resource: target.focusResource });
|
|
791
|
+
canceledSetupReveal = undefined;
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
// The native focus guard canceled this navigation. Repeating the same request cannot earn new focus;
|
|
795
|
+
// attachment remains real and the reveal remains incomplete. A changed basis or explicit refresh retries.
|
|
796
|
+
if (!(error instanceof vscode.CancellationError)
|
|
797
|
+
&& !(error instanceof Error && error.name === 'Canceled' && error.message === 'Canceled'))
|
|
798
|
+
throw error;
|
|
799
|
+
canceledSetupReveal = { ...basis, connection: new WeakRef(connection) };
|
|
800
|
+
log.debug('Automatic Chat reveal canceled for the current conversation and focus.');
|
|
801
|
+
controlsChanged.fire();
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
747
805
|
if (setupHandoffComplete())
|
|
748
806
|
welcomeWasVisible = false;
|
|
749
807
|
}
|
|
@@ -792,6 +850,41 @@ export async function activate(context) {
|
|
|
792
850
|
throw new vscode.CancellationError();
|
|
793
851
|
if (action.command !== offered.command)
|
|
794
852
|
throw new Error('The sign-in command changed. Try again.');
|
|
853
|
+
if (kind === 'install') {
|
|
854
|
+
// A task observes native process exit without closing the terminal that
|
|
855
|
+
// holds its diagnostics. Each click gets its own panel and identity;
|
|
856
|
+
// cancellation terminates only the execution returned for this task.
|
|
857
|
+
const operation = randomUUID();
|
|
858
|
+
const task = new vscode.Task({ type: 'supercode-setup', operation }, vscode.workspace.getWorkspaceFolder(vscode.Uri.file(action.cwd)) ?? vscode.TaskScope.Workspace, `${action.name} · install`, 'supercode', new vscode.ProcessExecution(action.program, action.arguments, { cwd: action.cwd, env: action.env }));
|
|
859
|
+
task.presentationOptions = { reveal: vscode.TaskRevealKind.Always, panel: vscode.TaskPanelKind.New, clear: false, close: false };
|
|
860
|
+
const listeners = [];
|
|
861
|
+
let finished = false;
|
|
862
|
+
let resolveExit;
|
|
863
|
+
const exited = new Promise(resolve => { resolveExit = resolve; });
|
|
864
|
+
const finish = (code) => {
|
|
865
|
+
if (finished)
|
|
866
|
+
return;
|
|
867
|
+
finished = true;
|
|
868
|
+
for (const listener of listeners)
|
|
869
|
+
listener.dispose();
|
|
870
|
+
resolveExit(code);
|
|
871
|
+
};
|
|
872
|
+
const owns = (execution) => execution.task.definition.type === 'supercode-setup'
|
|
873
|
+
&& execution.task.definition.operation === operation;
|
|
874
|
+
listeners.push(vscode.tasks.onDidEndTaskProcess(event => { if (owns(event.execution))
|
|
875
|
+
finish(event.exitCode); }), vscode.tasks.onDidEndTask(event => { if (owns(event.execution))
|
|
876
|
+
finish(undefined); }));
|
|
877
|
+
let execution;
|
|
878
|
+
try {
|
|
879
|
+
execution = await vscode.tasks.executeTask(task);
|
|
880
|
+
}
|
|
881
|
+
catch (error) {
|
|
882
|
+
finish(undefined);
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
return { exited, cancel: () => { if (!finished)
|
|
886
|
+
execution.terminate(); } };
|
|
887
|
+
}
|
|
795
888
|
const terminal = vscode.window.createTerminal({ name: `${action.name} · ${kind === 'login' ? 'sign in' : 'install'}`,
|
|
796
889
|
cwd: action.cwd, shellPath: action.program, shellArgs: action.arguments, env: action.env });
|
|
797
890
|
const exited = new Promise(resolve => {
|
|
@@ -807,7 +900,10 @@ export async function activate(context) {
|
|
|
807
900
|
},
|
|
808
901
|
}) : undefined;
|
|
809
902
|
const setupState = () => controls ? setupPresentation(controls.state, setupFlow?.operations ?? new Map(), setupFlow?.notice) : null;
|
|
810
|
-
context.subscriptions.push({ dispose: () => setupFlow?.dispose() }, vscode.commands.registerCommand('supercode.frontend.refreshSetup',
|
|
903
|
+
context.subscriptions.push({ dispose: () => setupFlow?.dispose() }, vscode.commands.registerCommand('supercode.frontend.refreshSetup', () => {
|
|
904
|
+
canceledSetupReveal = undefined;
|
|
905
|
+
return refreshSetup();
|
|
906
|
+
}), vscode.commands.registerCommand(SETUP_STATE, setupState), vscode.commands.registerCommand(SETUP_BEGIN, (harness) => {
|
|
811
907
|
if (!setupFlow)
|
|
812
908
|
throw new Error('This host does not expose sign-in.');
|
|
813
909
|
return setupFlow.begin(harness);
|
|
@@ -1011,7 +1107,7 @@ export async function activate(context) {
|
|
|
1011
1107
|
if (draft && draft.choosing === picking)
|
|
1012
1108
|
draft.choosing = undefined;
|
|
1013
1109
|
if (draft?.choice?.kind === 'signIn') {
|
|
1014
|
-
void refreshSetup().catch(error => log.warn(`Could not refresh
|
|
1110
|
+
void refreshSetup().catch(error => log.warn(`Could not refresh Chat setup: ${String(error)}`)).finally(pollSetup);
|
|
1015
1111
|
}
|
|
1016
1112
|
}
|
|
1017
1113
|
};
|
|
@@ -1025,12 +1121,16 @@ export async function activate(context) {
|
|
|
1025
1121
|
if (busy)
|
|
1026
1122
|
throw new Error(busy);
|
|
1027
1123
|
const draft = activeDraft();
|
|
1028
|
-
//
|
|
1029
|
-
//
|
|
1030
|
-
|
|
1031
|
-
if (
|
|
1124
|
+
// Refuse before native materialization accepts the input. A pending picker
|
|
1125
|
+
// must not hold an invisible send that starts when the choice later ends.
|
|
1126
|
+
const choiceRefusal = draftChoiceRefusal();
|
|
1127
|
+
if (choiceRefusal) {
|
|
1128
|
+
void vscode.window.showInformationMessage(choiceRefusal);
|
|
1032
1129
|
throw new vscode.CancellationError();
|
|
1130
|
+
}
|
|
1033
1131
|
await controls.refresh();
|
|
1132
|
+
if (draft && activeDraft() !== draft)
|
|
1133
|
+
throw new vscode.CancellationError();
|
|
1034
1134
|
controlsChanged.fire();
|
|
1035
1135
|
const choice = draft?.choice;
|
|
1036
1136
|
const loginHarness = choice?.kind === 'signIn' ? choice.harness : undefined;
|
|
@@ -1048,7 +1148,8 @@ export async function activate(context) {
|
|
|
1048
1148
|
// The picker shows the host's default until someone changes it; only a change is the person's choice.
|
|
1049
1149
|
const selected = inputState.groups.find(group => group.id === 'permissions')?.selected?.id;
|
|
1050
1150
|
const picked = selected === 'ask' || selected === 'autoApprove' ? selected : permission ?? newChatDefault();
|
|
1051
|
-
|
|
1151
|
+
if (!yoloFor(connection.id))
|
|
1152
|
+
choosePermission(connection.id, picked, permission !== undefined ? null : newChatDefault());
|
|
1052
1153
|
permission = undefined;
|
|
1053
1154
|
if (pendingDraft === draft) {
|
|
1054
1155
|
pendingDraft = undefined;
|
|
@@ -1071,8 +1172,11 @@ export async function activate(context) {
|
|
|
1071
1172
|
// the default harness decides; only with none known does the new-chat default stand in for it
|
|
1072
1173
|
: controls?.state.selection.harness ? ['claude-code', 'codex'].includes(controls.state.selection.harness) : newChatDefault() === 'autoApprove');
|
|
1073
1174
|
// An unbound input is a new chat, not the active conversation left behind it.
|
|
1074
|
-
const
|
|
1075
|
-
const
|
|
1175
|
+
const isYolo = id ? yoloFor(id) : newChatYolo();
|
|
1176
|
+
const selectedPermission = isYolo ? 'yolo' : resource ? currentOptions(resource).permissions : permission ?? newChatDefault();
|
|
1177
|
+
const permissionItems = isYolo ? [{ id: 'yolo', name: 'YOLO', description: id
|
|
1178
|
+
? 'This runtime runs without approval prompts. Change the project Chat runtime policy for its next start or resume.'
|
|
1179
|
+
: 'Project policy: start native Claude Code or Codex without approval prompts or the Codex sandbox.' }] : [{ id: 'ask', name: canApprove ? 'Ask for approval' : 'Harness permissions', description: canApprove ? 'Show each approval requested by the harness.' : 'Approval controls are unavailable for this harness.' },
|
|
1076
1180
|
...((canApprove || (!active && resource && currentOptions(resource).permissions === 'autoApprove')) ? [{ id: 'autoApprove', name: 'Auto approve', description: 'Approve the requests the harness would ask you about, including requests to run outside its sandbox. Its deny rules still apply.' }] : [])];
|
|
1077
1181
|
const groups = [{ id: 'permissions', name: 'Permissions', kind: 'permissions', items: permissionItems, selected: permissionItems.find(i => i.id === selectedPermission) ?? permissionItems[0] }];
|
|
1078
1182
|
const selected = controls?.state.sessions.find(session => session.id === id);
|
|
@@ -1091,24 +1195,34 @@ export async function activate(context) {
|
|
|
1091
1195
|
},
|
|
1092
1196
|
setPermission: (resource, value) => {
|
|
1093
1197
|
const id = boundId(resource);
|
|
1198
|
+
if (value === 'yolo' && !id && newChatYolo())
|
|
1199
|
+
return;
|
|
1094
1200
|
if (controls && !id)
|
|
1095
1201
|
throw new Error('This conversation has no session binding.');
|
|
1202
|
+
if (id && yoloFor(id))
|
|
1203
|
+
return;
|
|
1096
1204
|
if (id)
|
|
1097
1205
|
choosePermission(id, turnPermission(value));
|
|
1098
1206
|
else
|
|
1099
1207
|
permission = turnPermission(value);
|
|
1100
1208
|
},
|
|
1101
|
-
// A send that makes no conversation
|
|
1102
|
-
//
|
|
1103
|
-
//
|
|
1209
|
+
// A send that makes no conversation keeps its refusal for the draft handler.
|
|
1210
|
+
// Materialization can stop before that callback, so creation also shows the
|
|
1211
|
+
// failure directly instead of relying on a rejected native callback (#1282).
|
|
1104
1212
|
create: async (inputState) => {
|
|
1213
|
+
const refusedKey = activeDraft()?.resource.toString() ?? inputState.sessionResource?.toString() ?? '';
|
|
1105
1214
|
try {
|
|
1106
1215
|
return await createConversation(inputState);
|
|
1107
1216
|
}
|
|
1108
1217
|
catch (error) {
|
|
1109
1218
|
if (error instanceof vscode.CancellationError)
|
|
1110
1219
|
throw error;
|
|
1111
|
-
|
|
1220
|
+
const reason = String(error?.message ?? error);
|
|
1221
|
+
refusedSends.set(refusedKey, reason);
|
|
1222
|
+
// Materialization can end before the native workbench calls a request
|
|
1223
|
+
// handler. Surface the original launch error on this path as well.
|
|
1224
|
+
log.error(`Chat creation failed: ${reason}`);
|
|
1225
|
+
void vscode.window.showErrorMessage(`Chat could not start: ${reason}. No message was sent.`);
|
|
1112
1226
|
return undefined;
|
|
1113
1227
|
}
|
|
1114
1228
|
},
|
|
@@ -1122,6 +1236,10 @@ export async function activate(context) {
|
|
|
1122
1236
|
finishIdentitySave: (connection) => finishIdentitySave(connection, true),
|
|
1123
1237
|
};
|
|
1124
1238
|
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.activateSession', async (resource) => { await connectionFor(vscode.Uri.parse(resource)); }));
|
|
1239
|
+
// A paired native host can explain Enter when its model/options precondition
|
|
1240
|
+
// prevents the submit command from reaching session materialization at all.
|
|
1241
|
+
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.sendRefusal', () => draftChoiceRefusal()
|
|
1242
|
+
?? (controls?.state.setup?.ready === false ? 'Finish setting up your coding agent before sending. Your draft is saved.' : undefined)));
|
|
1125
1243
|
// A background task the chat named (sayTasks in runTurnInto), stopped through the harness's own control.
|
|
1126
1244
|
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.stopBackgroundTask', async (connectionId, taskId) => {
|
|
1127
1245
|
const connection = frontends.get(connectionId);
|
|
@@ -1162,10 +1280,12 @@ export async function activate(context) {
|
|
|
1162
1280
|
// with a new chat's draft open, a permission chosen is the draft's, never the conversation behind it
|
|
1163
1281
|
if (supplied?.permissions) {
|
|
1164
1282
|
const id = activeDraft() ? undefined : controls?.state.activeSession;
|
|
1165
|
-
if (id)
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1283
|
+
if (!(id ? yoloFor(id) : supplied.permissions === 'yolo' && newChatYolo())) {
|
|
1284
|
+
if (id)
|
|
1285
|
+
choosePermission(id, turnPermission(supplied.permissions));
|
|
1286
|
+
else
|
|
1287
|
+
permission = turnPermission(supplied.permissions);
|
|
1288
|
+
}
|
|
1169
1289
|
controlsChanged.fire();
|
|
1170
1290
|
return currentOptions();
|
|
1171
1291
|
}
|
|
@@ -1189,9 +1309,11 @@ export async function activate(context) {
|
|
|
1189
1309
|
?? controls.state.selection.harness;
|
|
1190
1310
|
const agents = setupAgents(controls.state);
|
|
1191
1311
|
const localChoices = agents.map(agent => ({ label: agent.name, id: agent.harness,
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1312
|
+
// An agent the host's account signs in (its launch carries that account's route) names that account.
|
|
1313
|
+
description: agent.signedIn && agent.account ? `Signed in with ${agent.account}`
|
|
1314
|
+
: agent.harness === 'codex' ? (agent.signedIn ? 'Signed in with ChatGPT' : 'Sign in with ChatGPT…')
|
|
1315
|
+
: agent.harness === 'claude-code' ? (agent.signedIn ? 'Signed in with Claude' : 'Sign in with Claude…')
|
|
1316
|
+
: controls.state.harnesses.find(h => h.id === agent.harness)?.description,
|
|
1195
1317
|
login: controls.state.setup?.actions.some(a => a.harness === agent.harness) ?? false,
|
|
1196
1318
|
autoStart: controls.state.harnesses.some(h => h.id === agent.harness && h.autoStart), configure: false }));
|
|
1197
1319
|
const modelChoices = localChoices.filter(choice => !choice.login && CONFIGURABLE_HARNESSES.includes(choice.id))
|
|
@@ -1441,6 +1563,7 @@ export async function activate(context) {
|
|
|
1441
1563
|
sayTasks();
|
|
1442
1564
|
const unwatchTasks = connection.session.onChange(sayTasks);
|
|
1443
1565
|
let completed = false;
|
|
1566
|
+
let turnStarted = false;
|
|
1444
1567
|
try {
|
|
1445
1568
|
// A new harness transcript is created by input, so binding its identity
|
|
1446
1569
|
// cannot be a prerequisite for that input. Retry the host save as native
|
|
@@ -1450,7 +1573,11 @@ export async function activate(context) {
|
|
|
1450
1573
|
// A turn the person stopped is marked stopped by VS Code itself; only an interruption from elsewhere is said.
|
|
1451
1574
|
if (event.kind === 'turn_interrupted' && token.isCancellationRequested)
|
|
1452
1575
|
return;
|
|
1453
|
-
|
|
1576
|
+
// Native submit publishes its initial user echo before `turn_started`.
|
|
1577
|
+
// Subsequent input belongs in the response while this turn is visible.
|
|
1578
|
+
if (event.kind === 'turn_started')
|
|
1579
|
+
turnStarted = true;
|
|
1580
|
+
writeProjected(stream, projectEvent(event, { ...projection, liveUserMessages: turnStarted }), granted, state);
|
|
1454
1581
|
watch.sweep();
|
|
1455
1582
|
rememberIdentity(connection);
|
|
1456
1583
|
}, { throwIfCancelled, onSubmitted: () => {
|
|
@@ -1723,15 +1850,26 @@ export async function activate(context) {
|
|
|
1723
1850
|
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) => {
|
|
1724
1851
|
if (controls)
|
|
1725
1852
|
await controls.refresh();
|
|
1853
|
+
const draft = activeDraft();
|
|
1854
|
+
const focusedId = focusedResource ? boundId(focusedResource) : undefined;
|
|
1855
|
+
const focusedSession = controls?.state.sessions.find(session => session.id === focusedId);
|
|
1856
|
+
const selectionSource = draft ? 'draft' : focusedAttached ? 'attachment' : focusedSession ? 'conversation'
|
|
1857
|
+
: focusedResource ? 'unavailable' : 'host';
|
|
1858
|
+
const selection = draft ? draftSelection() : focusedAttached ? undefined : focusedSession?.selection
|
|
1859
|
+
?? (focusedResource ? undefined : controls?.state.selection);
|
|
1726
1860
|
if (inspect === true)
|
|
1727
1861
|
return {
|
|
1728
1862
|
setupState: setupState(),
|
|
1729
1863
|
connections: frontends.all.map(c => ({ ...frontends.info(c), permissions: [...c.session.permissions],
|
|
1730
1864
|
pendingRequests: [...c.session.pendingRequests.values()] })),
|
|
1731
|
-
selection
|
|
1865
|
+
selection, selectionSource, focusedSession: focusedId ?? null,
|
|
1866
|
+
hostSelection: controls?.state.selection, activeSession: controls?.state.activeSession,
|
|
1867
|
+
focusedSessionResource: focusedResource?.toString() ?? null, focusGeneration,
|
|
1732
1868
|
busy: Boolean(controls?.state.busy || frontends.all.some(c => c.session.descriptor?.turn_state === 'busy' || c.session.pendingRequests.size)),
|
|
1733
1869
|
setupHandoff: { hostReady: controls?.state.setup?.ready ?? null, complete: setupHandoffComplete(),
|
|
1734
|
-
revealed: setupHandoff ?? null
|
|
1870
|
+
revealed: setupHandoff ?? null,
|
|
1871
|
+
lastCanceledReveal: canceledSetupReveal ? { id: canceledSetupReveal.id, baseUrl: canceledSetupReveal.baseUrl,
|
|
1872
|
+
generation: canceledSetupReveal.generation, focusResource: canceledSetupReveal.focusResource, live: canceledSetupReveal.live } : null },
|
|
1735
1873
|
sessions: controls?.state.sessions, permission: currentOptions().permissions, identity: identity(),
|
|
1736
1874
|
};
|
|
1737
1875
|
const rows = frontends.all.map((connection) => {
|
|
@@ -1883,25 +2021,40 @@ export async function activate(context) {
|
|
|
1883
2021
|
// panel on "Loading…" — observed.
|
|
1884
2022
|
const opening = setTimeout(() => void openResumedSession().catch(error => log.warn(`Could not reveal Chat: ${String(error)}`)), 500);
|
|
1885
2023
|
context.subscriptions.push({ dispose: () => clearTimeout(opening) });
|
|
1886
|
-
// Retry
|
|
1887
|
-
//
|
|
2024
|
+
// Retry attachment and reveal failures. A native navigation cancellation waits for changed focus/target/liveness
|
|
2025
|
+
// or an explicit refresh; it neither earns a completed handoff nor repeats the same veto every three seconds.
|
|
1888
2026
|
let setupTimer;
|
|
1889
2027
|
let setupDisposed = false;
|
|
1890
2028
|
const pollSetup = () => {
|
|
2029
|
+
if (!controls || setupDisposed) {
|
|
2030
|
+
clearTimeout(setupTimer);
|
|
2031
|
+
setupTimer = undefined;
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
1891
2034
|
const choice = activeDraft()?.choice;
|
|
1892
2035
|
const awaitingLogin = (choice?.kind === 'signIn' && choice.harness && !setupAgents(controls.state).some(h => h.harness === choice.harness && h.signedIn))
|
|
1893
2036
|
|| [...(setupFlow?.operations.values() ?? [])].some(operation => operation.phase === 'installing' || operation.phase === 'signing-in');
|
|
1894
2037
|
if ([...(setupFlow?.operations.values() ?? [])].some(operation => operation.phase === 'installing' || operation.phase === 'signing-in'))
|
|
1895
2038
|
return;
|
|
1896
|
-
|
|
2039
|
+
const connection = controls.state.activeSession ? frontends.get(controls.state.activeSession) : undefined;
|
|
2040
|
+
const canceled = connection && revealCanceledFor({ id: connection.id,
|
|
2041
|
+
baseUrl: controls.state.connection?.SUPERCODE_FRONTEND_URL ?? '', generation: focusGeneration,
|
|
2042
|
+
focusResource: focusedResource?.toString() ?? null,
|
|
2043
|
+
live: Boolean(connection.session.pendingRequests.size || connection.session.descriptor?.turn_state === 'busy') }, connection);
|
|
2044
|
+
if (controls.state.setup?.ready && !awaitingLogin && (setupHandoffComplete() || canceled)) {
|
|
2045
|
+
clearTimeout(setupTimer);
|
|
2046
|
+
setupTimer = undefined;
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
if (setupTimer || !controls.state.setup)
|
|
1897
2050
|
return;
|
|
1898
2051
|
setupTimer = setTimeout(() => {
|
|
1899
2052
|
setupTimer = undefined;
|
|
1900
|
-
void refreshSetup().catch(error => log.warn(`Could not refresh
|
|
2053
|
+
void refreshSetup().catch(error => log.warn(`Could not refresh Chat setup: ${String(error)}`)).finally(pollSetup);
|
|
1901
2054
|
}, 3000);
|
|
1902
2055
|
};
|
|
1903
2056
|
pollSetup();
|
|
1904
|
-
context.subscriptions.push(controlsChanged.event(pollSetup));
|
|
2057
|
+
context.subscriptions.push(controlsChanged.event(pollSetup), frontends.onDidChange(pollSetup));
|
|
1905
2058
|
context.subscriptions.push({ dispose: () => { setupDisposed = true; clearTimeout(setupTimer); } });
|
|
1906
2059
|
return {
|
|
1907
2060
|
connect: async (options) => {
|
|
@@ -1940,7 +2093,7 @@ function oneRowPerToolCall(parts) {
|
|
|
1940
2093
|
rows[at] = {
|
|
1941
2094
|
...first,
|
|
1942
2095
|
...part,
|
|
1943
|
-
pastTenseMessage: first.
|
|
2096
|
+
pastTenseMessage: first.pastTenseMessage || part.pastTenseMessage,
|
|
1944
2097
|
invocationMessage: first.invocationMessage ?? part.invocationMessage,
|
|
1945
2098
|
};
|
|
1946
2099
|
continue;
|
|
@@ -2021,11 +2174,12 @@ function historyText(part) {
|
|
|
2021
2174
|
}
|
|
2022
2175
|
function registerSessions(context, frontends, participant, granted, handler, historyParticipant, controls) {
|
|
2023
2176
|
const turns = vscode;
|
|
2177
|
+
const failedResources = new Map();
|
|
2024
2178
|
const items = () => {
|
|
2025
2179
|
return (controls?.catalogue() ?? []).filter(entry => !frontends.get(entry.id)).map(entry => {
|
|
2026
2180
|
return { resource: vscode.Uri.from({ scheme: SESSION_SCHEME, authority: entry.id, path: '/conversation' }), label: entry.title,
|
|
2027
2181
|
description: runtimeIdentity(entry.selection.harness, entry.selection.model, controls?.effective(entry)).name,
|
|
2028
|
-
status: vscode.ChatSessionStatus.Completed, timing: { created: entry.created } };
|
|
2182
|
+
status: failedResources.has(entry.id) ? vscode.ChatSessionStatus.Failed : vscode.ChatSessionStatus.Completed, timing: { created: entry.created } };
|
|
2029
2183
|
});
|
|
2030
2184
|
};
|
|
2031
2185
|
const connectedRow = (connection) => ({
|
|
@@ -2124,13 +2278,38 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2124
2278
|
if (token.isCancellationRequested)
|
|
2125
2279
|
throw new vscode.CancellationError();
|
|
2126
2280
|
const boundHandler = (request, chatContext, stream, token) => handler(request, { ...chatContext, chatSessionContext: { chatSessionItem: { resource, label: 'Conversation' } } }, stream, token);
|
|
2281
|
+
const failedContent = (error) => {
|
|
2282
|
+
const message = opaqueText(String(error?.message ?? error), 2048);
|
|
2283
|
+
if (failedResources.get(resource.authority) !== message) {
|
|
2284
|
+
failedResources.set(resource.authority, message);
|
|
2285
|
+
refreshItems();
|
|
2286
|
+
}
|
|
2287
|
+
return {
|
|
2288
|
+
history: [
|
|
2289
|
+
new turns.ChatRequestTurn(HISTORY_MARKER_PROMPT, undefined, [], 'supercode', []),
|
|
2290
|
+
new turns.ChatResponseTurn([new vscode.ChatResponseMarkdownPart(`**Conversation recovery failed**\n\n${message}`)], {}, 'supercode'),
|
|
2291
|
+
],
|
|
2292
|
+
requestHandler: boundHandler,
|
|
2293
|
+
};
|
|
2294
|
+
};
|
|
2127
2295
|
// Only a native untitled resource can create a conversation. Existing
|
|
2128
2296
|
// resources must resolve their exact saved binding; there is no fallback.
|
|
2129
|
-
|
|
2297
|
+
let connection;
|
|
2298
|
+
try {
|
|
2299
|
+
connection = controls ? (resource.path.startsWith('/untitled-') && !controls.bindings(resource) ? undefined : await controls.resolve(resource, token)) : frontends.get(resource.authority);
|
|
2300
|
+
}
|
|
2301
|
+
catch (error) {
|
|
2302
|
+
if (token.isCancellationRequested || error instanceof vscode.CancellationError)
|
|
2303
|
+
throw error;
|
|
2304
|
+
return failedContent(error);
|
|
2305
|
+
}
|
|
2306
|
+
failedResources.delete(resource.authority);
|
|
2130
2307
|
if (token.isCancellationRequested)
|
|
2131
2308
|
throw new vscode.CancellationError();
|
|
2132
2309
|
if (!connection) {
|
|
2133
|
-
|
|
2310
|
+
const draft = resource.path.startsWith('/untitled-') && !controls?.bindings(resource);
|
|
2311
|
+
return draft ? { history: [], requestHandler: boundHandler }
|
|
2312
|
+
: failedContent(frontends.unavailable ?? 'Could not attach to this saved conversation\'s runtime.');
|
|
2134
2313
|
}
|
|
2135
2314
|
connection.viewReady = false;
|
|
2136
2315
|
connection.viewResource = resource.toString();
|
|
@@ -2143,7 +2322,8 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2143
2322
|
// `state.history`), so the copy taken when this window attached is
|
|
2144
2323
|
// stale as soon as a turn runs. The generated client reuses the
|
|
2145
2324
|
// already-open event stream, so this is a snapshot, not a reconnect.
|
|
2146
|
-
const attached = frontends.get(connection.id) === connection && await connection.session.attach().then(() => true).catch(
|
|
2325
|
+
const attached = frontends.get(connection.id) === connection && await connection.session.attach().then(() => true).catch(error => {
|
|
2326
|
+
connection.session.reportFailure(error);
|
|
2147
2327
|
// An unreachable runtime replays what this window last saw.
|
|
2148
2328
|
return false;
|
|
2149
2329
|
});
|
|
@@ -2157,7 +2337,10 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2157
2337
|
// created. Keep it only as a fallback when no runtime user history is
|
|
2158
2338
|
// available, rather than trying to align equal-looking prompt text.
|
|
2159
2339
|
const runtimeHistory = connection.session.history;
|
|
2160
|
-
const replay =
|
|
2340
|
+
const replay = [
|
|
2341
|
+
...connection.session.historyEvents,
|
|
2342
|
+
...connection.session.replay.filter(event => event.sequence > connection.session.historyCursor),
|
|
2343
|
+
];
|
|
2161
2344
|
const hasRuntimeHistory = runtimeHistory.length > 0 || replay.some(event => event.kind === 'user_message');
|
|
2162
2345
|
const usingSavedHistory = !hasRuntimeHistory && Boolean(connection.persistedHistory?.length);
|
|
2163
2346
|
const canonicalHistory = usingSavedHistory ? connection.persistedHistory : runtimeHistory;
|
|
@@ -2214,6 +2397,8 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2214
2397
|
}
|
|
2215
2398
|
if (usingSavedHistory && connection.historyTruncated)
|
|
2216
2399
|
history.push(new turns.ChatRequestTurn('[Earlier conversation history]', undefined, [], authorId, []), new turns.ChatResponseTurn([new vscode.ChatResponseMarkdownPart('_Showing the latest 120 saved messages. Earlier context remains in the harness session._')], {}, authorId));
|
|
2400
|
+
if (connection.session.historyEventsDropped > 0)
|
|
2401
|
+
history.push(new turns.ChatRequestTurn(HISTORY_MARKER_PROMPT, undefined, [], authorId, []), new turns.ChatResponseTurn([new vscode.ChatResponseMarkdownPart(`_${connection.session.historyEventsDropped} earlier runtime events are outside this display snapshot._`)], {}, authorId));
|
|
2217
2402
|
for (const turn of projected) {
|
|
2218
2403
|
if (turn.role === 'user') {
|
|
2219
2404
|
history.push(new turns.ChatRequestTurn(turn.prompt ?? '', undefined, [], authorId, []));
|
|
@@ -2221,6 +2406,11 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2221
2406
|
}
|
|
2222
2407
|
history.push(new turns.ChatResponseTurn(historyParts(oneRowPerToolCall(turn.parts ?? [])), {}, authorId));
|
|
2223
2408
|
}
|
|
2409
|
+
if (connection.session.failure) {
|
|
2410
|
+
// An explicit runtime marker pairs this notice without presenting
|
|
2411
|
+
// it as a person's prompt. The native busy state stays unchanged.
|
|
2412
|
+
history.push(new turns.ChatRequestTurn(HISTORY_MARKER_PROMPT, undefined, [], authorId, []), new turns.ChatResponseTurn([new vscode.ChatResponseMarkdownPart(`**Conversation recovery failed**\n\n${opaqueText(connection.session.failure.message, 2048)}`)], {}, authorId));
|
|
2413
|
+
}
|
|
2224
2414
|
const renderedCursor = connection.session.cursor;
|
|
2225
2415
|
const pendingAtCursor = [...connection.session.pendingRequests.values()];
|
|
2226
2416
|
if (!live)
|
|
@@ -2249,6 +2439,7 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2249
2439
|
await observeActiveResponse(connection.session, token, (event) => {
|
|
2250
2440
|
writeProjected(stream, projectEvent(event, {
|
|
2251
2441
|
connectionId: connection.id,
|
|
2442
|
+
liveUserMessages: true,
|
|
2252
2443
|
pendingRequest: (id) => connection.session.pendingRequests.get(id) ?? connection.session.resolvedRequests.get(id),
|
|
2253
2444
|
canRespond: Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve')),
|
|
2254
2445
|
}), granted, state);
|
|
@@ -2256,6 +2447,11 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2256
2447
|
controls?.rememberIdentity(connection);
|
|
2257
2448
|
}, () => { connection.viewReady = true; }, renderedCursor);
|
|
2258
2449
|
}
|
|
2450
|
+
catch (error) {
|
|
2451
|
+
if (token.isCancellationRequested || error instanceof vscode.CancellationError)
|
|
2452
|
+
throw error;
|
|
2453
|
+
stream.markdown(`\n\n**Conversation stream failed**\n\n${opaqueText(String(error?.message ?? error), 2048)}\n\n`);
|
|
2454
|
+
}
|
|
2259
2455
|
finally {
|
|
2260
2456
|
connection.viewReady = connection.session.descriptor?.turn_state !== 'busy' && !connection.session.pendingRequests.size;
|
|
2261
2457
|
watch?.dispose();
|
package/dist/host-controls.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export interface ManagedChatSession {
|
|
|
27
27
|
title: string;
|
|
28
28
|
created: number;
|
|
29
29
|
defaultPermission?: 'ask' | 'autoApprove';
|
|
30
|
+
runtimePolicy?: 'default' | 'yolo';
|
|
30
31
|
}
|
|
31
32
|
/** Host-owned operations. The host revalidates commands before native terminal execution. */
|
|
32
33
|
export interface ChatSetupAction {
|
|
@@ -75,6 +76,9 @@ export interface HostState {
|
|
|
75
76
|
/** The mode a new chat's picker shows. A conversation's own default is its `ManagedChatSession.defaultPermission`;
|
|
76
77
|
* this is never applied to one that exists. `autoApprove` takes effect only where the runtime can answer approvals. */
|
|
77
78
|
newChatPermission?: 'ask' | 'autoApprove';
|
|
79
|
+
/** Project policy for the next start/resume; an existing runtime uses its recorded session policy. */
|
|
80
|
+
newChatRuntimePolicy?: 'default' | 'yolo' | null;
|
|
81
|
+
runtimePolicyError?: string | null;
|
|
78
82
|
}
|
|
79
83
|
export interface HostSelectionResult extends HostState {
|
|
80
84
|
connection: Record<string, string>;
|
package/dist/host-controls.js
CHANGED
|
@@ -70,9 +70,12 @@ export function runtimeIdentity(harness, model, effective) {
|
|
|
70
70
|
const harnessName = harnessDisplayName(harness);
|
|
71
71
|
const knownModel = effective?.model || (model && model !== harness ? model : '');
|
|
72
72
|
const modelName = effective?.modelName || { sonnet: 'Claude Sonnet', opus: 'Claude Opus', haiku: 'Claude Haiku' }[knownModel] || knownModel;
|
|
73
|
+
// The provider of a display name ("Claude Opus 4.8"), whose mark is media/<provider>.svg (supercode-ui's catalogue);
|
|
74
|
+
// the catalogue's modelProvider reads model ids.
|
|
73
75
|
const provider = /claude|sonnet|opus|haiku/i.test(modelName) || (!modelName && harness === 'claude-code') ? 'anthropic'
|
|
74
76
|
: /^(gpt|o[134]|chatgpt)|\/gpt/i.test(modelName) || (!modelName && harness === 'codex') ? 'openai'
|
|
75
|
-
: /gemini/i.test(modelName) || (!modelName && harness === 'gemini') ? 'google'
|
|
77
|
+
: /gemini/i.test(modelName) || (!modelName && harness === 'gemini') ? 'google'
|
|
78
|
+
: /grok/i.test(modelName) || (!modelName && harness === 'grok') ? 'xai' : undefined;
|
|
76
79
|
const effort = effective?.effort ? ` · ${effective.effort}` : '';
|
|
77
80
|
return { name: modelName ? `${modelName}${effort}` : `Model chosen by ${harnessName}`, provider };
|
|
78
81
|
}
|
package/dist/native-session.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CancellationToken } from 'vscode';
|
|
2
2
|
import type { FrontendEvent } from '../vendor/supercode-frontend/generated.mjs';
|
|
3
3
|
import { type VscodeFrontendSession } from './session.js';
|
|
4
|
-
export declare function sessionStatus(session: Pick<VscodeFrontendSession, 'descriptor' | 'pendingRequests'>): 'NeedsInput' | 'InProgress' | 'Completed';
|
|
4
|
+
export declare function sessionStatus(session: Pick<VscodeFrontendSession, 'descriptor' | 'pendingRequests' | 'failure'>): 'NeedsInput' | 'Failed' | 'InProgress' | 'Completed';
|
|
5
5
|
/** Follow a restored turn, including the interval between snapshot and callback. */
|
|
6
|
-
export declare function observeActiveResponse(session: Pick<VscodeFrontendSession, 'descriptor' | 'pendingRequests' | 'onEvent' | 'interrupt' | 'actions' | 'replay'>, token: CancellationToken, write: (event: FrontendEvent) => void, onSubscribed?: () => void, afterSequence?: number): Promise<void>;
|
|
6
|
+
export declare function observeActiveResponse(session: Pick<VscodeFrontendSession, 'descriptor' | 'pendingRequests' | 'onEvent' | 'onChange' | 'onFailure' | 'interrupt' | 'actions' | 'replay'>, token: CancellationToken, write: (event: FrontendEvent) => void, onSubscribed?: () => void, afterSequence?: number): Promise<void>;
|