@volter/supercode-frontend-vscode 0.1.25 → 0.1.27

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 CHANGED
@@ -26,8 +26,16 @@ the choice. Neither the extension nor its credentials are bundled or copied.
26
26
 
27
27
  The optional host-controls state can include `setup: { ready, actions, reason,
28
28
  cwd }`. Each action supplies `kind` (`login` or `install`), `harness`, `name` and
29
- the exact `command` shown before execution. Chat renders native buttons, with
30
- **Sign in with ChatGPT** for Codex and the reported name for other harnesses.
29
+ the exact `command` revalidated before execution. On a signed-out first open,
30
+ Chat shows its native empty-session welcome: one primary **Sign in with ChatGPT**
31
+ button for the official extension, followed by **Supercode · local agents** and
32
+ each installed agent's own sign-in link, labeled with its reported name. The
33
+ `defaultChatParticipant` proposal supplies `additionalWelcomeMessage`; the pinned
34
+ Code-OSS `ChatViewWelcomePart`, with the editor's native-welcome-button overlay,
35
+ turns the standalone trusted command link into a native button. Stock hosts
36
+ render it as an ordinary command link; product release requires the overlay.
37
+ Setup creates no transcript turns, status sessions or protocol markers, and displays no terminal commands, filesystem paths or runtime banner.
38
+ No installed agents means an explanatory empty state, without invented choices.
31
39
  Clicking asks the host's authenticated `/setup` door to revalidate `{ kind,
