@volter/supercode-frontend-vscode 0.1.46 → 0.1.48
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/dist/extension.js +38 -0
- package/dist/session.d.ts +16 -0
- package/dist/session.js +39 -0
- package/package.json +2 -2
- package/vendor/supercode-frontend/contract.json +3 -0
- package/vendor/supercode-frontend/generated-client.d.mts +2 -1
- package/vendor/supercode-frontend/generated-client.mjs +1 -0
- package/vendor/supercode-frontend/generated.d.mts +8 -2
- package/vendor/supercode-frontend/generated.mjs +1 -0
- package/vendor/supercode-frontend/package.json +1 -1
package/dist/extension.js
CHANGED
|
@@ -994,6 +994,22 @@ export async function activate(context) {
|
|
|
994
994
|
watchApprovals: (connection, stream, token) => watchApprovals(connection, stream, token, () => approvalFor(connection.id)),
|
|
995
995
|
};
|
|
996
996
|
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.activateSession', async (resource) => { await connectionFor(vscode.Uri.parse(resource)); }));
|
|
997
|
+
// A background task the chat named (sayTasks in runTurnInto), stopped through the harness's own control.
|
|
998
|
+
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.stopBackgroundTask', async (connectionId, taskId) => {
|
|
999
|
+
const connection = frontends.get(connectionId);
|
|
1000
|
+
const task = connection?.session.backgroundTasks.find(candidate => candidate.id === taskId);
|
|
1001
|
+
if (!connection || !task) {
|
|
1002
|
+
void vscode.window.showInformationMessage('That background task is no longer running.');
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
try {
|
|
1006
|
+
const stopped = await connection.session.stopTask(taskId);
|
|
1007
|
+
void vscode.window.showInformationMessage(stopped ? `Stopped ${task.description || taskId}.` : `The harness did not stop ${task.description || taskId}.`);
|
|
1008
|
+
}
|
|
1009
|
+
catch (error) {
|
|
1010
|
+
void vscode.window.showErrorMessage(`Could not stop ${task.description || taskId}: ${String(error?.message ?? error)}`);
|
|
1011
|
+
}
|
|
1012
|
+
}));
|
|
997
1013
|
// The host's native focus event is authoritative even when a cached model
|
|
998
1014
|
// skips content resolution. Untitled/non-harness focus never starts a runtime.
|
|
999
1015
|
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.focusSession', (resource) => {
|
|
@@ -1210,6 +1226,27 @@ export async function activate(context) {
|
|
|
1210
1226
|
});
|
|
1211
1227
|
const watch = watchApprovals(connection, stream, token, approval);
|
|
1212
1228
|
state.quiet = watch.answers;
|
|
1229
|
+
// The harness's own background tasks (a Monitor, a background shell): each event one reports is a turn of its own,
|
|
1230
|
+
// which this message waits behind, and nothing here would show it. Said once per change of the set, each with a
|
|
1231
|
+
// way to stop it.
|
|
1232
|
+
let saidTasks = '';
|
|
1233
|
+
const sayTasks = () => {
|
|
1234
|
+
const tasks = connection.session.backgroundTasks;
|
|
1235
|
+
const key = tasks.map(task => task.id).join(',');
|
|
1236
|
+
if (key === saidTasks)
|
|
1237
|
+
return;
|
|
1238
|
+
saidTasks = key;
|
|
1239
|
+
if (!tasks.length) {
|
|
1240
|
+
stream.markdown('\n\n_No background task is running any more._\n\n');
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
stream.markdown(`\n\n_${tasks.length === 1 ? 'A background task is' : `${tasks.length} background tasks are`} still running in this session. The harness answers each of its events as a turn of its own, so this message can wait behind them:_\n\n`);
|
|
1244
|
+
for (const task of tasks) {
|
|
1245
|
+
stream.button({ command: 'supercode.frontend.stopBackgroundTask', title: `Stop ${opaqueText(task.description || task.id, 80)}`, arguments: [connection.id, task.id] });
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
sayTasks();
|
|
1249
|
+
const unwatchTasks = connection.session.onChange(sayTasks);
|
|
1213
1250
|
let completed = false;
|
|
1214
1251
|
try {
|
|
1215
1252
|
await connection.session.runTurn(prompt, (event) => {
|
|
@@ -1219,6 +1256,7 @@ export async function activate(context) {
|
|
|
1219
1256
|
completed = true;
|
|
1220
1257
|
}
|
|
1221
1258
|
finally {
|
|
1259
|
+
unwatchTasks();
|
|
1222
1260
|
watch.dispose();
|
|
1223
1261
|
cancellation.dispose();
|
|
1224
1262
|
if (controls && !attachedIds.has(connection.id)) {
|
package/dist/session.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export interface VscodeRuntimeClient {
|
|
|
15
15
|
steer(prompt: string): Promise<{
|
|
16
16
|
queued: boolean;
|
|
17
17
|
}>;
|
|
18
|
+
stopTask(taskId: string): Promise<{
|
|
19
|
+
stopped: boolean;
|
|
20
|
+
}>;
|
|
18
21
|
interrupt(): Promise<{
|
|
19
22
|
interrupted: boolean;
|
|
20
23
|
}>;
|
|
@@ -62,6 +65,12 @@ export declare const TURN_TERMINAL_KINDS: readonly ["turn_succeeded", "turn_fail
|
|
|
62
65
|
* the view reports.
|
|
63
66
|
*/
|
|
64
67
|
export declare const LEASE_ERROR_NAMES: readonly string[];
|
|
68
|
+
/** One of the harness's own background tasks (a Monitor, a background shell), as it last listed them. */
|
|
69
|
+
export interface BackgroundTask {
|
|
70
|
+
id: string;
|
|
71
|
+
kind: string;
|
|
72
|
+
description: string;
|
|
73
|
+
}
|
|
65
74
|
/**
|
|
66
75
|
* One attachment to one already-running Volter Harness SDK runtime: cursor-safe
|
|
67
76
|
* event projection, permission narrowing, and the semantic actions the runtime
|
|
@@ -86,6 +95,11 @@ export declare class VscodeFrontendSession {
|
|
|
86
95
|
*/
|
|
87
96
|
replay: FrontendEvent[];
|
|
88
97
|
pendingRequests: Map<number, FrontendRequest>;
|
|
98
|
+
/**
|
|
99
|
+
* The harness's own background tasks as it last listed them (`background_tasks`). Each event one reports starts a
|
|
100
|
+
* turn of its own, so while any runs the session can be busy with turns no one here submitted.
|
|
101
|
+
*/
|
|
102
|
+
backgroundTasks: BackgroundTask[];
|
|
89
103
|
/**
|
|
90
104
|
* Requests this attachment has seen resolved. `request_resolved` deletes the
|
|
91
105
|
* pending entry while it is being projected, so without this the resolution
|
|
@@ -136,6 +150,8 @@ export declare class VscodeFrontendSession {
|
|
|
136
150
|
submit(prompt: string): Promise<void>;
|
|
137
151
|
steer(prompt: string): Promise<void>;
|
|
138
152
|
interrupt(): Promise<void>;
|
|
153
|
+
/** Stop one of the harness's own background tasks, by the id it listed it with. */
|
|
154
|
+
stopTask(taskId: string): Promise<boolean>;
|
|
139
155
|
respond(response: FrontendResponse): Promise<void>;
|
|
140
156
|
detach(): Promise<void>;
|
|
141
157
|
close(): Promise<void>;
|
package/dist/session.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/** How long a turn waits on a connection that dropped before saying it was lost (the follow loop's re-attach backs
|
|
2
|
+
* off to 15 s; this outlasts a few of its attempts). */
|
|
3
|
+
const DISCONNECT_GRACE_MS = 60_000;
|
|
1
4
|
/** The request this attachment tried to answer is no longer open. */
|
|
2
5
|
export class RequestNotPendingError extends Error {
|
|
3
6
|
requestId;
|
|
@@ -53,6 +56,11 @@ export class VscodeFrontendSession {
|
|
|
53
56
|
*/
|
|
54
57
|
replay = [];
|
|
55
58
|
pendingRequests = new Map();
|
|
59
|
+
/**
|
|
60
|
+
* The harness's own background tasks as it last listed them (`background_tasks`). Each event one reports starts a
|
|
61
|
+
* turn of its own, so while any runs the session can be busy with turns no one here submitted.
|
|
62
|
+
*/
|
|
63
|
+
backgroundTasks = [];
|
|
56
64
|
/**
|
|
57
65
|
* Requests this attachment has seen resolved. `request_resolved` deletes the
|
|
58
66
|
* pending entry while it is being projected, so without this the resolution
|
|
@@ -102,6 +110,7 @@ export class VscodeFrontendSession {
|
|
|
102
110
|
const staged = {
|
|
103
111
|
descriptor: structuredClone(snapshot.descriptor),
|
|
104
112
|
pendingRequests: new Map([...this.pendingRequests].map(([id, request]) => [id, structuredClone(request)])),
|
|
113
|
+
backgroundTasks: structuredClone(this.backgroundTasks),
|
|
105
114
|
cursor: Math.max(this.cursor, snapshot.history_cursor),
|
|
106
115
|
};
|
|
107
116
|
// Validate and project the whole attachment before any observable field
|
|
@@ -126,6 +135,7 @@ export class VscodeFrontendSession {
|
|
|
126
135
|
this.historyCursor = snapshot.history_cursor;
|
|
127
136
|
this.appendTranscript(structuredClone(snapshot.replay));
|
|
128
137
|
this.pendingRequests = staged.pendingRequests;
|
|
138
|
+
this.backgroundTasks = staged.backgroundTasks;
|
|
129
139
|
this.cursor = staged.cursor;
|
|
130
140
|
this.connection = 'connected';
|
|
131
141
|
this.attachedAtMs ??= Date.now();
|
|
@@ -260,6 +270,11 @@ export class VscodeFrontendSession {
|
|
|
260
270
|
if (request && typeof request.id === 'number')
|
|
261
271
|
state.pendingRequests.set(request.id, structuredClone(request));
|
|
262
272
|
}
|
|
273
|
+
if (event.kind === 'background_tasks' && Array.isArray(payload.tasks)) {
|
|
274
|
+
state.backgroundTasks = payload.tasks
|
|
275
|
+
.filter(task => task && typeof task.id === 'string')
|
|
276
|
+
.map(task => ({ id: task.id, kind: String(task.kind ?? 'task'), description: String(task.description ?? '') }));
|
|
277
|
+
}
|
|
263
278
|
if (event.kind === 'request_resolved' && typeof payload.request_id === 'number') {
|
|
264
279
|
const resolved = state.pendingRequests.get(payload.request_id);
|
|
265
280
|
if (resolved) {
|
|
@@ -334,6 +349,11 @@ export class VscodeFrontendSession {
|
|
|
334
349
|
this.require('interrupt');
|
|
335
350
|
await this.withControl(() => this.client.interrupt());
|
|
336
351
|
}
|
|
352
|
+
/** Stop one of the harness's own background tasks, by the id it listed it with. */
|
|
353
|
+
async stopTask(taskId) {
|
|
354
|
+
const result = await this.withControl(() => this.client.stopTask(taskId));
|
|
355
|
+
return result?.stopped === true;
|
|
356
|
+
}
|
|
337
357
|
async respond(response) {
|
|
338
358
|
this.require('respond');
|
|
339
359
|
if (!this.pendingRequests.has(response.request_id))
|
|
@@ -402,7 +422,25 @@ export class VscodeFrontendSession {
|
|
|
402
422
|
// that hangs one is worse than one that ends it a beat early. The
|
|
403
423
|
// descriptor going idle after the submit is the second, independent signal.
|
|
404
424
|
let sawBusy = false;
|
|
425
|
+
// A stream that ends is re-attached from its cursor (the extension's follow loop), and the replay ends the turn;
|
|
426
|
+
// only a connection closed, or one still down after DISCONNECT_GRACE_MS, ends the wait, said: a response held open
|
|
427
|
+
// with nothing in it is the silent failure.
|
|
428
|
+
let down;
|
|
429
|
+
const lost = () => fail?.(new Error('The connection to this conversation\'s runtime was lost before its turn ended; the turn may still be running. Reopen the chat to see where it stands.'));
|
|
430
|
+
finished.catch(() => undefined); // a loss while the submit is still in flight is answered by the submit's own error
|
|
405
431
|
const unwatch = this.onChange(() => {
|
|
432
|
+
if (this.connection === 'closed') {
|
|
433
|
+
lost();
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (this.connection === 'connected') {
|
|
437
|
+
clearTimeout(down);
|
|
438
|
+
down = undefined;
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
down ??= setTimeout(lost, DISCONNECT_GRACE_MS);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
406
444
|
const state = this.descriptor?.turn_state;
|
|
407
445
|
if (state === 'busy')
|
|
408
446
|
sawBusy = true;
|
|
@@ -417,6 +455,7 @@ export class VscodeFrontendSession {
|
|
|
417
455
|
return await finished;
|
|
418
456
|
}
|
|
419
457
|
finally {
|
|
458
|
+
clearTimeout(down);
|
|
420
459
|
unsubscribe();
|
|
421
460
|
unwatch();
|
|
422
461
|
}
|
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.48",
|
|
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",
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
},
|
|
191
191
|
"supercodeFrontendContract": {
|
|
192
192
|
"name": "@volter/supercode-frontend",
|
|
193
|
-
"version": "0.2.
|
|
193
|
+
"version": "0.2.9",
|
|
194
194
|
"mode": "generated-artifact"
|
|
195
195
|
},
|
|
196
196
|
"license": "MIT OR Apache-2.0"
|
|
@@ -251,6 +251,8 @@
|
|
|
251
251
|
"Reply": {"kind": "object", "fields": {"reply": {"type": "string"}}},
|
|
252
252
|
"Interrupted": {"kind": "object", "fields": {"interrupted": {"type": "boolean"}}},
|
|
253
253
|
"Queued": {"kind": "object", "fields": {"queued": {"type": "boolean"}}},
|
|
254
|
+
"TaskParams": {"kind": "object", "fields": {"task_id": {"type": "string"}}},
|
|
255
|
+
"Stopped": {"kind": "object", "fields": {"stopped": {"type": "boolean"}}},
|
|
254
256
|
"Closed": {"kind": "object", "fields": {"closed": {"type": "boolean"}}}
|
|
255
257
|
},
|
|
256
258
|
"methods": [
|
|
@@ -260,6 +262,7 @@
|
|
|
260
262
|
{"id": "submit", "wire": "frontend.v2.submit", "sdk_action": "input", "params": "MultimodalPromptParams", "result": "Reply", "compatibility": "completion_waiting_alias", "transports": ["local", "http", "websocket", "acp"]},
|
|
261
263
|
{"id": "interrupt", "wire": "frontend.v2.interrupt", "sdk_action": "interrupt", "params": "Empty", "result": "Interrupted", "transports": ["local", "http", "websocket", "acp"]},
|
|
262
264
|
{"id": "steer", "wire": "frontend.v2.steer", "sdk_action": "steer", "params": "PromptParams", "result": "Queued", "transports": ["local", "http", "websocket", "acp"]},
|
|
265
|
+
{"id": "stop_task", "wire": "frontend.v2.stop_task", "sdk_action": "stop_task", "params": "TaskParams", "result": "Stopped", "transports": ["local", "http", "websocket", "acp"]},
|
|
263
266
|
{"id": "respond", "wire": "frontend.v2.respond", "sdk_action": "respond", "params": "RespondParams", "result": "Accepted", "transports": ["local", "http", "websocket", "acp"]},
|
|
264
267
|
{"id": "invoke", "wire": "frontend.v2.invoke", "sdk_action": "input", "params": "InvokeParams", "result": "FrontendOperationResult", "transports": ["local", "http", "websocket", "acp"]},
|
|
265
268
|
{"id": "lease", "wire": "frontend.v2.lease", "sdk_action": "events", "params": "Empty", "result": "RuntimeLeaseSnapshot", "transports": ["local", "http", "websocket", "acp"]},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @generated by sdk/frontend/generate.mjs from contract.json. Do not edit.
|
|
2
|
-
import type { Accepted, Closed, FrontendAttachSnapshot, FrontendOperationInvocation, FrontendOperationResult, FrontendResponse, FrontendRuntimeDescriptor, FrontendWireMethod, Interrupted, Queued, Reply, RuntimeLeaseSnapshot } from './generated.mjs';
|
|
2
|
+
import type { Accepted, Closed, FrontendAttachSnapshot, FrontendOperationInvocation, FrontendOperationResult, FrontendResponse, FrontendRuntimeDescriptor, FrontendWireMethod, Interrupted, Queued, Reply, RuntimeLeaseSnapshot, Stopped } from './generated.mjs';
|
|
3
3
|
export abstract class GeneratedFrontendClient<Options = unknown> {
|
|
4
4
|
abstract rpc(method: FrontendWireMethod, params?: Record<string, unknown>, options?: Options): Promise<unknown>;
|
|
5
5
|
describe(options?: Options): Promise<FrontendRuntimeDescriptor>;
|
|
@@ -8,6 +8,7 @@ export abstract class GeneratedFrontendClient<Options = unknown> {
|
|
|
8
8
|
submit(prompt: string, options?: Options & {imageUrls?: string[]}): Promise<Reply>;
|
|
9
9
|
interrupt(options?: Options): Promise<Interrupted>;
|
|
10
10
|
steer(prompt: string, options?: Options): Promise<Queued>;
|
|
11
|
+
stopTask(taskId: string, options?: Options): Promise<Stopped>;
|
|
11
12
|
respond(response: FrontendResponse, options?: Options): Promise<Accepted>;
|
|
12
13
|
invoke(operation: FrontendOperationInvocation, options?: Options): Promise<FrontendOperationResult>;
|
|
13
14
|
lease(options?: Options): Promise<RuntimeLeaseSnapshot>;
|
|
@@ -9,6 +9,7 @@ export class GeneratedFrontendClient {
|
|
|
9
9
|
submit(prompt, { imageUrls, ...options } = {}) { return this.rpc(FRONTEND_METHODS.submit, { prompt, ...(imageUrls?.length ? {image_urls:imageUrls} : {}) }, options); }
|
|
10
10
|
interrupt(options) { return this.rpc(FRONTEND_METHODS.interrupt, {}, options); }
|
|
11
11
|
steer(prompt, options) { return this.rpc(FRONTEND_METHODS.steer, { prompt }, options); }
|
|
12
|
+
stopTask(taskId, options) { return this.rpc(FRONTEND_METHODS.stop_task, { task_id: taskId }, options); }
|
|
12
13
|
respond(response, options) { return this.rpc(FRONTEND_METHODS.respond, { response }, options); }
|
|
13
14
|
invoke(operation, options) { return this.rpc(FRONTEND_METHODS.invoke, { operation }, options); }
|
|
14
15
|
lease(options) { return this.rpc(FRONTEND_METHODS.lease, {}, options); }
|
|
@@ -139,8 +139,14 @@ export interface Interrupted {
|
|
|
139
139
|
export interface Queued {
|
|
140
140
|
queued: boolean;
|
|
141
141
|
}
|
|
142
|
+
export interface TaskParams {
|
|
143
|
+
task_id: string;
|
|
144
|
+
}
|
|
145
|
+
export interface Stopped {
|
|
146
|
+
stopped: boolean;
|
|
147
|
+
}
|
|
142
148
|
export interface Closed {
|
|
143
149
|
closed: boolean;
|
|
144
150
|
}
|
|
145
|
-
export type FrontendMethodId = 'describe' | 'attach' | 'send_input' | 'submit' | 'interrupt' | 'steer' | 'respond' | 'invoke' | 'lease' | 'acquire_control' | 'take_control' | 'heartbeat' | 'detach' | 'close';
|
|
146
|
-
export type FrontendWireMethod = 'frontend.v2.describe' | 'frontend.v2.attach' | 'frontend.v2.send_input' | 'frontend.v2.submit' | 'frontend.v2.interrupt' | 'frontend.v2.steer' | 'frontend.v2.respond' | 'frontend.v2.invoke' | 'frontend.v2.lease' | 'frontend.v2.acquire_control' | 'frontend.v2.take_control' | 'frontend.v2.heartbeat' | 'frontend.v2.detach' | 'frontend.v2.close';
|
|
151
|
+
export type FrontendMethodId = 'describe' | 'attach' | 'send_input' | 'submit' | 'interrupt' | 'steer' | 'stop_task' | 'respond' | 'invoke' | 'lease' | 'acquire_control' | 'take_control' | 'heartbeat' | 'detach' | 'close';
|
|
152
|
+
export type FrontendWireMethod = 'frontend.v2.describe' | 'frontend.v2.attach' | 'frontend.v2.send_input' | 'frontend.v2.submit' | 'frontend.v2.interrupt' | 'frontend.v2.steer' | 'frontend.v2.stop_task' | 'frontend.v2.respond' | 'frontend.v2.invoke' | 'frontend.v2.lease' | 'frontend.v2.acquire_control' | 'frontend.v2.take_control' | 'frontend.v2.heartbeat' | 'frontend.v2.detach' | 'frontend.v2.close';
|
|
@@ -7,6 +7,7 @@ export const FRONTEND_METHODS = Object.freeze({
|
|
|
7
7
|
"submit": "frontend.v2.submit",
|
|
8
8
|
"interrupt": "frontend.v2.interrupt",
|
|
9
9
|
"steer": "frontend.v2.steer",
|
|
10
|
+
"stop_task": "frontend.v2.stop_task",
|
|
10
11
|
"respond": "frontend.v2.respond",
|
|
11
12
|
"invoke": "frontend.v2.invoke",
|
|
12
13
|
"lease": "frontend.v2.lease",
|