@volter/supercode-frontend-vscode 0.1.11

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.
Files changed (39) hide show
  1. package/NOTICE.md +19 -0
  2. package/README.md +293 -0
  3. package/dist/bindings.d.ts +7 -0
  4. package/dist/bindings.js +5 -0
  5. package/dist/client.d.ts +11 -0
  6. package/dist/client.js +5 -0
  7. package/dist/context.d.ts +6 -0
  8. package/dist/context.js +14 -0
  9. package/dist/credential.d.ts +17 -0
  10. package/dist/credential.js +53 -0
  11. package/dist/extension.d.ts +39 -0
  12. package/dist/extension.js +1131 -0
  13. package/dist/host-controls.d.ts +58 -0
  14. package/dist/host-controls.js +66 -0
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.js +5 -0
  17. package/dist/native-session.d.ts +6 -0
  18. package/dist/native-session.js +41 -0
  19. package/dist/proposals.d.ts +24 -0
  20. package/dist/proposals.js +54 -0
  21. package/dist/session.d.ts +154 -0
  22. package/dist/session.js +431 -0
  23. package/dist/view.d.ts +176 -0
  24. package/dist/view.js +717 -0
  25. package/media/anthropic.svg +1 -0
  26. package/media/google.svg +1 -0
  27. package/media/icon.png +0 -0
  28. package/media/openai.svg +1 -0
  29. package/package.json +200 -0
  30. package/scripts/pack-extension.mjs +63 -0
  31. package/vendor/supercode-frontend/README.md +22 -0
  32. package/vendor/supercode-frontend/client.d.mts +106 -0
  33. package/vendor/supercode-frontend/client.mjs +300 -0
  34. package/vendor/supercode-frontend/contract.json +289 -0
  35. package/vendor/supercode-frontend/generated-client.d.mts +18 -0
  36. package/vendor/supercode-frontend/generated-client.mjs +20 -0
  37. package/vendor/supercode-frontend/generated.d.mts +146 -0
  38. package/vendor/supercode-frontend/generated.mjs +19 -0
  39. package/vendor/supercode-frontend/package.json +20 -0