32
40
  harness }` and return the action plus a structured `{ program, arguments, cwd, env? }`
33
41
  terminal launch. Absolute executables and an optional host-authored PATH keep
@@ -43,9 +51,10 @@ and reveals that conversation without a reload. Polling only finishes after both
43
51
  attachment and the host's native reveal command succeed for that same session
44
52
  and runtime URL. A transient attach/reveal failure stays pending and is retried;
45
53
  **Check again** retries that handoff even when the host already reports ready.
46
- Changed setup resources refresh
47
- Code-OSS's cached status history after an install. Hosts without these optional
48
- fields keep the existing runtime-attachment behavior.
54
+ Changed inventory updates welcome metadata in place, without opening another
55
+ session or moving focus. Ready connections clear the welcome and reveal the
56
+ actual conversation. Hosts without these optional fields keep the existing
57
+ runtime-attachment commands.
49
58
 
50
59
  A bundling host can expose a private lifecycle endpoint through
51
60
  `SUPERCODE_FRONTEND_HOST_URL` and `SUPERCODE_FRONTEND_HOST_CREDENTIAL_FILE`.
@@ -135,17 +144,11 @@ Three paths, in precedence order.
135
144
  an extension may put a bearer token that is better than the runtime's own private 0600 file. The
136
145
  base URL it was handed is remembered in `globalState`, so the next activation reconnects.
137
146
 
138
- A host that could not start a runtime at all says so with the fourth variable,
139
- `SUPERCODE_FRONTEND_UNAVAILABLE` (or `connect({unavailable})`): its own sentence, naming its own
140
- harness and the command a person must type. Nothing is attached and that sentence is what the
141
- Chat view shows. It is shown as a **session** — `chatViewsWelcome` cannot do it, because
142
- `ChatViewPane.shouldShowWelcome()` in
143
- `src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts` begins
144
- `const shouldShow = !hasCoreAgent && …`, so the whole welcome control is suppressed whenever any
145
- core agent is registered for the Chat location, which a Code-OSS derivative with an agent host
146
- always has; the `when` clause is never consulted. The session's `activeResponseCallback` is the
147
- one door that writes into a session nobody has typed into, so the reason is on screen where the
148
- eye lands rather than waiting for a prompt.
147
+ A host that could not start a runtime can supply
148
+ `SUPERCODE_FRONTEND_UNAVAILABLE` (or `connect({unavailable})`). Without managed
149
+ setup, its diagnostic appears in the native empty-chat welcome on hosts that
150
+ grant `defaultChatParticipant`. It is never manufactured into conversation
151
+ history. Managed setup uses the sign-in welcome described above instead.
149
152
 
150
153
  An attachment that arrives with HISTORY — a resumed runtime, `claude --resume <id>` — opens its
151
154
  own session once, so the panel opens *on* the conversation instead of beside it. A runtime with
@@ -8,4 +8,8 @@ export declare function chatGPTAvailable(): Promise<boolean>;
8
8
  /** Only called by an explicit click. The gallery and extension own installation
9
9
  * and authentication; there is no terminal or provider credential handling here. */
10
10
  export declare function signInWithChatGPT(): Promise<void>;
11
- export declare function setupParts(setup: ChatSetupState, chatGPT?: boolean): Array<vscode.ChatResponseMarkdownPart | vscode.ChatResponseCommandButtonPart>;
11
+ /** Empty-chat welcome content, rendered by Code-OSS's ChatViewWelcomePart.
12
+ * Only the official extension gets the primary button; native local sign-in
13
+ * actions remain ordinary links. Commands and host diagnostic paths stay out
14
+ * of the welcome, and the host still revalidates every action on click. */
15
+ export declare function setupWelcome(setup: ChatSetupState, chatGPT?: boolean): vscode.MarkdownString;
@@ -32,24 +32,33 @@ export async function signInWithChatGPT() {
32
32
  // Revealing its sidebar presents its own Sign in with ChatGPT when signed out.
33
33
  await vscode.commands.executeCommand('chatgpt.openSidebar');
34
34
  }
35
- export function setupParts(setup, chatGPT = false) {
36
- const parts = [];
35
+ /** Empty-chat welcome content, rendered by Code-OSS's ChatViewWelcomePart.
36
+ * Only the official extension gets the primary button; native local sign-in
37
+ * actions remain ordinary links. Commands and host diagnostic paths stay out
38
+ * of the welcome, and the host still revalidates every action on click. */
39
+ export function setupWelcome(setup, chatGPT = false) {
40
+ const welcome = new vscode.MarkdownString();
41
+ welcome.isTrusted = { enabledCommands: [CHATGPT_SIGN_IN, 'supercode.frontend.setup', 'supercode.frontend.refreshSetup'] };
37
42
  if (chatGPT) {
38
- parts.push(new vscode.ChatResponseCommandButtonPart({ title: 'Sign in with ChatGPT', command: CHATGPT_SIGN_IN }));
39
- parts.push(new vscode.ChatResponseMarkdownPart('Uses the official Codex extension. Installs it from the extension gallery if needed.'));
43
+ welcome.appendMarkdown(`[Sign in with ChatGPT](command:${CHATGPT_SIGN_IN})\n\n`);
44
+ welcome.appendMarkdown('Use the official Codex extension.');
45
+ welcome.appendMarkdown('\n\n');
40
46
  }
41
- parts.push(new vscode.ChatResponseMarkdownPart(chatGPT
42
- ? '**Supercode · use a local agent**\n\nComplete your local agent’s sign-in in the terminal and browser.'
43
- : 'Sign in to use Chat. Complete sign-in in the terminal and browser.'));
44
- for (const action of setup.actions) {
45
- const title = action.kind === 'install' ? `Install ${action.name}`
46
- : action.harness === 'codex' ? 'Sign in with ChatGPT' : `Sign in with ${action.name}`;
47
- parts.push(new vscode.ChatResponseMarkdownPart(`Runs: \`${action.command.replace(/`/g, '\\`')}\``));
48
- parts.push(new vscode.ChatResponseCommandButtonPart({ title, command: 'supercode.frontend.setup', arguments: [action.kind, action.harness] }));
47
+ welcome.appendMarkdown('**Supercode · local agents**\n\n');
48
+ const login = setup.actions.filter(action => action.kind === 'login');
49
+ if (login.length) {
50
+ for (const action of login) {
51
+ const title = new vscode.MarkdownString().appendText(`Sign in with ${action.name}`).value;
52
+ const args = encodeURIComponent(JSON.stringify([action.kind, action.harness]));
53
+ welcome.appendMarkdown(`- [${title}](command:supercode.frontend.setup?${args})\n`);
54
+ }
55
+ welcome.appendMarkdown('\n');
56
+ welcome.appendMarkdown('Each agent opens its own sign-in.');
49
57
  }
