@volter/supercode-frontend-vscode 0.1.47 → 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 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
@@ -56,6 +56,11 @@ export class VscodeFrontendSession {
56
56
  */
57
57
  replay = [];
58
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 = [];
59
64
  /**
60
65
  * Requests this attachment has seen resolved. `request_resolved` deletes the
61
66
  * pending entry while it is being projected, so without this the resolution
@@ -105,6 +110,7 @@ export class VscodeFrontendSession {
105
110
  const staged = {
106
111
  descriptor: structuredClone(snapshot.descriptor),
107
112
  pendingRequests: new Map([...this.pendingRequests].map(([id, request]) => [id, structuredClone(request)])),
113
+ backgroundTasks: structuredClone(this.backgroundTasks),
108
114
  cursor: Math.max(this.cursor, snapshot.history_cursor),
109
115
  };
110
116
  // Validate and project the whole attachment before any observable field
@@ -129,6 +135,7 @@ export class VscodeFrontendSession {
129
135
  this.historyCursor = snapshot.history_cursor;
130
136
  this.appendTranscript(structuredClone(snapshot.replay));
131
137
  this.pendingRequests = staged.pendingRequests;
138
+ this.backgroundTasks = staged.backgroundTasks;
132
139
  this.cursor = staged.cursor;
133
140
  this.connection = 'connected';
134
141
  this.attachedAtMs ??= Date.now();
@@ -263,6 +270,11 @@ export class VscodeFrontendSession {
263
270
  if (request && typeof request.id === 'number')
264
271
  state.pendingRequests.set(request.id, structuredClone(request));
265
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
+ }
266
278
  if (event.kind === 'request_resolved' && typeof payload.request_id === 'number') {
267
279
  const resolved = state.pendingRequests.get(payload.request_id);
268
280
  if (resolved) {
@@ -337,6 +349,11 @@ export class VscodeFrontendSession {
337
349
  this.require('interrupt');
338
350
  await this.withControl(() => this.client.interrupt());
339
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
+ }
340
357
  async respond(response) {
341
358
  this.require('respond');
342
359
  if (!this.pendingRequests.has(response.request_id))
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.47",
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.8",
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",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-frontend",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "type": "module",
5
5
  "description": "Generated language-neutral attachment client for Volter Harness SDK runtimes",
6
6
  "exports": {