@volter/supercode-frontend-vscode 0.1.53 → 0.1.55
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 +24 -3
- package/dist/extension.js +102 -13
- package/dist/session.d.ts +3 -0
- package/dist/session.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -400,14 +400,35 @@ running response is replayed through `activeResponseCallback`, with each pending
|
|
|
400
400
|
request drawn once from the snapshot and later events followed by sequence.
|
|
401
401
|
An idle runtime with a pending request also keeps this callback open until the
|
|
402
402
|
request is resolved. Standalone requests use the explicit runtime-history marker
|
|
403
|
-
instead of reopening an earlier answer.
|
|
404
|
-
|
|
405
|
-
|
|
403
|
+
instead of reopening an earlier answer. A new conversation sends its first input
|
|
404
|
+
before trying to save its native identity: the harness creates its transcript
|
|
405
|
+
from that input. Identity saves run after input acceptance, retry with later
|
|
406
|
+
native events until one succeeds, and finish at the end of a turn. A successful
|
|
407
|
+
host save acknowledges that exact runtime connection, native id and catalogue
|
|
408
|
+
identity; completion skips another save while all three still match. A reload
|
|
409
|
+
or replacement connection must obtain its own acknowledgement. Completion
|
|
410
|
+
refreshes the host before that comparison; a changed/cleared binding or failed
|
|
411
|
+
refresh invalidates the local acknowledgement and retains the save attempt.
|
|
412
|
+
After reload,
|
|
413
|
+
the exact runtime's user history/events recover that save obligation; restored
|
|
414
|
+
snapshots, later events and response completion retry through the same host
|
|
415
|
+
checks without sending input. A busy state or standalone request alone does
|
|
416
|
+
not establish submitted input. A failed save
|
|
417
|
+
does not resend input or abort a running turn. An unbound conversation whose
|
|
418
|
+
final save fails says the prompt was submitted, and tells the person to keep Chat
|
|
419
|
+
open and avoid repeating the prompt.
|
|
420
|
+
Stop is observed throughout the turn and checked before input, including a lease
|
|
406
421
|
renewal retry. Retained request/decision records with no originating replay
|
|
407
422
|
prompt appear in a separate runtime-history turn.
|
|
408
423
|
The [reload investigation](../../docs/evidence/chat-pending-approval-reload-2026-10-08.md)
|
|
409
424
|
separates the retained first-user failure from this source change; a live
|
|
410
425
|
candidate reload and approval click remain unmeasured.
|
|
426
|
+
The [first-input regression investigation](../../docs/evidence/chat-first-input-binding-2026-10-08.md)
|
|
427
|
+
records the 0.1.52 binding-order failure, the correction and its outstanding
|
|
428
|
+
product reading.
|
|
429
|
+
The [identity-save investigation](../../docs/evidence/chat-identity-save-acknowledgement-2026-10-08.md)
|
|
430
|
+
names the repeated host lookup removed at completion; candidate latency and
|
|
431
|
+
real-editor qualification remain open.
|
|
411
432
|
|
|
412
433
|
The pinned Code-OSS host needs a resource-scoping repair in its input-state change
|
|
413
434
|
routing: stock code broadcasts changes to all input states of a provider. The
|
package/dist/extension.js
CHANGED
|
@@ -422,6 +422,92 @@ export async function activate(context) {
|
|
|
422
422
|
// them, so they open as what they are: attachments, shown as their runtime
|
|
423
423
|
// reports them, and never taken for the conversation the host manages.
|
|
424
424
|
const attachedIds = new Set();
|
|
425
|
+
// A request handler can disappear on reload. Recover this obligation from
|
|
426
|
+
// the exact runtime's user history/events when its connection is reattached.
|
|
427
|
+
const identitySaves = new WeakMap();
|
|
428
|
+
const identitySave = (connection) => {
|
|
429
|
+
let save = identitySaves.get(connection);
|
|
430
|
+
if (!save) {
|
|
431
|
+
save = { submitted: false, remembered: false, again: false, warned: false };
|
|
432
|
+
identitySaves.set(connection, save);
|
|
433
|
+
}
|
|
434
|
+
save.submitted ||= connection.session.history.some(message => message.role === 'user')
|
|
435
|
+
|| connection.session.replay.some(event => event.kind === 'user_message');
|
|
436
|
+
return save;
|
|
437
|
+
};
|
|
438
|
+
const identitySaved = (connection, nativeId) => {
|
|
439
|
+
const save = identitySave(connection);
|
|
440
|
+
save.remembered = true;
|
|
441
|
+
const identity = controls?.state.sessions.find(session => session.id === connection.id)?.identity;
|
|
442
|
+
// Only a successful host save acknowledges this runtime/native-id pair.
|
|
443
|
+
// A restored catalogue identity alone is not a save receipt for a new connection.
|
|
444
|
+
save.acknowledged = identity ? { nativeId, identity } : undefined;
|
|
445
|
+
};
|
|
446
|
+
const rememberIdentity = (connection, submitted = false) => {
|
|
447
|
+
const save = identitySave(connection);
|
|
448
|
+
save.submitted ||= submitted;
|
|
449
|
+
const entry = controls?.state.sessions.find(session => session.id === connection.id);
|
|
450
|
+
const nativeId = connection.session.descriptor?.session_id;
|
|
451
|
+
if (!save.submitted || !controls || !entry || attachedIds.has(connection.id) || !nativeId)
|
|
452
|
+
return;
|
|
453
|
+
save.remembered ||= Boolean(entry.identity);
|
|
454
|
+
if (save.remembered)
|
|
455
|
+
return;
|
|
456
|
+
save.again = true;
|
|
457
|
+
if (save.pending)
|
|
458
|
+
return;
|
|
459
|
+
save.pending = (async () => {
|
|
460
|
+
do {
|
|
461
|
+
save.again = false;
|
|
462
|
+
try {
|
|
463
|
+
await controls.remember(connection.id, nativeId);
|
|
464
|
+
identitySaved(connection, nativeId);
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
log.warn(`Could not yet save conversation identity: ${String(error)}`);
|
|
468
|
+
}
|
|
469
|
+
} while (save.again && !save.remembered);
|
|
470
|
+
})().finally(() => { save.pending = undefined; });
|
|
471
|
+
};
|
|
472
|
+
const finishIdentitySave = async (connection, onlySubmitted = false) => {
|
|
473
|
+
const save = identitySave(connection);
|
|
474
|
+
if (onlySubmitted && !save.submitted)
|
|
475
|
+
return;
|
|
476
|
+
if (!controls || attachedIds.has(connection.id))
|
|
477
|
+
return;
|
|
478
|
+
await save.pending;
|
|
479
|
+
// The host may have cleared a binding since its last lifecycle reply.
|
|
480
|
+
// Read it before treating a local acknowledgement as satisfied.
|
|
481
|
+
const refreshed = await controls.refresh().then(() => true, error => {
|
|
482
|
+
log.warn(`Could not refresh chat identity: ${String(error)}`);
|
|
483
|
+
return false;
|
|
484
|
+
});
|
|
485
|
+
const nativeId = connection.session.descriptor?.session_id;
|
|
486
|
+
const identity = controls.state.sessions.find(session => session.id === connection.id)?.identity;
|
|
487
|
+
const acknowledged = save.acknowledged;
|
|
488
|
+
const satisfied = Boolean(refreshed && nativeId && acknowledged
|
|
489
|
+
&& acknowledged.nativeId === nativeId && acknowledged.identity === identity);
|
|
490
|
+
if (!satisfied) {
|
|
491
|
+
save.acknowledged = undefined;
|
|
492
|
+
save.remembered = false;
|
|
493
|
+
}
|
|
494
|
+
if (nativeId && !satisfied)
|
|
495
|
+
await controls.remember(connection.id, nativeId).then(() => {
|
|
496
|
+
identitySaved(connection, nativeId);
|
|
497
|
+
}, error => {
|
|
498
|
+
log.warn(`Could not save conversation identity: ${String(error)}`);
|
|
499
|
+
if (save.submitted && !save.remembered && !save.warned
|
|
500
|
+
&& !controls.state.sessions.find(session => session.id === connection.id)?.identity) {
|
|
501
|
+
save.warned = true;
|
|
502
|
+
void vscode.window.showWarningMessage('Your prompt was submitted, but Chat could not save this conversation for reopening. Keep this chat open; do not send the prompt again.');
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
controlsChanged.fire();
|
|
506
|
+
};
|
|
507
|
+
context.subscriptions.push(frontends.onDidChange(() => {
|
|
508
|
+
for (const connection of frontends.all)
|
|
509
|
+
rememberIdentity(connection);
|
|
510
|
+
}));
|
|
425
511
|
const savedAttachments = new Map(context.workspaceState.get('supercode.externalAttachments', [])
|
|
426
512
|
.map(entry => [entry.connectionId, entry]));
|
|
427
513
|
const restoringAttachments = new Map();
|
|
@@ -1030,6 +1116,8 @@ export async function activate(context) {
|
|
|
1030
1116
|
effective: (entry) => controls && entry.id === controls.state.activeSession ? controls.state.effective ?? null : draftEffective(entry.selection),
|
|
1031
1117
|
bindings: boundId,
|
|
1032
1118
|
watchApprovals: (connection, stream, token) => watchApprovals(connection, stream, token, () => approvalFor(connection.id)),
|
|
1119
|
+
rememberIdentity,
|
|
1120
|
+
finishIdentitySave: (connection) => finishIdentitySave(connection, true),
|
|
1033
1121
|
};
|
|
1034
1122
|
context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.activateSession', async (resource) => { await connectionFor(vscode.Uri.parse(resource)); }));
|
|
1035
1123
|
// A background task the chat named (sayTasks in runTurnInto), stopped through the harness's own control.
|
|
@@ -1343,12 +1431,9 @@ export async function activate(context) {
|
|
|
1343
1431
|
const unwatchTasks = connection.session.onChange(sayTasks);
|
|
1344
1432
|
let completed = false;
|
|
1345
1433
|
try {
|
|
1346
|
-
//
|
|
1347
|
-
//
|
|
1348
|
-
//
|
|
1349
|
-
if (controls && !attachedIds.has(connection.id) && connection.session.descriptor?.session_id) {
|
|
1350
|
-
await controls.remember(connection.id, connection.session.descriptor.session_id);
|
|
1351
|
-
}
|
|
1434
|
+
// A new harness transcript is created by input, so binding its identity
|
|
1435
|
+
// cannot be a prerequisite for that input. Retry the host save as native
|
|
1436
|
+
// events arrive; no save refusal submits input again or aborts its turn.
|
|
1352
1437
|
throwIfCancelled();
|
|
1353
1438
|
await connection.session.runTurn(prompt, (event) => {
|
|
1354
1439
|
// A turn the person stopped is marked stopped by VS Code itself; only an interruption from elsewhere is said.
|
|
@@ -1356,19 +1441,17 @@ export async function activate(context) {
|
|
|
1356
1441
|
return;
|
|
1357
1442
|
writeProjected(stream, projectEvent(event, projection), granted, state);
|
|
1358
1443
|
watch.sweep();
|
|
1359
|
-
|
|
1444
|
+
rememberIdentity(connection);
|
|
1445
|
+
}, { throwIfCancelled, onSubmitted: () => {
|
|
1446
|
+
rememberIdentity(connection, true);
|
|
1447
|
+
} });
|
|
1360
1448
|
completed = true;
|
|
1361
1449
|
}
|
|
1362
1450
|
finally {
|
|
1363
1451
|
unwatchTasks();
|
|
1364
1452
|
watch.dispose();
|
|
1365
1453
|
cancellation.dispose();
|
|
1366
|
-
|
|
1367
|
-
if (connection.session.descriptor?.session_id)
|
|
1368
|
-
await controls.remember(connection.id, connection.session.descriptor.session_id).catch(error => log.warn(`Could not save conversation identity: ${String(error)}`));
|
|
1369
|
-
await controls.refresh().catch(error => log.warn(`Could not refresh chat identity: ${String(error)}`));
|
|
1370
|
-
controlsChanged.fire();
|
|
1371
|
-
}
|
|
1454
|
+
await finishIdentitySave(connection);
|
|
1372
1455
|
}
|
|
1373
1456
|
// The model the turn ran on: what the harness's own replies name (the host watches them), else what the
|
|
1374
1457
|
// runtime reports; and the effort it ran at (host `effective`).
|
|
@@ -2073,6 +2156,9 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2073
2156
|
canRespond: Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve')),
|
|
2074
2157
|
}, projectHistory(canonicalHistory, granted, pendingToolNames));
|
|
2075
2158
|
const live = connection.session.descriptor?.turn_state === 'busy' || connection.session.pendingRequests.size > 0;
|
|
2159
|
+
controls?.rememberIdentity(connection);
|
|
2160
|
+
if (!live)
|
|
2161
|
+
void controls?.finishIdentitySave(connection);
|
|
2076
2162
|
const activeParts = [];
|
|
2077
2163
|
if (live) {
|
|
2078
2164
|
// Standalone requests have no user turn. Give their live response
|
|
@@ -2132,6 +2218,7 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2132
2218
|
canRespond: Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve')),
|
|
2133
2219
|
}), granted, state);
|
|
2134
2220
|
watch?.sweep();
|
|
2221
|
+
controls?.rememberIdentity(connection);
|
|
2135
2222
|
await observeActiveResponse(connection.session, token, (event) => {
|
|
2136
2223
|
writeProjected(stream, projectEvent(event, {
|
|
2137
2224
|
connectionId: connection.id,
|
|
@@ -2139,11 +2226,13 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2139
2226
|
canRespond: Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve')),
|
|
2140
2227
|
}), granted, state);
|
|
2141
2228
|
watch?.sweep();
|
|
2229
|
+
controls?.rememberIdentity(connection);
|
|
2142
2230
|
}, () => { connection.viewReady = true; }, renderedCursor);
|
|
2143
2231
|
}
|
|
2144
2232
|
finally {
|
|
2145
2233
|
connection.viewReady = connection.session.descriptor?.turn_state !== 'busy' && !connection.session.pendingRequests.size;
|
|
2146
2234
|
watch?.dispose();
|
|
2235
|
+
await controls?.finishIdentitySave(connection);
|
|
2147
2236
|
}
|
|
2148
2237
|
}
|
|
2149
2238
|
: undefined,
|
package/dist/session.d.ts
CHANGED
|
@@ -161,10 +161,13 @@ export declare class VscodeFrontendSession {
|
|
|
161
161
|
* Submit one prompt and feed every event of the resulting turn to `onEvent`,
|
|
162
162
|
* resolving when the runtime reports the turn finished. The subscription is
|
|
163
163
|
* registered BEFORE the prompt is sent, so no early delta is lost.
|
|
164
|
+
* `onSubmitted` observes the input RPC's successful return, not transcript
|
|
165
|
+
* persistence or turn completion; it runs once, after any lease retry.
|
|
164
166
|
*/
|
|
165
167
|
runTurn(prompt: string, onEvent: (event: FrontendEvent) => void, options?: {
|
|
166
168
|
onCancel?: (cancel: () => void) => void;
|
|
167
169
|
throwIfCancelled?: () => void;
|
|
170
|
+
onSubmitted?: () => void;
|
|
168
171
|
}): Promise<FrontendEvent | null>;
|
|
169
172
|
private require;
|
|
170
173
|
private notify;
|
package/dist/session.js
CHANGED
|
@@ -428,6 +428,8 @@ export class VscodeFrontendSession {
|
|
|
428
428
|
* Submit one prompt and feed every event of the resulting turn to `onEvent`,
|
|
429
429
|
* resolving when the runtime reports the turn finished. The subscription is
|
|
430
430
|
* registered BEFORE the prompt is sent, so no early delta is lost.
|
|
431
|
+
* `onSubmitted` observes the input RPC's successful return, not transcript
|
|
432
|
+
* persistence or turn completion; it runs once, after any lease retry.
|
|
431
433
|
*/
|
|
432
434
|
async runTurn(prompt, onEvent, options = {}) {
|
|
433
435
|
let settle;
|
|
@@ -480,6 +482,8 @@ export class VscodeFrontendSession {
|
|
|
480
482
|
});
|
|
481
483
|
try {
|
|
482
484
|
await this.submit(prompt, options.throwIfCancelled);
|
|
485
|
+
// Acceptance can precede the harness's first durable transcript record.
|
|
486
|
+
options.onSubmitted?.();
|
|
483
487
|
return await finished;
|
|
484
488
|
}
|
|
485
489
|
finally {
|
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.55",
|
|
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",
|