50
- if (!setup.actions.length) {
51
- parts.push(new vscode.ChatResponseMarkdownPart(setup.reason || 'No sign-in action is available.'));
58
+ else {
59
+ welcome.appendMarkdown('No local agents are ready. Install an agent on this machine to use it with Supercode.');
52
60
  }
53
- parts.push(new vscode.ChatResponseCommandButtonPart({ title: 'Check again', command: 'supercode.frontend.refreshSetup' }));
54
- return parts;
61
+ // Inline so it remains a secondary link, not another primary button.
62
+ welcome.appendMarkdown('\n\nAlready signed in? [Check again](command:supercode.frontend.refreshSetup).');
63
+ return welcome;
55
64
  }
package/dist/extension.js CHANGED
@@ -2,7 +2,7 @@ import { resolveSessionBinding } from './bindings.js';
2
2
  import { withTextContext } from './context.js';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { ChatHostControls, runtimeIdentity, harnessDisplayName, turnPermission } from './host-controls.js';
5
- import { CHATGPT_SIGN_IN, chatGPTAvailable, setupParts, signInWithChatGPT } from './chat-setup.js';
5
+ import { CHATGPT_SIGN_IN, chatGPTAvailable, setupWelcome, signInWithChatGPT } from './chat-setup.js';
6
6
  import * as vscode from 'vscode';
7
7
  import { createFrontendClient } from './client.js';
8
8
  import { readCredentialFile } from './credential.js';
@@ -15,8 +15,6 @@ const PARTICIPANT_ID = 'supercode.chat';
15
15
  const DEFAULT_PARTICIPANT_ID = 'supercode.chat.default';
16
16
  /** `package.json` gates the `isDefault` participant on this key. */
17
17
  const DEFAULT_PARTICIPANT_CONTEXT_KEY = 'supercodeChatDefaultParticipant';
18
- /** `chatViewsWelcome` shows the empty state until this is true. */
19
- const ATTACHED_CONTEXT_KEY = 'supercodeAttached';
20
18
  const PERMISSIONS = ['observe', 'interact', 'approve', 'terminate'];
21
19
  /** The environment a runtime uses to hand this frontend a live connection. */
