@volter/supercode-frontend-vscode 0.1.64 → 0.1.65
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 +49 -30
- package/dist/extension.js +131 -26
- package/dist/proposals.d.ts +1 -1
- package/dist/proposals.js +16 -0
- package/dist/view.d.ts +16 -0
- package/dist/view.js +65 -12
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -180,9 +180,10 @@ default is never stored as a choice (a pick equal to the default follows the def
|
|
|
180
180
|
changes), and an Auto approve is never stored for a conversation that cannot answer. Conversations opened before 0.1.36 saved their picker's
|
|
181
181
|
**Ask** when opened and keep it. Auto approve approves the requests the harness would ask you about, including requests to run
|
|
182
182
|
outside its sandbox (Codex's approval requests are its sandbox escalations); the harness's deny
|
|
183
|
-
rules still apply. A request it answers adds no line of its own: the call's row is the record
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
rules still apply. A request it answers adds no line of its own: the call's row is the record, as in Code-OSS's own
|
|
184
|
+
agent host, whose Auto approve answers a harness's call with no reason on its row; a card already shown loses its
|
|
185
|
+
buttons where the host can retire it (`chatConfirmationResolution`), else gets its answer as a line beneath it, and an
|
|
186
|
+
approval it could not answer has its card drawn again for the person to answer. A turn
|
|
186
187
|
re-joined after a page reload (the conversation's active response) is answered the same way. Unsupported modes are refused. Unanswerable
|
|
187
188
|
harnesses show **Harness permissions**. Previously answered requests cannot affect another
|
|
188
189
|
connection. Native command-button fallback limitations are documented below.
|
|
@@ -312,7 +313,9 @@ the frontend does not coerce them into JavaScript's object label.
|
|
|
312
313
|
record this history path. The original Windows approval-row report remains
|
|
313
314
|
unattributed, and native approval behavior is separate.
|
|
314
315
|
|
|
315
|
-
The extension asks for
|
|
316
|
+
The extension asks for eight proposals. Three of them (`chatSessionResponses`, `chatSystemNotification`,
|
|
317
|
+
`chatConfirmationResolution`) exist only in Volter's Code-OSS fork (volter-ai/code-oss, from `cb0c38b5`); a host
|
|
318
|
+
without one drops its name as a proposal that does not exist. A host grants them through
|
|
316
319
|
`product.json#extensionEnabledApiProposals` (a Code-OSS derivative that bundles this extension) or
|
|
317
320
|
`--enable-proposed-api volter-ai-dev.supercode-frontend-vscode`. **Stock VS Code grants none, and
|
|
318
321
|
everything below still works there** — the granted set is read back from the extension host at
|
|
@@ -326,13 +329,18 @@ request), and each part declares its own fallback.
|
|
|
326
329
|
| `chatSessionsProvider` | the runtime's sessions in the Chat view's session picker, each reopening with its history | the participant still attaches; there is no session list |
|
|
327
330
|
| `chatParticipantPrivate` | dynamic participant identity with the harness and provider icon; an interrupted turn's error at warning level | static participant identity; the interruption is an ordinary error |
|
|
328
331
|
| `chatProvider` | native fixed-model identity and provider icon | host new-chat configuration |
|
|
332
|
+
| `chatSessionResponses` | the `ChatResult` a re-joined response resolves with and a saved turn's `result.errorDetails` as the response's error; the question carousel on a re-joined response; a conversation that could not be opened anchored to the system-initiated request "Couldn't open session" (`ChatRequestTurn.systemInitiatedLabel`) | a re-joined or saved response's error as a warning part, `Answer…` buttons on a re-joined response, that conversation's error under the marker request |
|
|
333
|
+
| `chatSystemNotification` | `ChatResponseSystemNotificationPart` for a harness notice, with its icon, live and saved | the notice as a plain line |
|
|
334
|
+
| `chatConfirmationResolution` | `ChatResponseConfirmationPart.isUsed`: an approval card answered elsewhere loses its buttons | one plain line beneath the card says the answer |
|
|
329
335
|
|
|
330
336
|
### Parity with Code-OSS's own chat
|
|
331
337
|
|
|
332
|
-
The measure is the event, not the API part: **does this event look as
|
|
333
|
-
|
|
338
|
+
The measure is the event, not the API part: **does this event look as Code-OSS draws its own equivalent
|
|
339
|
+
event?** The citations are read from Volter's Code-OSS fork at the editor's `FORK.json` pin; the workbench archives
|
|
340
|
+
this product ships are cut from earlier fork commits until they are re-cut at it: `4a7cd45` on macOS arm64, `4782d9a2`
|
|
341
|
+
on Linux x64 and Windows x64. Using a native part for something VS Code never draws is not
|
|
334
342
|
parity. Each row names what Code-OSS itself draws, with its source, and what this frontend draws on the product's
|
|
335
|
-
host (all
|
|
343
|
+
host (all eight proposals granted). **Native**: drawn as Code-OSS draws it. **Fallback**: drawn, but plainer than
|
|
336
344
|
Code-OSS's own (why given). **Gap (API)**: Code-OSS's own drawing is not reachable through the extension API at the
|
|
337
345
|
pin; the row names the fork change that would reach it. **Gap**: reachable, not built. This count is the measure of
|
|
338
346
|
done; a change that moves a row says so here. Paths are under `src/vs/` unless they start with `src/vscode-dts`.
|
|
@@ -350,21 +358,24 @@ done; a change that moves a row says so here. Paths are under `src/vs/` unless t
|
|
|
350
358
|
| A subagent and its calls | the subagent row with its calls grouped under it (`chatSubagentContentPart.ts`) | subagent tool data, `subAgentInvocationId` | native |
|
|
351
359
|
| A hook that blocked or warned | the hook row (`chatHookContentPart.ts`) | `ChatResponseHookPart` | native; **gap (API)** inside a subagent: the part carries no subagent id |
|
|
352
360
|
| An approval | the confirmation card, answered in the running turn (`chatConfirmationContentPart.ts`; the fork's `volter.chat.inTurnApprovalCapability`, `volterHarness/volterChat.ts`) | `ChatResponseConfirmationPart` where the host advertises in-turn cards (this product's host does); command buttons on a stock host, whose confirmation answers with a new request | native; fallback on a stock host |
|
|
353
|
-
| A harness's questions | the question carousel (`chatQuestionCarouselPart.ts`) | the carousel on a turn this window runs
|
|
361
|
+
| A harness's questions | the question carousel (`chatQuestionCarouselPart.ts`), in its turn's response | the carousel, on a turn this window runs and on a re-joined response (`chatSessionResponses`: `mainThreadChatSessions.ts` forwards the answer to the session's stream by its `resolveId`); `Answer…` buttons on a re-joined response on a host without it | native; fallback on a re-joined response without `chatSessionResponses` |
|
|
354
362
|
| A request answered through its own card in this window | the card or carousel shows what was chosen (`chatConfirmationContentPart.ts`, `chatQuestionCarouselPart.ts`); the call's row carries on or ends denied | the same; a stock host's buttons stay, inert, and the status bar says what was sent | native |
|
|
355
|
-
| A request answered any other way (another window, Auto approve turned on after its card showed, a typed reply) | the card retires itself with
|
|
356
|
-
| An approval answered automatically (Auto approve) | the call's row
|
|
363
|
+
| A request answered any other way (another window, Auto approve turned on after its card showed, a typed reply) | the request's card retires itself: the call's row carries on or ends denied (`agentHostSessionHandler.ts` `IChatToolInvocation.confirmWith`); a carousel answered elsewhere is marked used with its answers (`answeredExternally`) | an approval card loses its buttons (`chatConfirmationResolution`: the same `ChatResponseConfirmationPart` pushed with `isUsed`, which `chatModel.ts` matches by its `data`); the call's row carries the outcome. A question carousel stays live, with one plain line beneath it saying the answer | native for an approval card; **gap (API)** for a question carousel: an extension cannot mark a carousel it pushed answered. Fork: let the extension resolve a pushed carousel with the answers given elsewhere (`answeredExternally`) |
|
|
364
|
+
| An approval answered automatically (Auto approve) | the call's row alone: Code-OSS's own agent host answers a harness's call under its Auto approve with `confirmed: NotNeeded` and no reason (`platform/agentHost/node/sessionPermissions.ts` `createToolReadyAction`, after `agentSideEffects.ts` strips the confirmation), so `getApprovalMessageFromReason` (`chatToolPartUtilities.ts`) has nothing to say. Only a client tool the workbench runs itself is "Auto approved by `autoApprove`" (`agentHostSessionHandler.ts` `getClientToolPreApproval`) | the call's row alone; an answer that fails or does not settle draws the approval's card again, as the one still waiting | native |
|
|
357
365
|
| A request this window cannot answer (another client holds it) | no read-only confirmation: another client's call is its row, "Running {0} on another client…", with a cancel action (`stateToProgressAdapter.ts` `toolCallStateToInvocation`) | its title and summary as words, no controls; its answer as a line beneath it | fallback: the extension API cannot draw another client's call |
|
|
358
|
-
| A turn that failed | the response's error (`agentHostSessionHandler.ts` `_getTurnErrorDetails`, `errorDetails`) | the request handler's `ChatResult.errorDetails
|
|
359
|
-
| A turn interrupted from elsewhere | the response's warning-level, expected error (`_getTurnErrorDetails`, `executionInterrupted`) | `errorDetails` with `level: Warning`, `isExpectedError
|
|
366
|
+
| A turn that failed | the response's error (`agentHostSessionHandler.ts` `_getTurnErrorDetails`, `errorDetails`) | the request handler's `ChatResult.errorDetails`; on a re-joined response the `ChatResult` its `activeResponseCallback` resolves with, and in history the saved turn's `result.errorDetails` (`chatSessionResponses`: `extHostChatSessions.ts` passes both on, and `chatServiceImpl.ts` sets the result on the response as it completes); on a host without it, VS Code's warning part with the error's words there | native; fallback on a re-joined response and in history without `chatSessionResponses` |
|
|
367
|
+
| A turn interrupted from elsewhere | the response's warning-level, expected error (`_getTurnErrorDetails`, `executionInterrupted`) | `errorDetails` with `level: Warning`, `isExpectedError`, on the same paths | native; fallback without `chatSessionResponses`, as above |
|
|
360
368
|
| A turn the person stopped | VS Code marks the response stopped | nothing more | native |
|
|
361
369
|
| A request Chat cannot run (no sign-in, no runtime, a refused send) | the request's error | a thrown error, which the workbench writes as the response's error | native |
|
|
362
|
-
| A transport or stream error while a turn may still run (`stream_error`, `transport_error`, `watch_error`, `runtime_disconnected
|
|
370
|
+
| A transport or stream error while a turn may still run (`stream_error`, `transport_error`, `watch_error`, `runtime_disconnected`) | the response's error | VS Code's warning part with the error's words | fallback: the turn may still be running, so it is not ended with an error |
|
|
371
|
+
| A re-joined stream that fails; a conversation that cannot be recovered | the response's error; a session that cannot be loaded is the system-initiated request "Couldn't open session" with that error (`agentHostSessionHandler.ts` `sessionLoadFailed`, `sessionLoadFailedLabel`) | the re-joined response's result carries the error; the conversation's marker request is the system-initiated "Couldn't open session" and its saved response's result carries the error (`chatSessionResponses`); VS Code's warning part under the marker request on a host without it | native; fallback without `chatSessionResponses` |
|
|
363
372
|
| A harness warning (`cache_warning`, `warning`, a usage limit) | the warning part (`chatErrorContentPart.ts`, warning level) | `ChatResponseWarningPart` | native |
|
|
364
|
-
| A harness record that is not the person's words (a background task's notification, live or saved; an interruption record) | a system notification row (`stateToProgressAdapter.ts` `systemNotificationToChatPart`; `claudeReplayMapper.ts` keeps `compact_boundary` and `notification`) | the notice's own words
|
|
373
|
+
| A harness record that is not the person's words (a background task's notification, live or saved; an interruption record) | a system notification row, its icon picked per notification kind with the completion check as default (`stateToProgressAdapter.ts` `systemNotificationToChatPart`; `claudeReplayMapper.ts` keeps `compact_boundary` and `notification`) | `ChatResponseSystemNotificationPart` with the notice's own words (`chatSystemNotification`, converted to `{kind: 'systemNotification'}` in `extHostTypeConverters.ts`), live and saved, once per task (Claude Code reports a finished task both as `system/task_notification` and as the user-role record that wakes the agent; the saved transcript keeps only the latter, a `queued_command` attachment). "Turn interrupted." and a background task that failed or was stopped carry `warning`, by analogy with the icon upstream gives an automatic-approval review that was interrupted: upstream draws neither as a notification of its own. A plain line on a host without it, never a progress part, which Code-OSS hides once any other part follows it (`chatProgressContentPart.ts`) | native; fallback without `chatSystemNotification` |
|
|
365
374
|
| A status line (`status`) | the progress message, hidden once another part follows (`chatProgressContentPart.ts`) | `ChatResponseProgressPart` | native |
|
|
366
|
-
| Input arriving from elsewhere while a turn runs |
|
|
367
|
-
| A
|
|
375
|
+
| Input arriving from elsewhere while a turn runs | the running turn's pending Steering message until the turn takes it in, then nothing: the turn keeps its response and no request turn opens (`agentHostSessionHandler.ts` `_applyRemotePendingMessages` → `syncPendingRequestsFromRemote`; `removePendingRequest` once taken in) | **Additional input** and the words, inside the running response, once the turn takes it in | **gap (runtime)**: the runtime announces no input waiting to be taken in (no frontend event carries it), and the runtime must echo a client-chosen input id on `user_message` and `turn_started`, and return it from `submit` and `steer` (`sdk/frontend/contract.json` carries no turn or input id, so which input started which turn is only a guess by text and order). Then a proposed API with which a session's provider sets and removes its pending Steering messages, mirroring those two calls |
|
|
376
|
+
| A message sent from this window while a turn it follows runs (a re-joined response) | a pending Steering message, which the workbench dispatches to a streamed session's provider at once (`chatServiceImpl.ts`, mid-turn steering); the turn keeps its response | the request handler's own turn, which the runtime refuses while a turn runs | **gap (runtime)**: steering it into the running turn without drawing its runtime echo a second time needs to know which `user_message` is that echo: the runtime must echo a client-chosen input id on `user_message` and `turn_started`, and return it from `submit` and `steer` (`sdk/frontend/contract.json` carries no turn or input id, so which input started which turn is only a guess by text and order) |
|
|
377
|
+
| A turn the runtime starts on its own (input that starts a turn while none runs, a turn after the previous one ended) | a request turn of its own (`agentHostSessionHandler.ts`, a changed active turn id: `startServerRequest`) | drawn inside the response of this window that follows the conversation, which follows to idle (a re-joined response), or only when the conversation reopens | **gap (runtime)**: opening it as a request turn of its own needs to know which input started it: the runtime must echo a client-chosen input id on `user_message` and `turn_started`, and return it from `submit` and `steer` (`sdk/frontend/contract.json` carries no turn or input id, so which input started which turn is only a guess by text and order) |
|
|
378
|
+
| A prompt the runtime's scheduler starts | a system-initiated request turn: a notification row with its label (`chatListRenderer.ts` `renderSystemInitiatedRequest`) | a notice, "Scheduled prompt: …", inside the response this window follows, or nothing until the conversation reopens; the same in saved history. Nothing in this repository produces `scheduled_prompt_started` (it is declared by `agent-runtime-host.mjs` `displayKinds` and read by `frontend-transcript.mjs` and the browser client), so its payload is not known; the prompt is read from `prompt`, `name` or `message` | **gap (runtime)**: a producer, and the turn attribution above, so that its turn opens as its own system-initiated request without also drawing the runtime's `user_message` for it |
|
|
368
379
|
| A Codex diff | file edits with Keep/Undo and the multi-file diff (`chatTextEditContentPart.ts`, `chatMultiDiffContentPart.ts`) | `codeblockUri` with a fenced diff, plus one reference per file | **gap (known)**: the harness has already written the files and no event carries the before/after the editor needs (see *Why `diff_updated` is not an edit part*) |
|
|
369
380
|
| An MCP call | the MCP input/output row | the simple input/output row | fallback: its result arrives as text |
|
|
370
381
|
| Token usage | the context-window indicator (`stream.usage`) | not shown | **gap** |
|
|
@@ -373,11 +384,16 @@ done; a change that moves a row says so here. Paths are under `src/vs/` unless t
|
|
|
373
384
|
| History past its bound | left out without a word; a turn without its request gets "Message content could not be retrieved" (`claudeReplayMapper.ts` `missingPromptPlaceholder`) | the same | native |
|
|
374
385
|
| A call waiting on approval in re-joined history | its row, with its card on the active response | the same | native |
|
|
375
386
|
|
|
376
|
-
Count, by row (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
input from elsewhere
|
|
380
|
-
|
|
387
|
+
Count, by row (36), on this product's host (Volter's Code-OSS fork at the editor's `FORK.json` pin, with all eight
|
|
388
|
+
proposals granted; a stock host draws the fallbacks above): 26 native, of which hooks are an API gap inside a
|
|
389
|
+
subagent and a request answered elsewhere an API gap for a question carousel; 4 fallback; 4 gaps that wait on the
|
|
390
|
+
runtime's turn attribution (input from elsewhere while a turn runs, a message this window sends into a turn it
|
|
391
|
+
follows, a turn the runtime starts on its own, a scheduled prompt, which also has no producer yet); 1 known gap
|
|
392
|
+
(native edits); 1 gap not built (`stream.usage`). The rows marked native for a harness's questions on a re-joined
|
|
393
|
+
response, harness records, a failed or interrupted turn on a re-joined or saved response, a conversation that cannot
|
|
394
|
+
be recovered, and a card answered elsewhere rest on the fork's proposals `chatSessionResponses`,
|
|
395
|
+
`chatSystemNotification` and `chatConfirmationResolution`; an automatic approval is native because Code-OSS's own
|
|
396
|
+
agent host draws that call's row alone.
|
|
381
397
|
|
|
382
398
|
Parts the extension API offers that no harness event this frontend receives carries, so not used:
|
|
383
399
|
`ChatResponseAnchorPart`, `ChatResponseFileTreePart`, `ChatResponseMarkdownWithVulnerabilitiesPart`,
|
|
@@ -417,21 +433,21 @@ it needs and the fallback it degrades to. Each arm names the producer it reads.
|
|
|
417
433
|
| Event kind | Producer | Rendered as |
|
|
418
434
|
|---|---|---|
|
|
419
435
|
| `text_delta` | `crates/runtime/src/event.rs` `AgentEvent::TextDelta` | `response.markdown` (streamed) |
|
|
420
|
-
| `user_message` | `sdk/interchange/agent-runtime-host.mjs` accepted-turn projection; `runtime-native-projection.mjs` `claudeBlocks` for Claude's own user-role records (`source: harness`) | the request turn in history; dropped live (VS Code already shows it); a harness record shows its `notice` as a plain line, or nothing |
|
|
436
|
+
| `user_message` | `sdk/interchange/agent-runtime-host.mjs` accepted-turn projection; `runtime-native-projection.mjs` `claudeBlocks` for Claude's own user-role records (`source: harness`) | the request turn in history; dropped live before `turn_started` (VS Code already shows it); after it, **Additional input** inside the response; a harness record shows its `notice` as a system notification row (`chatSystemNotification`) → a plain line, or nothing |
|
|
421
437
|
| `turn_started` / `turn_completed` / `turn_succeeded` | `submit_claimed`; `AgentEvent::TurnCompleted` | lifecycle only — the handler resolves, nothing renders |
|
|
422
|
-
| `turn_interrupted` | `submit_claimed` | the
|
|
423
|
-
| `turn_failed` | `submit_claimed` | the
|
|
438
|
+
| `turn_interrupted` | `submit_claimed` | the response's result: `errorDetails` at warning level (a re-joined or saved response needs `chatSessionResponses`); else `ChatResponseWarningPart` → its words |
|
|
439
|
+
| `turn_failed` | `submit_claimed` | the response's result: `errorDetails` (a re-joined or saved response needs `chatSessionResponses`); else `ChatResponseWarningPart` → blockquote |
|
|
424
440
|
| `tool_call_started` / `tool_call_completed` | `AgentEvent::ToolCallStarted` / `ToolCallCompleted` | one `ChatToolInvocationPart` per call, updated in place with its result (`enablePartialUpdate`) → a progress line plus a labeled Markdown result |
|
|
425
441
|
| `diff_updated` | `crates/codex-frontend/src/codex_app_server_v0_144.rs` (stock Codex `turn/diff/updated`) | `codeblockUri` + a fenced `diff` block, plus one stable `reference` per file the diff names → the fenced block alone |
|
|
426
442
|
| `request` | `sdk/interchange/frontend-request-bridge.mjs` request publication | `ChatResponseConfirmationPart` where the host advertises in-turn cards, the question carousel for questions; else a titled markdown block plus one **command button per answer**. Read-only (title and summary) for a request this window cannot answer |
|
|
427
|
-
| `request_resolved` | `FrontendRequestBroker::respond` | nothing for a card answered through itself in this window; else one plain line beneath the card naming the answer |
|
|
443
|
+
| `request_resolved` | `FrontendRequestBroker::respond` | nothing for a card answered through itself in this window; a native approval card answered elsewhere loses its buttons (`chatConfirmationResolution`); else one plain line beneath the card naming the answer |
|
|
428
444
|
| `cache_warning` | `AgentEvent::CacheWarning` | `ChatResponseWarningPart` → blockquote |
|
|
429
445
|
| `hook` | `runtime-native-projection.mjs` from Claude Code's failed `hook_response`, its blocked-prompt `informational` record, and a tool result in its own `PreToolUse:<tool> hook error:` words | `ChatResponseHookPart` ("Blocked by" / "Warning from" the hook) → blockquote |
|
|
430
446
|
| `background_tasks` | `runtime-native-projection.mjs` from Claude Code's `background_tasks_changed` | nothing: a background command is its call's row, as in Code-OSS's own Claude host |
|
|
431
447
|
| any event with `parent_id` | `runtime-native-projection.mjs` from Claude Code's `parent_tool_use_id` | a subagent's calls are grouped under its call; its own text, reasoning and input are not shown |
|
|
432
448
|
| `usage` | `AgentEvent::Usage` | nothing (see `stream.usage` in the parity table) |
|
|
433
449
|
| `background_output` | `sdk/interchange/agent-background.mjs` `status` | nothing: the status call's own row carries the output |
|
|
434
|
-
| `scheduled_prompt_started` / `_deferred` / `_completed` | declared by `agent-runtime-host.mjs` `displayKinds`; no producer here | started: a
|
|
450
|
+
| `scheduled_prompt_started` / `_deferred` / `_completed` | declared by `agent-runtime-host.mjs` `displayKinds`; no producer here | started: a notice, "Scheduled prompt: …"; deferred and completed: lifecycle only |
|
|
435
451
|
| `scheduler_error` | same | `ChatResponseWarningPart` with its words → blockquote |
|
|
436
452
|
| `reasoning` / `thinking` | no producer in this repository; read because `sdk/interchange/frontend-transcript.mjs` reads them | `ChatResponseThinkingProgressPart` → blockquote |
|
|
437
453
|
| `stream_error` / `transport_error` / `watch_error` / `runtime_disconnected` | `crates/opencode-frontend/src/server.rs`; `crates/interchange/src/watch.rs`; synthesized by a frontend (`sdk/interchange/frontend-app.mjs`); `stream_error` has no producer here | `ChatResponseWarningPart` with its words → blockquote |
|
|
@@ -467,11 +483,13 @@ status bar says what was sent. An elicitation that carries a `requested_schema`
|
|
|
467
483
|
can also be answered by typing.
|
|
468
484
|
|
|
469
485
|
**A harness's questions are VS Code's question carousel.** Why the carousel once swallowed its answer, read
|
|
470
|
-
from
|
|
486
|
+
from Code-OSS at `4a7cd45`, the fork commit the macOS arm64 archive is still cut from (Linux and Windows: `4782d9a2`): `questionCarousel` resolves through the resolver map of the stream it
|
|
471
487
|
was called on, and `$handleQuestionCarouselAnswer` reads only the participant's map
|
|
472
488
|
(`extHostChatAgents2.ts`). A session's *re-joined* response (`activeResponseCallback`) gets a stream with a
|
|
473
|
-
map of its own (`extHostChatSessions.ts`) that no answer
|
|
474
|
-
|
|
489
|
+
map of its own (`extHostChatSessions.ts`) that no answer reached at that pin. The fork's `chatSessionResponses`
|
|
490
|
+
routes a carousel answer to a session's stream by its `resolveId` (`mainThreadChatSessions.ts` →
|
|
491
|
+
`$handleQuestionCarouselAnswer`), so on the product's host the carousel is used on a re-joined response too;
|
|
492
|
+
a host without it keeps the buttons above on a re-joined response. The design:
|
|
475
493
|
|
|
476
494
|
- *The question reaches Chat as a request.* Supercode's own `ask_user` already asks through an `elicitation`
|
|
477
495
|
whose `requested_schema.properties.qN` carry `title`, `description`, `x-options` (`[{label, description}]`)
|
|
@@ -486,7 +504,8 @@ turn this window's request handler is running; a re-joined response keeps the bu
|
|
|
486
504
|
alone as `x-question`, and `x-options`) is one `ChatQuestion` per property: single or multi select with
|
|
487
505
|
free text allowed, or text when it has no options; Skip is allowed. Any other form (an MCP server's own
|
|
488
506
|
schema, with its `enum`, `format` or `required`) keeps the `Answer…` door, which honours them. Without the
|
|
489
|
-
carousel (no `chatParticipantAdditions`, or a re-joined response
|
|
507
|
+
carousel (no `chatParticipantAdditions`, or a re-joined response on a host without `chatSessionResponses`) it is
|
|
508
|
+
the `Answer…` button.
|
|
490
509
|
- *The answer reaches the harness in its own format.* The carousel's answers become the elicitation's
|
|
491
510
|
`{action:'accept', content:{qN: label | [labels] | text}}`, or `decline` when skipped or submitted with
|
|
492
511
|
nothing chosen; a stopped turn sends nothing (Stop cancels its requests). A reply typed into Chat instead
|
package/dist/extension.js
CHANGED
|
@@ -10,7 +10,7 @@ import { createFrontendClient } from './client.js';
|
|
|
10
10
|
import { readCredentialFile } from './credential.js';
|
|
11
11
|
import { grantedProposals, PROPOSAL_EFFECTS, REQUESTED_PROPOSALS } from './proposals.js';
|
|
12
12
|
import { observeActiveResponse, sessionStatus } from './native-session.js';
|
|
13
|
-
import { RequestNotPendingError, VscodeFrontendSession } from './session.js';
|
|
13
|
+
import { RequestNotPendingError, TURN_TERMINAL_KINDS, VscodeFrontendSession } from './session.js';
|
|
14
14
|
import { acceptedDecisions, opaqueText, projectEvent, projectPendingRequests, projectHistory, projectReplayTurns, HISTORY_MARKER_PROMPT, PlanTracker, resolveParts, resolutionSentence, turnOutcome, typedAnswer, } from './view.js';
|
|
15
15
|
const SESSION_SCHEME = 'supercode';
|
|
16
16
|
/** The harnesses whose model and reasoning effort a new chat can set (the host passes them to the CLI). */
|
|
@@ -204,6 +204,10 @@ class SupercodeFrontends {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
let nativeInTurnApprovalCards = false;
|
|
207
|
+
/** The host draws a harness notice as its system notification row (`chatSystemNotification`). */
|
|
208
|
+
let systemNotifications = false;
|
|
209
|
+
/** The host retires a confirmation the extension marks answered (`chatConfirmationResolution`). */
|
|
210
|
+
let confirmationResolution = false;
|
|
207
211
|
/** The output channel, for records that are not conversation (`unshown` parts). */
|
|
208
212
|
let outputLog;
|
|
209
213
|
/** Each such record once, however often a history is rebuilt from the same events. */
|
|
@@ -225,6 +229,18 @@ const drew = (stream, requestId) => {
|
|
|
225
229
|
const drawn = drawnRequests.get(stream) ?? new Set();
|
|
226
230
|
drawnRequests.set(stream, drawn.add(requestId));
|
|
227
231
|
};
|
|
232
|
+
/** The confirmation cards each response stream drew natively, so one answered elsewhere can be retired. */
|
|
233
|
+
const drawnCards = new WeakMap();
|
|
234
|
+
/** Retires a native card drawn in `stream`, as one answered through itself is; false where the host cannot. */
|
|
235
|
+
function retireCard(stream, requestId) {
|
|
236
|
+
const card = confirmationResolution ? drawnCards.get(stream)?.get(requestId) : undefined;
|
|
237
|
+
if (!card)
|
|
238
|
+
return false;
|
|
239
|
+
const retired = new vscode.ChatResponseConfirmationPart(card.title, card.message, card.data, card.buttons);
|
|
240
|
+
retired.isUsed = true;
|
|
241
|
+
stream.push(retired);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
228
244
|
/**
|
|
229
245
|
* A harness's questions as VS Code's own carousel, inside the running turn; the person's answers are the
|
|
230
246
|
* elicitation's `{action:'accept', content:{<key>: label | [labels] | text}}`, and Skip is a decline. The answer
|
|
@@ -337,6 +353,12 @@ function writePart(stream, part, state) {
|
|
|
337
353
|
case 'progress':
|
|
338
354
|
stream.progress(part.value);
|
|
339
355
|
return;
|
|
356
|
+
case 'notice':
|
|
357
|
+
if (systemNotifications)
|
|
358
|
+
stream.push(new vscode.ChatResponseSystemNotificationPart(part.value, part.icon ? new vscode.ThemeIcon(part.icon) : undefined));
|
|
359
|
+
else
|
|
360
|
+
stream.markdown(`\n\n${part.value}\n\n`);
|
|
361
|
+
return;
|
|
340
362
|
case 'warning':
|
|
341
363
|
stream.warning(part.value);
|
|
342
364
|
return;
|
|
@@ -370,6 +392,9 @@ function writePart(stream, part, state) {
|
|
|
370
392
|
case 'resolution':
|
|
371
393
|
if (!drawnRequests.get(stream)?.has(part.requestId) || answeredByCard.has(cardKey(part.connectionId, part.requestId)))
|
|
372
394
|
return;
|
|
395
|
+
// A native card answered elsewhere retires itself, as Code-OSS's does; the call's row carries the outcome.
|
|
396
|
+
if (retireCard(stream, part.requestId))
|
|
397
|
+
return;
|
|
373
398
|
stream.markdown(`\n\n${part.value}\n\n`);
|
|
374
399
|
return;
|
|
375
400
|
case 'unshown':
|
|
@@ -380,9 +405,10 @@ function writePart(stream, part, state) {
|
|
|
380
405
|
return;
|
|
381
406
|
drew(stream, part.requestId);
|
|
382
407
|
if (part.approval && nativeInTurnApprovalCards && part.answers.length > 0) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
408
|
+
const card = { title: part.title, message: part.message, data: { kind: 'supercode.inTurnApproval', version: 1, answers: part.answers }, buttons: part.answers.map(answer => answer.label) };
|
|
409
|
+
stream.confirmation(card.title, card.message, card.data, card.buttons);
|
|
410
|
+
const cards = drawnCards.get(stream) ?? new Map();
|
|
411
|
+
drawnCards.set(stream, cards.set(part.requestId, card));
|
|
386
412
|
return;
|
|
387
413
|
}
|
|
388
414
|
// Stable API on purpose: a command button runs INSIDE the live turn,
|
|
@@ -472,6 +498,8 @@ export async function activate(context) {
|
|
|
472
498
|
// description, so this is the grant, not the request.
|
|
473
499
|
const granted = grantedProposals(context.extension.packageJSON);
|
|
474
500
|
log.info(`Volter Harness: proposals requested ${REQUESTED_PROPOSALS.join(', ')}; granted ${granted.size ? [...granted].join(', ') : 'none (stable chat parts only)'}`);
|
|
501
|
+
systemNotifications = granted.has('chatSystemNotification');
|
|
502
|
+
confirmationResolution = granted.has('chatConfirmationResolution');
|
|
475
503
|
// Explicit workbench support is required: stock confirmation clicks send
|
|
476
504
|
// a new prompt. A missing capability keeps the stable in-turn buttons.
|
|
477
505
|
if (granted.has('chatParticipantAdditions')) {
|
|
@@ -1608,8 +1636,9 @@ export async function activate(context) {
|
|
|
1608
1636
|
void vscode.window.showWarningMessage(`${connection.label}: ${label} is waiting for approval. Open its chat to answer it.`);
|
|
1609
1637
|
}
|
|
1610
1638
|
};
|
|
1611
|
-
// An approval answered automatically adds no line of its own
|
|
1612
|
-
//
|
|
1639
|
+
// An approval answered automatically adds no line of its own: Code-OSS's own agent host draws a call its Auto
|
|
1640
|
+
// approve answered as its plain row (`sessionPermissions.ts` `createToolReadyAction`, `confirmed: NotNeeded`).
|
|
1641
|
+
// A card it answered after the card was shown gets its resolution.
|
|
1613
1642
|
const unsettled = setTimeout(() => {
|
|
1614
1643
|
log.warn(`Auto approve has had no answer for ${label} for ${AUTO_ANSWER_SETTLE_MS / 1000} s.`);
|
|
1615
1644
|
showCard();
|
|
@@ -2259,6 +2288,12 @@ function historyParts(parts, granted) {
|
|
|
2259
2288
|
out.push(hookPart(part));
|
|
2260
2289
|
continue;
|
|
2261
2290
|
}
|
|
2291
|
+
// A harness notice is Code-OSS's system notification row in history too, where the host has it.
|
|
2292
|
+
if (part.part === 'notice' && granted.has('chatSystemNotification')) {
|
|
2293
|
+
flush();
|
|
2294
|
+
out.push(new vscode.ChatResponseSystemNotificationPart(part.value, part.icon ? new vscode.ThemeIcon(part.icon) : undefined));
|
|
2295
|
+
continue;
|
|
2296
|
+
}
|
|
2262
2297
|
// A warning and the agent's reasoning are VS Code's own parts in history too, where the host granted them.
|
|
2263
2298
|
if ((part.part === 'warning' || part.part === 'thinking') && granted.has('chatParticipantAdditions')) {
|
|
2264
2299
|
flush();
|
|
@@ -2272,10 +2307,29 @@ function historyParts(parts, granted) {
|
|
|
2272
2307
|
flush();
|
|
2273
2308
|
return out.length ? out : [new vscode.ChatResponseMarkdownPart('')];
|
|
2274
2309
|
}
|
|
2275
|
-
/**
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2310
|
+
/** How a turn that did not succeed ends its response, as VS Code's agent host ends one (`_getTurnErrorDetails`). */
|
|
2311
|
+
function outcomeErrorDetails(outcome, granted) {
|
|
2312
|
+
// (an interruption is the expected, warning-level error: `isExpectedError` and `level` are
|
|
2313
|
+
// `chatParticipantPrivate`'s, which the ext host checks for `isExpectedError`, so a host without it gets the message)
|
|
2314
|
+
return outcome.interrupted && granted.has('chatParticipantPrivate')
|
|
2315
|
+
? { message: outcome.message, isExpectedError: true, level: 1 /* ChatErrorLevel.Warning */ }
|
|
2316
|
+
: { message: outcome.message };
|
|
2317
|
+
}
|
|
2318
|
+
/** A conversation that failed, as a history response shows it: the response's own error where the host keeps a saved
|
|
2319
|
+
* response's result (`chatSessionResponses`), as Code-OSS shows a session that could not be loaded; else VS Code's
|
|
2320
|
+
* warning part where the host granted it, else the words alone. */
|
|
2321
|
+
function failureResponse(message, granted) {
|
|
2322
|
+
if (granted.has('chatSessionResponses'))
|
|
2323
|
+
return { parts: [], result: { errorDetails: { message } } };
|
|
2324
|
+
return { parts: [granted.has('chatParticipantAdditions') ? new vscode.ChatResponseWarningPart(message) : new vscode.ChatResponseMarkdownPart(message)], result: {} };
|
|
2325
|
+
}
|
|
2326
|
+
/** The request a failure's response answers: the marker turn, which Code-OSS anchors as the system-initiated request
|
|
2327
|
+
* "Couldn't open session" (`agentHostSessionHandler.ts` `sessionLoadFailedLabel`) where the host keeps that label. */
|
|
2328
|
+
function failureRequest(turns, participant, granted) {
|
|
2329
|
+
const request = new turns.ChatRequestTurn(HISTORY_MARKER_PROMPT, undefined, [], participant, []);
|
|
2330
|
+
if (granted.has('chatSessionResponses'))
|
|
2331
|
+
request.systemInitiatedLabel = "Couldn't open session";
|
|
2332
|
+
return request;
|
|
2279
2333
|
}
|
|
2280
2334
|
/**
|
|
2281
2335
|
* One text part as the markdown a HISTORY turn carries (tool calls are drawn
|
|
@@ -2285,6 +2339,7 @@ function historyText(part) {
|
|
|
2285
2339
|
switch (part.part) {
|
|
2286
2340
|
case 'markdown':
|
|
2287
2341
|
case 'progress':
|
|
2342
|
+
case 'notice':
|
|
2288
2343
|
return part.value;
|
|
2289
2344
|
case 'warning':
|
|
2290
2345
|
return `> ⚠ ${part.value}`;
|
|
@@ -2424,10 +2479,11 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2424
2479
|
failedResources.set(resource.authority, message);
|
|
2425
2480
|
refreshItems();
|
|
2426
2481
|
}
|
|
2482
|
+
const failure = failureResponse(message, granted);
|
|
2427
2483
|
return {
|
|
2428
2484
|
history: [
|
|
2429
|
-
|
|
2430
|
-
new turns.ChatResponseTurn(
|
|
2485
|
+
failureRequest(turns, 'supercode', granted),
|
|
2486
|
+
new turns.ChatResponseTurn(failure.parts, failure.result, 'supercode'),
|
|
2431
2487
|
],
|
|
2432
2488
|
requestHandler: boundHandler,
|
|
2433
2489
|
};
|
|
@@ -2518,6 +2574,7 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2518
2574
|
if (!live)
|
|
2519
2575
|
void controls?.finishIdentitySave(connection);
|
|
2520
2576
|
const activeParts = [];
|
|
2577
|
+
let heldFailure;
|
|
2521
2578
|
if (live) {
|
|
2522
2579
|
// Standalone requests have no user turn. Give their live response
|
|
2523
2580
|
// an explicit runtime marker instead of reopening an old answer.
|
|
@@ -2538,6 +2595,8 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2538
2595
|
// responses can be stale; their command buttons are disabled.
|
|
2539
2596
|
const last = projected.at(-1);
|
|
2540
2597
|
if (last?.role === 'assistant') {
|
|
2598
|
+
// (a failure that ended that turn stays held for the live response, as it was in history)
|
|
2599
|
+
heldFailure = last.errorEvent;
|
|
2541
2600
|
activeParts.push(...(projected.pop()?.parts ?? []).map(part => part.part === 'answers' || part.part === 'questions'
|
|
2542
2601
|
? { part: 'markdown', value: `**${(part.part === 'questions' ? part.fallback : part).title}**\n\n${(part.part === 'questions' ? part.fallback : part).message}` }
|
|
2543
2602
|
// (its card is words here, so its answer is said beneath it)
|
|
@@ -2552,12 +2611,14 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2552
2611
|
history.push(new turns.ChatRequestTurn(turn.prompt ?? '', undefined, [], authorId, []));
|
|
2553
2612
|
continue;
|
|
2554
2613
|
}
|
|
2555
|
-
|
|
2614
|
+
// (a turn that failed keeps its error as the saved response's own, where the host shows one)
|
|
2615
|
+
history.push(new turns.ChatResponseTurn(historyParts(oneRowPerToolCall(turn.parts ?? []), granted), turn.error ? { errorDetails: outcomeErrorDetails(turn.error, granted) } : {}, authorId));
|
|
2556
2616
|
}
|
|
2557
2617
|
if (connection.session.failure) {
|
|
2558
2618
|
// An explicit runtime marker pairs this notice without presenting
|
|
2559
2619
|
// it as a person's prompt. The native busy state stays unchanged.
|
|
2560
|
-
|
|
2620
|
+
const failure = failureResponse(opaqueText(connection.session.failure.message, 2048), granted);
|
|
2621
|
+
history.push(failureRequest(turns, authorId, granted), new turns.ChatResponseTurn(failure.parts, failure.result, authorId));
|
|
2561
2622
|
}
|
|
2562
2623
|
const renderedCursor = connection.session.cursor;
|
|
2563
2624
|
const pendingAtCursor = [...connection.session.pendingRequests.values()];
|
|
@@ -2574,8 +2635,31 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2574
2635
|
// answered here under the conversation's mode (t_39dac582): Auto approve answers the cards
|
|
2575
2636
|
// already waiting and later ones; Ask leaves them for the person, as before.
|
|
2576
2637
|
const watch = controls && !controls.attached(connection) ? controls.watchApprovals(connection, stream, token) : undefined;
|
|
2638
|
+
// Where the host keeps a re-joined response's result and answers its questions (`chatSessionResponses`),
|
|
2639
|
+
// how the turn it ends on did not succeed is that result, as on a turn this window runs, and its
|
|
2640
|
+
// questions are the carousel. It follows to idle, as it always did, so an earlier turn's failure it
|
|
2641
|
+
// passes is still drawn, as its warning part.
|
|
2642
|
+
const results = granted.has('chatSessionResponses');
|
|
2643
|
+
let failure = results ? heldFailure : undefined;
|
|
2644
|
+
let result;
|
|
2645
|
+
// Draws the held failure where it stood, once anything follows it, as history draws one.
|
|
2646
|
+
let release = () => { };
|
|
2577
2647
|
try {
|
|
2578
|
-
const state = { toolInputs: new Map(), toolNames: new Map(), toolTitles: new Map(), toolCommands: new Map(), toolKinds: new Map(), quiet: watch?.answers
|
|
2648
|
+
const state = { toolInputs: new Map(), toolNames: new Map(), toolTitles: new Map(), toolCommands: new Map(), toolKinds: new Map(), quiet: watch?.answers,
|
|
2649
|
+
...(results ? { carousel: true, pending: (requestId) => connection.session.pendingRequests.has(requestId), cancelled: () => token.isCancellationRequested } : {}) };
|
|
2650
|
+
const project = (event) => projectEvent(event, {
|
|
2651
|
+
connectionId: connection.id,
|
|
2652
|
+
liveUserMessages: true,
|
|
2653
|
+
plan,
|
|
2654
|
+
notices,
|
|
2655
|
+
pendingRequest: (id) => connection.session.pendingRequests.get(id) ?? connection.session.resolvedRequests.get(id),
|
|
2656
|
+
canRespond: Boolean(connection.session.descriptor?.actions.respond && connection.session.permissions.has('approve')),
|
|
2657
|
+
});
|
|
2658
|
+
release = () => {
|
|
2659
|
+
if (failure)
|
|
2660
|
+
writeProjected(stream, project(failure), granted, state);
|
|
2661
|
+
failure = undefined;
|
|
2662
|
+
};
|
|
2579
2663
|
for (const part of oneRowPerToolCall(activeParts))
|
|
2580
2664
|
writePart(stream, part, state);
|
|
2581
2665
|
writeProjected(stream, projectPendingRequests(pendingAtCursor.filter(request => connection.session.pendingRequests.has(request.id)), {
|
|
@@ -2585,25 +2669,45 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2585
2669
|
watch?.sweep();
|
|
2586
2670
|
controls?.rememberIdentity(connection);
|
|
2587
2671
|
await observeActiveResponse(connection.session, token, (event) => {
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2672
|
+
// (a failure the next turn follows is that turn's past: drawn where it stood as soon as the next
|
|
2673
|
+
// turn starts or anything is drawn after it, so neither a later Stop nor a disconnect makes it
|
|
2674
|
+
// this response's error)
|
|
2675
|
+
const terminal = TURN_TERMINAL_KINDS.includes(event.kind);
|
|
2676
|
+
if (event.kind === 'turn_started' || terminal)
|
|
2677
|
+
release();
|
|
2678
|
+
if (results && terminal) {
|
|
2679
|
+
failure = turnOutcome(event) && !token.isCancellationRequested ? event : undefined;
|
|
2680
|
+
}
|
|
2681
|
+
else {
|
|
2682
|
+
const projected = project(event);
|
|
2683
|
+
if (resolveParts(projected, granted).some(part => part.part !== 'unshown'))
|
|
2684
|
+
release();
|
|
2685
|
+
writeProjected(stream, projected, granted, state);
|
|
2686
|
+
}
|
|
2596
2687
|
watch?.sweep();
|
|
2597
2688
|
controls?.rememberIdentity(connection);
|
|
2598
2689
|
}, () => { connection.viewReady = true; }, renderedCursor);
|
|
2690
|
+
const outcome = failure && turnOutcome(failure);
|
|
2691
|
+
if (outcome)
|
|
2692
|
+
result = { errorDetails: outcomeErrorDetails(outcome, granted) };
|
|
2599
2693
|
}
|
|
2600
2694
|
catch (error) {
|
|
2601
2695
|
if (token.isCancellationRequested || error instanceof vscode.CancellationError)
|
|
2602
2696
|
throw error;
|
|
2603
|
-
// The response's own error is what VS Code shows
|
|
2604
|
-
//
|
|
2697
|
+
// The response's own error is what VS Code shows; a host that does not keep a re-joined
|
|
2698
|
+
// response's result gets the warning part.
|
|
2605
2699
|
const message = opaqueText(String(error?.message ?? error), 2048);
|
|
2606
|
-
|
|
2700
|
+
// (a failure held when the connection drops is drawn; the drop is the result)
|
|
2701
|
+
if (results) {
|
|
2702
|
+
try {
|
|
2703
|
+
release();
|
|
2704
|
+
}
|
|
2705
|
+
catch {
|
|
2706
|
+
// (a stream that refuses a part has closed)
|
|
2707
|
+
}
|
|
2708
|
+
result = { errorDetails: { message } };
|
|
2709
|
+
}
|
|
2710
|
+
else if (granted.has('chatParticipantAdditions'))
|
|
2607
2711
|
stream.warning(message);
|
|
2608
2712
|
else
|
|
2609
2713
|
stream.markdown(`\n\n${message}\n\n`);
|
|
@@ -2613,6 +2717,7 @@ function registerSessions(context, frontends, participant, granted, handler, his
|
|
|
2613
2717
|
watch?.dispose();
|
|
2614
2718
|
await controls?.finishIdentitySave(connection);
|
|
2615
2719
|
}
|
|
2720
|
+
return result;
|
|
2616
2721
|
}
|
|
2617
2722
|
: undefined,
|
|
2618
2723
|
};
|
package/dist/proposals.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* unconditionally, so a `typeof vscode.ChatResponseConfirmationPart` probe
|
|
14
14
|
* cannot answer this question — only the description can.
|
|
15
15
|
*/
|
|
16
|
-
export declare const REQUESTED_PROPOSALS: readonly ["defaultChatParticipant", "chatParticipantPrivate", "chatParticipantAdditions", "chatSessionsProvider", "chatProvider"];
|
|
16
|
+
export declare const REQUESTED_PROPOSALS: readonly ["defaultChatParticipant", "chatParticipantPrivate", "chatParticipantAdditions", "chatSessionsProvider", "chatProvider", "chatSessionResponses", "chatSystemNotification", "chatConfirmationResolution"];
|
|
17
17
|
export type ProposalName = (typeof REQUESTED_PROPOSALS)[number];
|
|
18
18
|
/** What each requested proposal buys, and what this frontend does without it. */
|
|
19
19
|
export declare const PROPOSAL_EFFECTS: Readonly<Record<ProposalName, {
|
package/dist/proposals.js
CHANGED
|
@@ -19,6 +19,10 @@ export const REQUESTED_PROPOSALS = Object.freeze([
|
|
|
19
19
|
'chatParticipantAdditions',
|
|
20
20
|
'chatSessionsProvider',
|
|
21
21
|
'chatProvider',
|
|
22
|
+
// Volter's Code-OSS fork (volter-ai/code-oss); a host without them drops the names ("DOES NOT EXIST").
|
|
23
|
+
'chatSessionResponses',
|
|
24
|
+
'chatSystemNotification',
|
|
25
|
+
'chatConfirmationResolution',
|
|
22
26
|
]);
|
|
23
27
|
/** What each requested proposal buys, and what this frontend does without it. */
|
|
24
28
|
export const PROPOSAL_EFFECTS = Object.freeze({
|
|
@@ -39,6 +43,18 @@ export const PROPOSAL_EFFECTS = Object.freeze({
|
|
|
39
43
|
grants: 'the runtime’s sessions in the Chat view’s session picker, each reopening with its history',
|
|
40
44
|
without: 'the participant still attaches; there is no session list',
|
|
41
45
|
},
|
|
46
|
+
chatSessionResponses: {
|
|
47
|
+
grants: 'a re-joined or saved response ending with its error; questions on a re-joined response as the carousel; a conversation that could not be opened anchored as Code-OSS anchors one',
|
|
48
|
+
without: 'a re-joined or saved response’s error as a warning part; questions there as buttons; that conversation’s error under the marker request',
|
|
49
|
+
},
|
|
50
|
+
chatSystemNotification: {
|
|
51
|
+
grants: 'a harness notice (a background task finishing, an interruption record) as the system notification row',
|
|
52
|
+
without: 'the notice as a plain line',
|
|
53
|
+
},
|
|
54
|
+
chatConfirmationResolution: {
|
|
55
|
+
grants: 'an approval card answered elsewhere losing its buttons',
|
|
56
|
+
without: 'the card stays and one plain line beneath it says the answer',
|
|
57
|
+
},
|
|
42
58
|
});
|
|
43
59
|
/** Read the proposals the HOST granted this extension, never the ones it asked for. */
|
|
44
60
|
export function grantedProposals(packageJSON) {
|
package/dist/view.d.ts
CHANGED
|
@@ -34,6 +34,14 @@ export type ChatPart = {
|
|
|
34
34
|
} | {
|
|
35
35
|
part: 'progress';
|
|
36
36
|
value: string;
|
|
37
|
+
}
|
|
38
|
+
/** A record of the harness that is neither the agent's words nor the person's (a background task finishing, an
|
|
39
|
+
* interruption record, a scheduled prompt's start): Code-OSS's system notification row where the host has it
|
|
40
|
+
* (`chatSystemNotification`), with `icon` where it reports no completion, else a plain line. Markdown-safe text. */
|
|
41
|
+
| {
|
|
42
|
+
part: 'notice';
|
|
43
|
+
value: string;
|
|
44
|
+
icon?: string;
|
|
37
45
|
} | {
|
|
38
46
|
part: 'warning';
|
|
39
47
|
value: string;
|
|
@@ -300,6 +308,14 @@ export interface ProjectedTurn {
|
|
|
300
308
|
prompt?: string;
|
|
301
309
|
/** Rendered parts for an assistant turn. */
|
|
302
310
|
parts?: ChatPart[];
|
|
311
|
+
/** How an assistant turn ended when it did not succeed, kept as the saved response's own error where the host
|
|
312
|
+
* shows one (`chatSessionResponses`); otherwise it is a warning part among `parts`. */
|
|
313
|
+
error?: {
|
|
314
|
+
message: string;
|
|
315
|
+
interrupted: boolean;
|
|
316
|
+
};
|
|
317
|
+
/** The record `error` came from, drawn as its warning part where anything follows it. */
|
|
318
|
+
errorEvent?: FrontendEvent;
|
|
303
319
|
}
|
|
304
320
|
export declare function projectReplayTurns(events: readonly FrontendEvent[], granted: ReadonlySet<string>, context: ProjectionContext, history?: readonly ProjectedTurn[]): ProjectedTurn[];
|
|
305
321
|
/**
|
package/dist/view.js
CHANGED
|
@@ -701,8 +701,8 @@ export function projectEvent(event, context) {
|
|
|
701
701
|
// (a subagent's own input, `parent_id`: what it was asked is on its row)
|
|
702
702
|
if (text(payload.parent_id))
|
|
703
703
|
return [];
|
|
704
|
-
// A harness record (a background task's notification, an interruption) is Code-OSS's system notification row
|
|
705
|
-
//
|
|
704
|
+
// A harness record (a background task's notification, an interruption) is Code-OSS's system notification row
|
|
705
|
+
// (`noticeLine`).
|
|
706
706
|
const notice = notPersonNotice(payload);
|
|
707
707
|
if (notice !== undefined)
|
|
708
708
|
return notice && firstNotice(context, notifiedCall(payload)) ? [stable(noticeLine(notice))] : [];
|
|
@@ -918,9 +918,9 @@ export function projectEvent(event, context) {
|
|
|
918
918
|
return [];
|
|
919
919
|
// crates/harness/src/server.rs `submit_claimed`. VS Code's own agent host draws an interrupted turn as its
|
|
920
920
|
// response's warning-level error (`agentHostSessionHandler.ts` `_getTurnErrorDetails`, `executionInterrupted`):
|
|
921
|
-
// the handler's turn returns it as its result (`turnOutcome`), and a re-joined response
|
|
922
|
-
//
|
|
923
|
-
// (VS Code marks a stopped response itself).
|
|
921
|
+
// the handler's turn returns it as its result (`turnOutcome`), and so do a re-joined response and a saved one
|
|
922
|
+
// where the host keeps their result (`chatSessionResponses`); elsewhere they show its warning part. The live
|
|
923
|
+
// writer drops it when the person stopped the turn (VS Code marks a stopped response itself).
|
|
924
924
|
case 'turn_interrupted': {
|
|
925
925
|
const value = turnOutcome(event).message;
|
|
926
926
|
return [{ emit: { part: 'warning', value }, needs: 'chatParticipantAdditions', fallback: [{ part: 'markdown', value }] }];
|
|
@@ -935,8 +935,9 @@ export function projectEvent(event, context) {
|
|
|
935
935
|
// repository and is read only because that reader reads it.
|
|
936
936
|
//
|
|
937
937
|
// A failed turn is VS Code's own request error (`ChatResult.errorDetails`) on the turn the handler runs
|
|
938
|
-
// (`turnOutcome`)
|
|
939
|
-
// error's own words, as VS Code's agent host shows a turn's error, never
|
|
938
|
+
// (`turnOutcome`), and on a re-joined or saved response where the host keeps their result (`chatSessionResponses`);
|
|
939
|
+
// elsewhere it is the warning part. The error's own words, as VS Code's agent host shows a turn's error, never
|
|
940
|
+
// the event kind's name.
|
|
940
941
|
case 'turn_failed':
|
|
941
942
|
case 'stream_error':
|
|
942
943
|
case 'transport_error':
|
|
@@ -1057,7 +1058,7 @@ function messageText(message) {
|
|
|
1057
1058
|
}
|
|
1058
1059
|
/**
|
|
1059
1060
|
* What a `user_message` shows when no person typed it: undefined for the person's words, '' to show nothing, else a
|
|
1060
|
-
* short notice, plain text drawn as a
|
|
1061
|
+
* short notice, plain text drawn as a notice row (`noticeLine`). The harness's own records carry `source: 'harness'` (and a `notice` where one is shown) from the one
|
|
1061
1062
|
* provenance rule the runtime applies (`@volter/supercode-interchange/user-provenance`). The editor's automatic
|
|
1062
1063
|
* tripwire nudge travels through the person's input door, so the editor's frame is what names it.
|
|
1063
1064
|
*/
|
|
@@ -1069,11 +1070,26 @@ function notPersonNotice(payload) {
|
|
|
1069
1070
|
return undefined;
|
|
1070
1071
|
}
|
|
1071
1072
|
/**
|
|
1072
|
-
* A notice as a
|
|
1073
|
+
* A notice as a row of the transcript: its own words, plain, as Code-OSS's system notification row
|
|
1074
|
+
* (`stateToProgressAdapter.ts` `systemNotificationToChatPart`). Not a progress part: Code-OSS hides a progress message
|
|
1073
1075
|
* as soon as any other part follows it (`chatProgressContentPart.ts`), so the notice would never be seen.
|
|
1074
1076
|
*/
|
|
1075
1077
|
function noticeLine(notice) {
|
|
1076
|
-
|
|
1078
|
+
const icon = noticeIcon(notice);
|
|
1079
|
+
return { part: 'notice', value: plain(notice), ...(icon ? { icon } : {}) };
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* The icon of a notice that reports no completion. Code-OSS's agent host picks one per notification kind
|
|
1083
|
+
* (`stateToProgressAdapter.ts` `systemNotificationToChatPart`), with the completion check as its default; `warning` here
|
|
1084
|
+
* follows, by analogy, the one it gives an automatic-approval review that was interrupted. None for the others.
|
|
1085
|
+
*/
|
|
1086
|
+
function noticeIcon(notice) {
|
|
1087
|
+
// (by analogy: upstream draws no harness interruption or failed background task as a notification of its own)
|
|
1088
|
+
if (notice === 'Turn interrupted.')
|
|
1089
|
+
return 'warning';
|
|
1090
|
+
if (/^Background task (failed|killed|stopped|cancell?ed|error)\b/i.test(notice))
|
|
1091
|
+
return 'warning';
|
|
1092
|
+
return undefined;
|
|
1077
1093
|
}
|
|
1078
1094
|
/** The call a background task's notification record names (`<tool-use-id>`), for `firstNotice`. */
|
|
1079
1095
|
function notifiedCall(payload) {
|
|
@@ -1126,6 +1142,18 @@ export function projectReplayTurns(events, granted, context, history = []) {
|
|
|
1126
1142
|
}
|
|
1127
1143
|
let replayTurn = false;
|
|
1128
1144
|
let streamedText;
|
|
1145
|
+
// A saved failure is its response's error only while nothing follows it: the next turn's start, or anything drawn
|
|
1146
|
+
// after it in that response, turns it back into its warning part where it stood, so an error never shows after a
|
|
1147
|
+
// later turn's content and a second failure never replaces the first. A re-joined response follows the same rule.
|
|
1148
|
+
const append = (turn, ...parts) => {
|
|
1149
|
+
const failed = turn.errorEvent;
|
|
1150
|
+
if (failed) {
|
|
1151
|
+
turn.error = undefined;
|
|
1152
|
+
turn.errorEvent = undefined;
|
|
1153
|
+
turn.parts.push(...resolveParts(projectEvent(failed, context), granted));
|
|
1154
|
+
}
|
|
1155
|
+
turn.parts.push(...parts);
|
|
1156
|
+
};
|
|
1129
1157
|
for (const event of events) {
|
|
1130
1158
|
if (event.kind === 'request') {
|
|
1131
1159
|
const request = (payloadOf(event).request ?? payloadOf(event));
|
|
@@ -1150,7 +1178,7 @@ export function projectReplayTurns(events, granted, context, history = []) {
|
|
|
1150
1178
|
const last = turns[turns.length - 1];
|
|
1151
1179
|
const notice = noticeLine(shown);
|
|
1152
1180
|
if (last.role === 'assistant' && last.parts)
|
|
1153
|
-
last
|
|
1181
|
+
append(last, notice);
|
|
1154
1182
|
else
|
|
1155
1183
|
turns.push({ role: 'assistant', parts: [notice] });
|
|
1156
1184
|
continue;
|
|
@@ -1159,6 +1187,31 @@ export function projectReplayTurns(events, granted, context, history = []) {
|
|
|
1159
1187
|
replayTurn = true;
|
|
1160
1188
|
continue;
|
|
1161
1189
|
}
|
|
1190
|
+
// A turn that failed or was interrupted is its saved response's own error, where the host shows one.
|
|
1191
|
+
const outcome = granted.has('chatSessionResponses') ? turnOutcome(event) : undefined;
|
|
1192
|
+
if (outcome) {
|
|
1193
|
+
streamedText = undefined;
|
|
1194
|
+
if (!replayTurn) {
|
|
1195
|
+
turns.push({ role: 'user', prompt: HISTORY_MARKER_PROMPT });
|
|
1196
|
+
replayTurn = true;
|
|
1197
|
+
}
|
|
1198
|
+
const last = turns[turns.length - 1];
|
|
1199
|
+
if (last.role === 'assistant') {
|
|
1200
|
+
append(last);
|
|
1201
|
+
last.error = outcome;
|
|
1202
|
+
last.errorEvent = event;
|
|
1203
|
+
}
|
|
1204
|
+
else {
|
|
1205
|
+
turns.push({ role: 'assistant', parts: [], error: outcome, errorEvent: event });
|
|
1206
|
+
}
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
// (the next turn's start ends the last failure's hold, as on a re-joined response)
|
|
1210
|
+
if (event.kind === 'turn_started') {
|
|
1211
|
+
const last = turns[turns.length - 1];
|
|
1212
|
+
if (last?.role === 'assistant' && last.errorEvent)
|
|
1213
|
+
append(last);
|
|
1214
|
+
}
|
|
1162
1215
|
// (a record that is not conversation is no part of a turn: it goes to `unshown`, wherever it falls)
|
|
1163
1216
|
const parts = resolveParts(projectEvent(event, context), granted).filter(part => {
|
|
1164
1217
|
if (part.part !== 'unshown')
|
|
@@ -1184,7 +1237,7 @@ export function projectReplayTurns(events, granted, context, history = []) {
|
|
|
1184
1237
|
}
|
|
1185
1238
|
const last = turns[turns.length - 1];
|
|
1186
1239
|
if (last.role === 'assistant' && last.parts)
|
|
1187
|
-
last
|
|
1240
|
+
append(last, ...parts);
|
|
1188
1241
|
else
|
|
1189
1242
|
turns.push({ role: 'assistant', parts });
|
|
1190
1243
|
}
|
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.65",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "VS Code chat-view frontend for an existing Volter Harness SDK runtime",
|
|
7
7
|
"repository": {
|
|
@@ -53,7 +53,10 @@
|
|
|
53
53
|
"chatParticipantPrivate",
|
|
54
54
|
"chatParticipantAdditions",
|
|
55
55
|
"chatSessionsProvider",
|
|
56
|
-
"chatProvider"
|
|
56
|
+
"chatProvider",
|
|
57
|
+
"chatSessionResponses",
|
|
58
|
+
"chatSystemNotification",
|
|
59
|
+
"chatConfirmationResolution"
|
|
57
60
|
],
|
|
58
61
|
"contributes": {
|
|
59
62
|
"taskDefinitions": [
|