package/NOTICE.md ADDED
@@ -0,0 +1,19 @@
1
+ # Third-party notice
2
+
3
+ This optional frontend has **no runtime npm dependency**. It builds against `@types/vscode`
4
+ (Copyright (c) Microsoft Corporation, MIT) as an exact devDependency for types only; `vscode` itself
5
+ is supplied by the editor at runtime and is never bundled or redistributed here.
6
+
7
+ No Microsoft source is copied into this repository. In particular the editor's
8
+ `vscode.proposed.*.d.ts` files are **not** vendored: `src/vscode-proposed.d.ts` is a
9
+ Volter Harness-authored declaration of the narrow proposed-API subset this package calls, written against
10
+ the published proposals rather than copied from them.
11
+
12
+ The generated attachment client under `vendor/supercode-frontend/` is this repository's own
13
+ `@volter/supercode-frontend`, synced by `scripts/sync-sdk.mjs` and version-pinned by
14
+ `package.json#supercodeFrontendContract`.
15
+
16
+ Volter Harness-authored integration and presentation code is identified by this package's source history
17
+ and remains under the repository's declared `MIT OR Apache-2.0` license.
18
+
19
+ The bundled provider/harness SVG marks in media/ are derived from this repository's sdk/ui/src/logo.jsx. Brand marks identify the active provider; they do not imply affiliation.
package/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # Volter Harness VS Code frontend
2
+
3
+ This is an optional VS Code extension that presents an **already-running** Volter Harness SDK runtime in
4
+ the editor's own Chat view. It uses the exact generated `@volter/supercode-frontend` client
5
+ for all session discovery, history, events, and semantic actions, and VS Code's own chat API for all
6
+ presentation — no webview, no React, no stylesheet of its own.
7
+
8
+ It does not run an agent, load context, authenticate a provider, execute tools,
9
+ import sessions, or write session files. The Rust/headless Volter Harness core does not depend on this
10
+ package or npm.
11
+
12
+ ## Native chat controls
13
+
14
+ A bundling host can expose a private lifecycle endpoint through
15
+ `SUPERCODE_FRONTEND_HOST_URL` and `SUPERCODE_FRONTEND_HOST_CREDENTIAL_FILE`.
16
+ The endpoint must be loopback HTTP and the credential follows the runtime credential-file
17
+ checks. `GET /state` describes the selected harness/model/effort, available choices,
18
+ and current connection; `POST /select` asks the host to start the selected configuration
19
+ and returns its new scoped connection. The host owns validation, process lifetime,
20
+ persistence, and failure handling. Prompts and approvals still use the generated frontend client.
21
+ The contract is in `src/host-controls.ts`.
22
+
23
+ With that host, **Chat: New Chat: Choose Harness, Model and Reasoning** chooses
24
+ configuration before opening a separate conversation. A conversation's harness, model and
25
+ reasoning settings remain fixed: this integration does not advertise hot swapping. Native
26
+ Chat displays its selected model and provider icon inside the composer and its supported
27
+ approval policy below it. The response author identifies the harness.
28
+
29
+ The host publishes durable conversation IDs and their exact harness history identities.
30
+ `POST /open` resumes only the requested saved conversation; `POST /remember` binds its
31
+ reported native session ID to discovered history. Unknown or missing bindings fail explicitly,
32
+ never falling back to the currently active runtime. The host may provide an `openSessionCommand`
33
+ to reveal an exact resource through its native widget service. Cached native widgets should
34
+ notify `supercode.frontend.activateSession` when focused. The frontend owns no processes.
35
+
36
+ The permission group offers **Ask for approval** and, on an answerable interactive attachment,
37
+ **Auto approve**. Policies are scoped by conversation and captured for each turn; automatic
38
+ approval does not bypass harness sandbox rules. Unsupported modes are refused. Unanswerable
39
+ harnesses show **Harness permissions**. Previously answered requests cannot affect another
40
+ connection. Native command-button fallback limitations are documented below.
41
+
42
+ Text files and selected ranges use native Add Context and attachment chips. Their current
43
+ editor contents are serialized as explicitly labeled user-supplied context through the existing
44
+ prompt protocol. Binary text, more than 256 KiB per attachment, or more than 1 MiB total is
45
+ rejected without truncation. Image and tool attachments remain unavailable. Model inventories,
46
+ mode/tool controls, and context limits are not invented where the runtime has no contract.
47
+
48
+ These controls use native provider APIs; there is no custom composer or webview.
49
+ `test/bindings.test.mjs`, `test/context.test.mjs`, and `test/host-controls.test.mjs` cover
50
+ routing boundaries, context serialization/limits, identity and approval interpretation.
51
+ Live product acceptance is separate from these frontend unit tests.
52
+
53
+ ## Attaching
54
+
55
+ Three paths, in precedence order.
56
+
57
+ 1. **A host hands over the connection.** `activate()` returns an API a bundling product calls when
58
+ it already knows where its runtime is:
59
+
60
+ ```ts
61
+ const supercode = vscode.extensions.getExtension('volter-ai-dev.supercode-frontend-vscode');
62
+ const api = await supercode.activate();
63
+ await api.connect({baseUrl, token, permissions: ['observe', 'interact', 'approve']});
64
+ // api.connections, api.disconnect(id), api.grantedProposals, api.onDidChangeConnections
65
+ ```
66
+
67
+ 2. **The runtime's environment handover.** At activation the extension reads
68
+ `SUPERCODE_FRONTEND_URL`, `SUPERCODE_FRONTEND_CLIENT_ID`, `SUPERCODE_FRONTEND_CREDENTIAL_FILE`
69
+ and `SUPERCODE_FRONTEND_PERMISSIONS` from the extension host's environment — the same shape
70
+ `crates/cli/src/frontend_client.rs` already uses to launch the pi frontend, without the `PI`
71
+ infix. A Code-OSS server passes its environment to its extension host, so a product that starts
72
+ the server hands the connection over with no pasted token. The credential is a *file*, read with
73
+ `sdk/frontend-pi`'s rules (a regular, private, single-linked file of 64 hex bytes, re-checked
74
+ across the open). It differs from pi's in one deliberate way: the token is stored in
75
+ `SecretStorage` **before** the one-shot file is removed, because an extension host outlives one
76
+ attachment and reloads.
77
+
78
+ The credential file is **read, never consumed**. `sdk/frontend-pi` unlinks it, because a pi
79
+ process is exactly one attachment; an extension host is not — it reloads, and a reload must not
80
+ lose the runtime it was handed. Nor can the token be kept instead: `SecretStorage` is in-memory
81
+ in a host with no credential provider (measured in VS Code Web), and there is no durable place
82
+ an extension may put a bearer token that is better than the runtime's own private 0600 file. The
83
+ base URL it was handed is remembered in `globalState`, so the next activation reconnects.
84
+
85
+ A host that could not start a runtime at all says so with the fourth variable,
86
+ `SUPERCODE_FRONTEND_UNAVAILABLE` (or `connect({unavailable})`): its own sentence, naming its own
87
+ harness and the command a person must type. Nothing is attached and that sentence is what the
88
+ Chat view shows. It is shown as a **session** — `chatViewsWelcome` cannot do it, because
89
+ `ChatViewPane.shouldShowWelcome()` in
90
+ `src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts` begins
91
+ `const shouldShow = !hasCoreAgent && …`, so the whole welcome control is suppressed whenever any
92
+ core agent is registered for the Chat location, which a Code-OSS derivative with an agent host
93
+ always has; the `when` clause is never consulted. The session's `activeResponseCallback` is the
94
+ one door that writes into a session nobody has typed into, so the reason is on screen where the
95
+ eye lands rather than waiting for a prompt.
96
+
97
+ An attachment that arrives with HISTORY — a resumed runtime, `claude --resume <id>` — opens its
98
+ own session once, so the panel opens *on* the conversation instead of beside it. A runtime with
99
+ no history is left alone: there is nothing to show and stealing the panel would be rude.
100
+
101
+ 3. **Settings plus `SecretStorage`.** `supercode.frontend.baseUrl` names the runtime;
102
+ **Volter Harness: Set Runtime Token** prompts once and stores the credential in `SecretStorage`. The
103
+ credential is never a setting and never a URL query. `supercode.frontend.permissions` narrows the
104
+ attachment; `observe` is always included, and every control is still rendered only when the
105
+ runtime's own descriptor advertises the matching action.
106
+
107
+ The generated client opens SSE **before** taking the attachment snapshot, so no event falls between
108
+ history and live. Dropping or detaching never closes the runtime.
109
+
110
+ ## API proposals, and what it does without them
111
+
112
+ The extension asks for five proposals. A host grants them through
113
+ `product.json#extensionEnabledApiProposals` (a Code-OSS derivative that bundles this extension) or
114
+ `--enable-proposed-api volter-ai-dev.supercode-frontend-vscode`. **Stock VS Code grants none, and
115
+ everything below still works there** — the granted set is read back from the extension host at
116
+ runtime (`context.extension.packageJSON.enabledApiProposals` is the *filtered* description, not the
117
+ request), and each part declares its own fallback.
118
+
119
+ | Proposal | What it buys | Without it |
120
+ |---|---|---|
121
+ | `defaultChatParticipant` | `isDefault` — Volter Harness answers the Chat view with no `@` mention | the ordinary `@supercode` participant; a mention is required |
122
+ | `chatParticipantAdditions` | `ChatToolInvocationPart`, `ChatResponseConfirmationPart`, `ChatResponseWarningPart`, `codeblockUri`, `ChatResponseThinkingProgressPart` | progress lines, labeled Markdown results, typed replies, plain fenced diffs, blockquoted reasoning |
123
+ | `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 |
124
+ | `chatParticipantPrivate` | dynamic participant identity with the harness and provider icon | static participant identity |
125
+ | `chatProvider` | native fixed-model identity and provider icon | host new-chat configuration |
126
+
127
+ The session participant uses the exact session type id (`supercode`). With
128
+ `chatParticipantPrivate`, the extension registers its dynamic identity directly and keeps
129
+ `canDelegate` disabled so the workbench does not pre-register a generic agent that masks the
130
+ model name. Model descriptors target only the `supercode` session type, whose contribution declares
131
+ `requiresCustomModels` so the workbench renders and forwards the native model selection. As in Code-OSS's own
132
+ AgentHostLanguageModelProvider, they do not expose a second direct inference endpoint:
133
+ the participant validates model settings against the bound conversation, and the runtime
134
+ owns execution. Unknown token limits remain zero, not invented. Model/reasoning changes
135
+ require an explicitly new conversation; they never reset an existing transcript on send.
136
+ The permission group uses `kind: permissions`; the embedding workbench must expose its
137
+ native permission-picker action for this session type. No composer CSS or bespoke toolbar
138
+ is supplied by this frontend.
139
+
140
+ Two participant contributions exist for one reason: the workbench refuses a `chatParticipants` entry
141
+ that declares `isDefault` without the grant *and skips it entirely*, so a single entry would leave a
142
+ stock host with no `@supercode` at all. `supercode.chat` carries no proposal-gated manifest field and
143
+ is `when`-gated on `!supercodeChatDefaultParticipant`; `supercode.chat.default` carries `isDefault`,
144
+ `locations: ["panel"]` and `modes: ["ask"]` and is gated on the inverse. The extension sets that
145
+ context key from the grant it actually received. A host that grants nothing logs one line naming the
146
+ refused proposal and runs on the plain participant. (`locations` is omitted from the plain entry on
147
+ purpose: it too needs `chatParticipantAdditions`, and its absence already defaults to the Chat panel
148
+ — `chatParticipant.contribution.ts`, `locations: isNonEmptyArray(...) ? ... : [ChatAgentLocation.Chat]`.)
149
+
150
+ ## The mapping
151
+
152
+ `src/view.ts` is the whole event-to-chat-part mapping and it is pure: every input is the runtime's
153
+ own sequenced `FrontendEvent`, every output is a description of a VS Code chat part plus the proposal
154
+ it needs and the fallback it degrades to. Each arm names the producer it reads.
155
+
156
+ | Event kind | Producer | Rendered as |
157
+ |---|---|---|
158
+ | `text_delta` | `crates/runtime/src/event.rs` `AgentEvent::TextDelta` | `response.markdown` (streamed) |
159
+ | `user_message` | `crates/harness/src/server.rs` `submit_claimed` | the request turn in history; dropped live (VS Code already shows it) |
160
+ | `turn_started` / `turn_completed` / `turn_succeeded` | `submit_claimed`; `AgentEvent::TurnCompleted` | lifecycle only — the handler resolves, nothing renders |
161
+ | `turn_interrupted` | `submit_claimed` | an italic notice |
162
+ | `turn_failed` | `submit_claimed` | `ChatResponseWarningPart` → blockquote |
163
+ | `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 |
164
+ | `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 |
165
+ | `request` | `crates/harness/src/server.rs` `FrontendRequestBroker::publish` | a titled markdown block plus one **command button per answer** — stable API, no proposal |
166
+ | `cache_warning` | `AgentEvent::CacheWarning` | `ChatResponseWarningPart` → blockquote |
167
+ | `usage` | `AgentEvent::Usage` | a progress line |
168
+ | `background_output` | `AgentEvent::BackgroundOutput` | a fenced block, saying so when the runtime stopped retaining |
169
+ | `scheduled_prompt_started` / `_deferred` / `_completed` | declared by `frontend_descriptor`'s `display.event_kinds` | a progress line |
170
+ | `scheduler_error` | same | `ChatResponseWarningPart` → blockquote |
171
+ | `reasoning` / `thinking` | no producer in this repository; read because `crates/frontend-model/src/transcript.rs` reads them | `ChatResponseThinkingProgressPart` → blockquote |
172
+ | `stream_error` / `transport_error` / `watch_error` / `runtime_disconnected` | `crates/opencode-frontend/src/server.rs`; `crates/interchange/src/watch.rs`; synthesized by a frontend (`crates/frontend-tui/src/app.rs`); `stream_error` has no producer here | `ChatResponseWarningPart` → blockquote |
173
+ | anything else | opaque by contract | one **bounded** markdown cell that names the kind and shows the payload — never dropped |
174
+
175
+ **Why `diff_updated` is not an edit part.** Measured: the payload carries one unified-diff *string*
176
+ and nothing else. `ChatResponseTextEditPart` wants ranges and the runtime has already written the
177
+ file, so replaying them would double-apply. `ChatResponseMultiDiffPart` wants an `originalUri` and a
178
+ `modifiedUri` per file — two resources this frontend would have to fabricate and serve.
179
+ `ChatResponseExternalEditPart` brackets a callback that *performs* the edit so the editor can take a
180
+ "before" snapshot; ours would be a no-op after the write, making the editor's undo stop wrong. What
181
+ the payload does state is which files it touched (`+++ b/<path>`), so those become stable references.
182
+
183
+ **Why a request is buttons.** Two richer mechanisms were tried against a live runtime in the
184
+ editor's own Chat view, and both failed there, so neither ships. `ChatResponseConfirmationPart`
185
+ retires its own buttons on click — but the click answers by sending a **new chat request**
186
+ (`chatConfirmationContentPart.onDidClick`), and a Volter Harness approval is raised **while the turn is
187
+ still running** (`decide_approval` blocks the agent loop), so the editor refuses that request, the
188
+ click does nothing, and the runtime waits forever. `stream.questionCarousel` is the right shape on
189
+ paper — it resolves inside the running turn and the widget retires itself — and the widget did
190
+ render with its three options; but Submit retired it without ever delivering an answer this
191
+ frontend could read, leaving the runtime blocked. A preferred path that silently swallows the
192
+ answer is worse than a plain one that works. A `ChatResponseCommandButtonPart` runs inside the live
193
+ turn, needs no proposal, and carries its own answer per button — so an approval offers `Allow` / `Allow for session` / `Deny`, all three, where a
194
+ confirmation's one `data` payload could only ever have carried two. The command
195
+ (`supercode.frontend.respond`) narrates itself in the status bar, because an action whose success is
196
+ invisible is one nobody can trust. A pushed command button cannot be withdrawn — `chatCommandContentPart`
197
+ enables on `!isStale` and carries no used-state, and only `confirmation`, `mcpAuthenticationRequired`,
198
+ `planReview` and `questionCarousel` have one — so the buttons stay visible after the answer. They are
199
+ inert: the command refuses a request the runtime no longer holds and says "already resolved", and the
200
+ resolution sentence lands directly beneath them. An elicitation that carries a `requested_schema` gets an
201
+ `Answer…` button that asks for the value first; a request still pending while the session is idle
202
+ can also be answered by typing.
203
+
204
+ **A hosted harness runtime.** When the runtime is one Volter Harness HOSTS — a Claude Code, Codex, pi or
205
+ opencode session it started through `harness.v1.runtimes.start` — an approval it raises is
206
+ **answerable from this view** for every harness whose reply this runtime can translate
207
+ (`crates/harness/src/runtime/hosted.rs`): the canonical `respond {decision}` becomes that harness's
208
+ own envelope, which for Claude Code is `{behavior:'allow'}` / `{behavior:'deny', message}`. The
209
+ request states which decisions its harness protocol actually carries, so this view offers exactly
210
+ those and no button the runtime would refuse — Claude Code has an allow and a deny and no
211
+ always-allow *behavior* at all, so it gets two. A harness with no translation keeps
212
+ `actions.respond: false`, and then the request is rendered as an observation naming who answers it,
213
+ because a control appears only for an action the descriptor advertises. Tool completions name only
214
+ the call they answer (Claude Code's `tool_result` block carries just a `tool_use_id`), so an unnamed
215
+ completion keeps the name the start stated rather than becoming a tool called `tool`.
216
+
217
+ **Bounds.** The projection is bounded like `@volter/supercode-frontend-browser`'s: 64 KiB per
218
+ part, 256 characters per title, 2 KiB for an opaque payload, 500 history turns. When turns are
219
+ omitted the view says so; the runtime's own history is unchanged and authoritative. Terminal control
220
+ characters never reach a rendered part.
221
+
222
+ **The turn.** A chat request submits through `frontend.v2.send_input` and resolves on the first
223
+ `turn_succeeded` / `turn_failed` / `turn_interrupted`; the request's cancellation token calls
224
+ `frontend.v2.interrupt`. It *also* resolves when the descriptor goes idle after having been busy:
225
+ the editor holds the chat response open until the handler returns, and a frontend that hangs one
226
+ forever is worse than one that ends it a beat early. A sequence gap is likewise not fatal — it is
227
+ what the cursor exists for, so the session re-takes its snapshot rather than throwing out of the
228
+ live listener and stranding every in-flight turn.
229
+
230
+ **The lease.** The runtime hands ONE controller a lease and expires it
231
+ (`crates/harness/src/runtime_lease.rs`), so an attachment that may submit takes control on attach
232
+ and heartbeats at a third of the advertised TTL; `controller_required` / `lease_expired` on any
233
+ action is a take-control-and-retry, never something the view reports.
234
+
235
+ **The system prompt renders nothing.** It is in the runtime's canonical history because the MODEL
236
+ needs it; a reader does not, and a collapsed block is still a reader's problem. No chat product shows
237
+ one to a person, so `role: 'system'` is dropped outright and the transcript starts where the
238
+ conversation does.
239
+
240
+ **Session times are real or nothing.** `frontend.v2` carries no session timestamp, and the editor's
241
+ session list ALWAYS reads `timing.created`, rendering the epoch ("57 yrs ago") when a provider
242
+ supplies none — there is no path in `agentSessionsViewer` that omits the time. So the item gets the
243
+ true times this frontend does know: when this window attached, and the turn boundaries it watched go
244
+ by. Never a zero dressed up as a date.
245
+
246
+ **History.** Saved tool results use labeled Markdown and fenced output; raw HTML is
247
+ not supported by native history rendering. A saved call is labeled neutrally because
248
+ a recorded invocation does not prove that permission was granted. VS Code's chat history is request/response **pairs** and drops a response turn with no
249
+ request before it. A runtime transcript legitimately opens with assistant or system content — the
250
+ runtime compacts, so its canonical history can begin `[earlier conversation compacted: N messages]`
251
+ with no user turn left. Rather than drop it, the projection pairs it with a marker turn,
252
+ `⟨supercode runtime history⟩`, which names itself: nothing is presented as a person's words.
253
+
254
+ ## Building and packaging
255
+
256
+ ```
257
+ npm install && npm run build # sync the generated client, then tsc
258
+ npm test # build + the pure mapping over recorded events
259
+ npm run extension # write the VS Code extension folder to .extension/
260
+ node scripts/pack-extension.mjs --out .extension --vsix # and a .vsix
261
+ node scripts/pack-extension.mjs --out .stock --no-proposals # the stock-host manifest
262
+ ```
263
+
264
+ One `package.json` cannot be both manifests: npm wants the scoped
265
+ `@volter/supercode-frontend-vscode`, while VS Code builds an extension id as
266
+ `${publisher}.${name}` and a scope would put an `@` and a `/` in it. The npm manifest is the source
267
+ of truth and `scripts/pack-extension.mjs` writes the extension's manifest beside the same built
268
+ files, with the plain name that resolves to `volter-ai-dev.supercode-frontend-vscode`. That folder is
269
+ what `--extensions-dir` wants; `--vsix` runs the stock packer over it.
270
+
271
+ `scripts/pack-extension.mjs` **ships inside the published package**, so a host that bundles this
272
+ extension into its own build runs the same transform rather than reimplementing it:
273
+
274
+ ```
275
+ node node_modules/@volter/supercode-frontend-vscode/scripts/pack-extension.mjs \
276
+ --out <build>/extensions/supercode-chat
277
+ ```
278
+
279
+ The installed package is already built (`dist/` and `vendor/` are in `files`), so the script writes
280
+ the extension manifest beside those bytes with no build step of its own.
281
+
282
+ Conversation creation uses `ChatSessionItemController.newChatSessionItemHandler`.
283
+ Permissions use `getChatSessionInputState` and its change/disposal events, with
284
+ per-conversation preferences persisted in workspace state. New untitled chats
285
+ start with Ask; old untitled bindings are read only for backwards compatibility.
286
+ Restored response cancellation interrupts the runtime and waits for acknowledgement.
287
+ Session-list updates consistently prioritize pending input over busy status.
288
+
289
+ The pinned Code-OSS host needs a resource-scoping repair in its input-state change
290
+ routing: stock code broadcasts changes to all input states of a provider. The
291
+ Volter overlay guards this fix with a native regression test. The former legacy
292
+ option-notification queue is no longer required. The narrow reveal/focus bridge
293
+ remains because the native model catalogue and participant identity are provider-wide.
@@ -0,0 +1,7 @@
1
+ /** Resolve only explicit saved bindings. Never substitute the currently active runtime. */
2
+ export declare function resolveSessionBinding(resource: {
3
+ authority: string;
4
+ toString(): string;
5
+ }, sessions: readonly {
6
+ id: string;
7
+ }[], aliases: ReadonlyMap<string, string>): string | undefined;
@@ -0,0 +1,5 @@
1
+ /** Resolve only explicit saved bindings. Never substitute the currently active runtime. */
2
+ export function resolveSessionBinding(resource, sessions, aliases) {
3
+ const candidate = sessions.some(session => session.id === resource.authority) ? resource.authority : aliases.get(resource.toString());
4
+ return candidate && sessions.some(session => session.id === candidate) ? candidate : undefined;
5
+ }
@@ -0,0 +1,11 @@
1
+ import { FrontendClient } from '../vendor/supercode-frontend/client.mjs';
2
+ export type FrontendPermission = 'observe' | 'interact' | 'approve' | 'terminate';
3
+ export interface VscodeFrontendConnection {
4
+ baseUrl: string;
5
+ token: string;
6
+ clientId?: string;
7
+ permissions?: FrontendPermission[];
8
+ fetch?: typeof globalThis.fetch;
9
+ }
10
+ /** Create the only runtime-facing object this presentation package may own. */
11
+ export declare function createFrontendClient(connection: VscodeFrontendConnection): FrontendClient;
package/dist/client.js ADDED
@@ -0,0 +1,5 @@
1
+ import { FrontendClient } from '../vendor/supercode-frontend/client.mjs';
2
+ /** Create the only runtime-facing object this presentation package may own. */
3
+ export function createFrontendClient(connection) {
4
+ return new FrontendClient(connection);
5
+ }
@@ -0,0 +1,6 @@
1
+ /** Text context delivered through the runtime's existing prompt door. No silent truncation. */
2
+ export interface TextContext {
3
+ name: string;
4
+ text: string;
5
+ }
6
+ export declare function withTextContext(prompt: string, entries: readonly TextContext[]): string;
@@ -0,0 +1,14 @@
1
+ export function withTextContext(prompt, entries) {
2
+ let total = 0;
3
+ for (const entry of entries) {
4
+ const size = new TextEncoder().encode(entry.text).length;
5
+ if (size > 256 * 1024)
6
+ throw new Error(`Attachment ${entry.name} exceeds 256 KiB. Select a smaller range.`);
7
+ total += size;
8
+ if (entry.text.includes('\0'))
9
+ throw new Error(`Attachment ${entry.name} is binary; only text context is supported.`);
10
+ }
11
+ if (total > 1024 * 1024)
12
+ throw new Error('Attached text exceeds 1 MiB. Remove files or select smaller ranges.');
13
+ return entries.length ? `${prompt}\n\nAttached context (user-supplied document contents):\n${JSON.stringify(entries)}` : prompt;
14
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Read one scoped runtime credential from the file the runtime handed over.
3
+ *
4
+ * The rules are `@volter/supercode-frontend-pi`'s (`bin/credential.mjs`):
5
+ * a regular, private, single-linked file of exactly 64 lowercase hex bytes,
6
+ * re-checked across the open so a path swap cannot slip a different secret in.
7
+ * The bytes never enter argv, a setting, or a URL.
8
+ *
9
+ * It differs from pi's in ONE way, deliberately: pi UNLINKS the file, because a
10
+ * pi process is exactly one attachment. An extension host is not — it reloads,
11
+ * and a reload must not lose the runtime it was handed. Nor can it keep the
12
+ * secret instead: `SecretStorage` is in-memory in a host with no credential
13
+ * provider (VS Code Web), and there is no durable place an extension may put a
14
+ * bearer token that is better than the runtime's own private 0600 file. So the
15
+ * file is READ and left alone; its lifetime is the runtime's to manage.
16
+ */
17
+ export declare function readCredentialFile(path: string): Promise<string>;
@@ -0,0 +1,53 @@
1
+ import { constants } from 'node:fs';
2
+ import { lstat, open } from 'node:fs/promises';
3
+ /**
4
+ * Read one scoped runtime credential from the file the runtime handed over.
5
+ *
6
+ * The rules are `@volter/supercode-frontend-pi`'s (`bin/credential.mjs`):
7
+ * a regular, private, single-linked file of exactly 64 lowercase hex bytes,
8
+ * re-checked across the open so a path swap cannot slip a different secret in.
9
+ * The bytes never enter argv, a setting, or a URL.
10
+ *
11
+ * It differs from pi's in ONE way, deliberately: pi UNLINKS the file, because a
12
+ * pi process is exactly one attachment. An extension host is not — it reloads,
13
+ * and a reload must not lose the runtime it was handed. Nor can it keep the
14
+ * secret instead: `SecretStorage` is in-memory in a host with no credential
15
+ * provider (VS Code Web), and there is no durable place an extension may put a
16
+ * bearer token that is better than the runtime's own private 0600 file. So the
17
+ * file is READ and left alone; its lifetime is the runtime's to manage.
18
+ */
19
+ export async function readCredentialFile(path) {
20
+ let handle;
21
+ try {
22
+ const before = await lstat(path);
23
+ if (!before.isFile() || before.isSymbolicLink()) {
24
+ throw new Error('scoped frontend credential must be a regular file');
25
+ }
26
+ handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
27
+ const metadata = await handle.stat();
28
+ const current = await lstat(path);
29
+ if (!metadata.isFile() ||
30
+ before.nlink !== 1 ||
31
+ metadata.nlink !== 1 ||
32
+ before.dev !== metadata.dev ||
33
+ before.ino !== metadata.ino ||
34
+ current.dev !== metadata.dev ||
35
+ current.ino !== metadata.ino) {
36
+ throw new Error('scoped frontend credential path changed while opening');
37
+ }
38
+ if (typeof process.getuid === 'function' &&
39
+ (metadata.uid !== process.getuid() || (metadata.mode & 0o077) !== 0)) {
40
+ throw new Error('scoped frontend credential file must be private to the current user');
41
+ }
42
+ if (metadata.size !== 64)
43
+ throw new Error('scoped frontend credential is malformed');
44
+ const bytes = await handle.readFile();
45
+ if (!bytes.every((byte) => (byte >= 48 && byte <= 57) || (byte >= 97 && byte <= 102))) {
46
+ throw new Error('scoped frontend credential is malformed');
47
+ }
48
+ return bytes.toString('ascii');
49
+ }
50
+ finally {
51
+ await handle?.close().catch(() => { });
52
+ }
53
+ }
@@ -0,0 +1,39 @@
1
+ import * as vscode from 'vscode';
2
+ import { type FrontendPermission } from './client.js';
3
+ import { type ProposalName } from './proposals.js';
4
+ export interface SupercodeConnectOptions {
5
+ baseUrl?: string;
6
+ token?: string;
7
+ permissions?: FrontendPermission[];
8
+ clientId?: string;
9
+ connectionId?: string;
10
+ label?: string;
11
+ /**
12
+ * Why there is no runtime, in the HOST's own words, when it could not start
13
+ * one. Given this instead of a `baseUrl`, nothing is attached and the
14
+ * sentence is what the Chat view shows.
15
+ */
16
+ unavailable?: string;
17
+ }
18
+ export interface SupercodeConnectionInfo {
19
+ readonly id: string;
20
+ readonly baseUrl: string;
21
+ readonly label: string;
22
+ readonly sessionId: string | null;
23
+ readonly model: string | null;
24
+ readonly turnState: 'idle' | 'busy' | null;
25
+ }
26
+ /** What `activate` returns: the door a host uses to hand over a connection. */
27
+ export interface SupercodeFrontendApi {
28
+ /**
29
+ * Attach to a runtime, or — with `{unavailable}` and no `baseUrl` — say why
30
+ * there is none, in the host's own words, and have that shown on screen.
31
+ */
32
+ connect(options: SupercodeConnectOptions): Promise<SupercodeConnectionInfo | undefined>;
33
+ disconnect(connectionId?: string): Promise<void>;
34
+ readonly connections: readonly SupercodeConnectionInfo[];
35
+ readonly grantedProposals: readonly ProposalName[];
36
+ onDidChangeConnections: vscode.Event<void>;
37
+ }
38
+ export declare function activate(context: vscode.ExtensionContext): Promise<SupercodeFrontendApi>;
39
+ export declare function deactivate(): void;