22
20
  const ENV = Object.freeze({
@@ -46,16 +44,6 @@ function normalizePermissions(value, fallback) {
46
44
  permissions.unshift('observe');
47
45
  return value === undefined || value === null ? fallback : permissions;
48
46
  }
49
- /** The one session item that stands for "there is no runtime, and here is why". */
50
- const UNAVAILABLE_PATH = '/unavailable';
51
- /**
52
- * The prompt of the turn that carries a status this frontend is reporting.
53
- * It names itself; nothing here is presented as a person's words.
54
- */
55
- const STATUS_MARKER_PROMPT = '⟨supercode⟩';
56
- function unavailableResource() {
57
- return vscode.Uri.from({ scheme: SESSION_SCHEME, authority: 'supercode', path: UNAVAILABLE_PATH });
58
- }
59
47
  function sessionResource(connection) {
60
48
  const sessionId = 'conversation';
61
49
  return vscode.Uri.from({ scheme: SESSION_SCHEME, authority: connection.id, path: `/${encodeURIComponent(sessionId)}` });
@@ -367,6 +355,8 @@ export async function activate(context) {
367
355
  context.subscriptions.push(vscode.lm.registerLanguageModelChatProvider('supercode-runtime', {
368
356
  onDidChangeLanguageModelChatInformation: controlsChanged.event,
369
357
  provideLanguageModelChatInformation: () => {
358
+ if (controls.state.setup && !controls.state.setup.ready)
359
+ return [];
370
360
  const { selection, models } = controls.state;
371
361
  // A running harness does not advertise hot model changes. Configuration
372
362
  // belongs to the explicit new-conversation flow.
@@ -437,22 +427,14 @@ export async function activate(context) {
437
427
  // Host readiness alone is not completion: the native reveal must succeed too.
438
428
  setupHandoff = { id, baseUrl: connection.baseUrl };
439
429
  };
440
- let setupFingerprint = '';
430
+ let setupRevealed = false;
441
431
  const revealSetup = async () => {
442
- if (!controls?.state.setup || controls.state.setup.ready || !controls.state.openSessionCommand)
443
- return;
444
- const fingerprint = JSON.stringify(controls.state.setup);
445
- if (setupFingerprint === fingerprint)
432
+ if (!granted.has('chatSessionsProvider') || !controls?.state.setup || controls.state.setup.ready || setupRevealed)
446
433
  return;
447
- setupFingerprint = fingerprint;
448
- // A changed resource avoids Code-OSS reusing cached status history after an install.
449
- try {
450
- await vscode.commands.executeCommand(controls.state.openSessionCommand, vscode.Uri.from({ scheme: SESSION_SCHEME, path: UNAVAILABLE_PATH, query: encodeURIComponent(fingerprint) }).toString());
451
- }
452
- catch (error) {
453
- setupFingerprint = '';
454
- throw error;
455
- }
434
+ // A real empty session uses the widget's native welcome, never transcript
435
+ // history. Inventory changes update participant metadata in place.
436
+ await vscode.commands.executeCommand(`workbench.action.chat.openNewChatSessionInPlace.${SESSION_SCHEME}`, 'sidebar');
437
+ setupRevealed = true;
456
438
  };
457
439
  let refreshingSetup;
458
440
  const refreshSetup = () => {
@@ -461,6 +443,7 @@ export async function activate(context) {
461
443
  return;
462
444
  await controls.refresh();
463
445
  if (controls.state.setup?.ready) {
446
+ setupRevealed = false;
464
447
  await connectManaged();
465
448
  controlsChanged.fire();
466
449
  if (!setupHandoffComplete())
@@ -566,6 +549,8 @@ export async function activate(context) {
566
549
  changed: controlsChanged.event,
567
550
  currentOptions,
568
551
  options: (resource) => {
552
+ if (controls?.state.setup && !controls.state.setup.ready)
553
+ return [];
569
554
  const id = resource ? boundId(resource) : undefined;
570
555
  const active = resource ? (id ? frontends.get(id) : undefined) : managed();
571
556
  const canApprove = active?.session.descriptor?.actions.respond && active.session.permissions.has('approve');
@@ -605,8 +590,6 @@ export async function activate(context) {
605
590
  attached: (connection) => attachedIds.has(connection.id),
606
591
  catalogue: () => controls?.state.sessions ?? [],
607
592
  bindings: boundId,
608
- setup: () => controls?.state.setup,
609
- offerChatGPT,
610
593
  };
611
594
  context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.activateSession', async (resource) => { await connectionFor(vscode.Uri.parse(resource)); }));
612
595
  context.subscriptions.push(vscode.commands.registerCommand('supercode.frontend.openSession', async (id) => {
@@ -698,12 +681,6 @@ export async function activate(context) {
698
681
  await enqueueSelection(selection);
699
682
  }));
700
683
  context.subscriptions.push({ dispose: () => frontends.dispose() });
701
- context.subscriptions.push(frontends.onDidChange(() => {
702
- void vscode.commands.executeCommand('setContext', ATTACHED_CONTEXT_KEY, frontends.all.length > 0);
703
- }));
704
- // From the connections, not `false`: one made earlier in activation (the editor host
705
- // attaches before this line) already fired its change, and `false` here outlived it.
706
- await vscode.commands.executeCommand('setContext', ATTACHED_CONTEXT_KEY, frontends.all.length > 0);
707
684
  const runTurnInto = async (connection, prompt, stream, token, approval) => {
708
685
  if (approval === 'autoApprove' && (!connection.session.descriptor?.actions.respond || !connection.session.permissions.has('approve'))) {
709
686
  throw new Error('This harness does not support automatic approval through Chat. Select Harness permissions.');
@@ -753,8 +730,7 @@ export async function activate(context) {
753
730
  await applying;
754
731
  const requestedResource = chatContext.chatSessionContext?.chatSessionItem?.resource;
755
732
  if (controls?.state.setup && !controls.state.setup.ready) {
756
- for (const part of setupParts(controls.state.setup, offerChatGPT))
757
- stream.push(part);
733
+ stream.markdown('Sign in before sending a message. Use **New Chat: Choose Agent** to get started.');
758
734
  return {};
759
735
  }
760
736
  const connection = requestedResource
@@ -817,6 +793,22 @@ export async function activate(context) {
817
793
  log.warn(`Volter Harness chat participant ${id} was not registered: ${String(error?.message ?? error)}`);
818
794
  }
819
795
  }
796
+ let welcomeValue;
797
+ const updateWelcome = () => {
798
+ if (!granted.has('defaultChatParticipant'))
799
+ return;
800
+ const setup = controls?.state.setup;
801
+ const welcome = setup && !setup.ready ? setupWelcome(setup, offerChatGPT)
802
+ : !controls && frontends.all.length === 0 ? new vscode.MarkdownString(unavailableMarkdown(frontends.unavailable))
803
+ : undefined;
804
+ if (welcomeValue === welcome?.value)
805
+ return;
806
+ welcomeValue = welcome?.value;
807
+ for (const participant of participants)
808
+ participant.additionalWelcomeMessage = welcome;
809
+ };
810
+ updateWelcome();
811
+ context.subscriptions.push(controlsChanged.event(updateWelcome), frontends.onDidChange(updateWelcome));
820
812
  // The runtime's sessions in the Chat view's own picker. Without the
821
813
  // `chatSessionsProvider` grant the participant alone still works.
822
814
  if (granted.has('chatSessionsProvider')) {
@@ -1216,27 +1208,7 @@ function applyTiming(item, connection) {
1216
1208
  }
1217
1209
  function registerSessions(context, frontends, participant, granted, handler, controls) {
1218
1210
  const turns = vscode;
1219
- /**
1220
- * The session list is the door that actually SHOWS a reason on screen.
1221
- * `chatViewsWelcome` cannot: `ChatViewPane.shouldShowWelcome()` in
1222
- * `src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts`
1223
- * begins `const shouldShow = !hasCoreAgent && …`, so the whole welcome
1224
- * control is suppressed whenever ANY core agent is registered for the Chat
1225
- * location — which a Code-OSS derivative with an agent host always has. The
1226
- * `when` clause is never even consulted there.
1227
- */
1228
- const unavailableItem = () => {
1229
- if (!frontends.unavailable || frontends.all.length > 0)
1230
- return undefined;
1231
- const item = controller.createChatSessionItem(unavailableResource(), controls?.setup() ? 'Sign in to Chat' : 'Volter Harness · no runtime');
1232
- item.description = frontends.unavailable.split('\n')[0];
1233
- item.status = vscode.ChatSessionStatus.Failed;
1234
- return item;
1235
- };
1236
1211
  const items = () => {
1237
- const unavailable = unavailableItem();
1238
- if (unavailable)
1239
- return [unavailable];
1240
1212
  return (controls?.catalogue() ?? []).filter(entry => !frontends.get(entry.id)).map(entry => {
1241
1213
  const item = controller.createChatSessionItem(vscode.Uri.from({ scheme: SESSION_SCHEME, authority: entry.id, path: '/conversation' }), entry.title);
1242
1214
  item.description = runtimeIdentity(entry.selection.harness, entry.selection.model).name;
@@ -1290,21 +1262,6 @@ function registerSessions(context, frontends, participant, granted, handler, con
1290
1262
  context.subscriptions.push(vscode.chat.registerChatSessionContentProvider(SESSION_SCHEME, {
1291
1263
  provideChatSessionContent: async (resource, _token, { inputState }) => {
1292
1264
  const boundHandler = (request, chatContext, stream, token) => handler(request, { ...chatContext, chatSessionContext: { chatSessionItem: { resource, label: 'Conversation' } } }, stream, token);
1293
- if (resource.path === UNAVAILABLE_PATH) {
1294
- // As HISTORY, not `activeResponseCallback`: the editor only invokes
1295
- // that callback for a session it believes is mid-response, and this
1296
- // one is not running anything. A history turn renders the moment
1297
- // the session opens, with no request behind it and nothing to wait
1298
- // for — which is the whole point, since the person has not typed.
1299
- return {
1300
- title: controls?.setup() ? 'Sign in to Chat' : 'Volter Harness · no runtime',
1301
- history: [
1302
- new turns.ChatRequestTurn(STATUS_MARKER_PROMPT, undefined, [], participant.id, []),
1303
- new turns.ChatResponseTurn(controls?.setup() ? setupParts(controls.setup(), controls.offerChatGPT) : [new vscode.ChatResponseMarkdownPart(unavailableMarkdown(frontends.unavailable))], {}, participant.id),
1304
- ],
1305
- requestHandler: boundHandler,
1306
- };
1307
- }
1308
1265
  // Only a native untitled resource can create a conversation. Existing
1309
1266
  // resources must resolve their exact saved binding; there is no fallback.
1310
1267
  const connection = controls ? (resource.path.startsWith('/untitled-') && !controls.bindings(resource) ? undefined : await controls.resolve(resource)) : frontends.get(resource.authority);
@@ -18,7 +18,7 @@ export interface ManagedChatSession {
18
18
  title: string;
19
19
  created: number;
20
20
  }
21
- /** Host-owned operations. The frontend displays commands before the person runs them. */
21
+ /** Host-owned operations. The host revalidates commands before native terminal execution. */
22
22
  export interface ChatSetupAction {
23
23
  kind: 'login' | 'install';
24
24
  harness: string;
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.25",
4
+ "version": "0.1.27",
5
5
  "type": "module",
6
6
  "description": "VS Code chat-view frontend for an existing Volter Harness SDK runtime",
7
7
  "publisher": "volter-ai-dev",
@@ -80,11 +80,11 @@
80
80
  {
81
81
  "type": "supercode",
82
82
  "name": "supercode-runtime",
83
- "displayName": "Harness",
83
+ "displayName": "Supercode",
84
84
  "description": "Conversations with your AI assistant",
85
85
  "icon": "$(server-process)",
86
86
  "welcomeTitle": "Chat",
87
- "welcomeMessage": "Write a message to your coding agent.",
87
+ "welcomeMessage": "Work with your coding agent.",
88
88
  "inputPlaceholder": "Ask a question or describe a task",
89
89
  "order": 1,
90
90
  "capabilities": {
@@ -97,14 +97,6 @@
97
97
  "requiresCustomModels": true
98
98
  }
99
99
  ],
100
- "chatViewsWelcome": [
101
- {
102
- "icon": "$(server-process)",
103
- "title": "Volter Harness",
104
- "content": "Volter Harness is not attached to a runtime.\n[Set runtime token](command:supercode.frontend.setToken)\n[Attach](command:supercode.frontend.connect)",
105
- "when": "!supercodeAttached"
106
- }
107
- ],
108
100
  "commands": [
109
101
  {
110
102
  "command": "supercode.frontend.setToken",