@silo-code/sdk 0.46.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents-service.d.ts +1101 -17
- package/dist/agents-service.d.ts.map +1 -1
- package/dist/domain-types.d.ts +52 -0
- package/dist/domain-types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/permissions.d.ts +12 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js.map +1 -1
- package/dist/toolbar-items.d.ts +31 -10
- package/dist/toolbar-items.d.ts.map +1 -1
- package/dist/types.d.ts +154 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agents-service.ts +1135 -17
- package/src/domain-types.ts +53 -0
- package/src/index.ts +22 -0
- package/src/permissions.ts +13 -1
- package/src/toolbar-items.ts +31 -8
- package/src/types.ts +156 -7
package/src/agents-service.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Disposable } from "./types";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TabActivityBinder, TabIconBinder } from "./tab-adornment";
|
|
3
3
|
|
|
4
4
|
// `ctx.agents` — host-computed coding-agent activity and resume-identity
|
|
5
5
|
// observability. See RFC 0018 (docs/proposals/0018-ctx-agents-surface.md).
|
|
@@ -29,10 +29,31 @@ import type { TerminalKind } from "./domain-types";
|
|
|
29
29
|
export type AgentActivity = "none" | "working" | "idle" | "error" | "dead";
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* Which kind of **Agent Session** an {@link AgentInfo} describes (RFC 0038):
|
|
33
|
+
*
|
|
34
|
+
* - `"terminal"` — the agent runs in a PTY and draws its own TUI; Silo
|
|
35
|
+
* *infers* what it is doing from OSC/output signals, and its identity comes
|
|
36
|
+
* from detection (ADR 0028).
|
|
37
|
+
* - `"chat"` — the agent is an Agent Client Protocol child speaking structured
|
|
38
|
+
* JSON-RPC over piped stdio; it *reports* what it is doing and declares its
|
|
39
|
+
* identity at `initialize`, and Silo renders the conversation.
|
|
40
|
+
*
|
|
41
|
+
* `ctx.agents` reports **one shape regardless of kind** — the promise is
|
|
42
|
+
* observation parity, not capability parity. Most consumers never need to
|
|
43
|
+
* branch on this; use {@link AgentsService.reveal} to bring a session into
|
|
44
|
+
* view without knowing which kind it is.
|
|
45
|
+
*
|
|
46
|
+
* @category Core Types
|
|
47
|
+
* @public
|
|
48
|
+
* @beta
|
|
49
|
+
*/
|
|
50
|
+
export type AgentSessionKind = "terminal" | "chat";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Live agent-activity and resume-identity state for one **Agent Session**
|
|
54
|
+
* (RFC 0038), computed once by the host and shared across every subscriber —
|
|
55
|
+
* never recomputed per-extension. Returned by {@link AgentsService.getState}
|
|
56
|
+
* and {@link AgentsService.getByTerminalId}; delivered to
|
|
36
57
|
* {@link AgentsService.subscribe} listeners on every change.
|
|
37
58
|
*
|
|
38
59
|
* @category Consumer Services
|
|
@@ -40,20 +61,62 @@ export type AgentActivity = "none" | "working" | "idle" | "error" | "dead";
|
|
|
40
61
|
* @beta
|
|
41
62
|
*/
|
|
42
63
|
export interface AgentInfo {
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Stable id for this Agent Session — the key {@link AgentsService.reveal},
|
|
66
|
+
* {@link AgentsService.resume}, and {@link AgentsService.acknowledge} take.
|
|
67
|
+
* For a Terminal session this is the terminal record id; for a Chat session
|
|
68
|
+
* it is the session handle's id. Prefer this over
|
|
69
|
+
* {@link AgentInfo.terminalId} for anything that should not care which kind
|
|
70
|
+
* of session it is.
|
|
71
|
+
*/
|
|
72
|
+
readonly id: string;
|
|
73
|
+
/**
|
|
74
|
+
* The terminal record id backing this session, when there is one — the same
|
|
75
|
+
* id {@link AgentsService.getByTerminalId} and `ctx.terminals` take.
|
|
76
|
+
* Present for every `kind: "terminal"` session; absent for a `kind: "chat"`
|
|
77
|
+
* session, which has no PTY. A consumer that needs a terminal-tab id should
|
|
78
|
+
* check {@link AgentInfo.kind} first, or use {@link AgentsService.reveal}.
|
|
79
|
+
*/
|
|
80
|
+
readonly terminalId?: string;
|
|
81
|
+
/** The workspace this session belongs to. */
|
|
46
82
|
readonly workspaceId: string;
|
|
47
83
|
/**
|
|
48
|
-
* The
|
|
84
|
+
* The session's display label — host-computed, the same string for either
|
|
85
|
+
* kind, and the one a consumer should render. A status row, a navigator row
|
|
86
|
+
* and the session's own dock tab all showing this field is what keeps them
|
|
87
|
+
* from drifting.
|
|
88
|
+
*
|
|
89
|
+
* It is a three-step fallback, and the two kinds are exact parallels:
|
|
49
90
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
91
|
+
* | step | Terminal | Chat |
|
|
92
|
+
* | ------------------ | ----------------------------------- | --------------------------------- |
|
|
93
|
+
* | the agent's words | the OSC window title | `session_info_update.title` |
|
|
94
|
+
* | the user's name | `TerminalRecord.customName` | *(no rename gesture yet)* |
|
|
95
|
+
* | fallback | the terminal's derived name | declared agent name → profile label |
|
|
96
|
+
*
|
|
97
|
+
* The user's name wins where they set one; otherwise the agent's own words
|
|
98
|
+
* win, with agent **status markers stripped** (Claude's spinner, Cursor's
|
|
99
|
+
* trailing ` - Working …`) — the status is already structured state on this
|
|
100
|
+
* same record, so repeating it in the label is noise.
|
|
101
|
+
*
|
|
102
|
+
* Not every agent volunteers a title, and whether a given one does is its
|
|
103
|
+
* own choice — and its adapter's, version to version. A session that never
|
|
104
|
+
* gets one sits on the fallback, exactly as a terminal tab does for a CLI
|
|
105
|
+
* that writes no OSC title. Silo does not paper over that by inventing a
|
|
106
|
+
* summary from the first prompt.
|
|
55
107
|
*/
|
|
56
|
-
readonly
|
|
108
|
+
readonly title: string;
|
|
109
|
+
/**
|
|
110
|
+
* Which kind of Agent Session this is — `"terminal"` or `"chat"`. See
|
|
111
|
+
* {@link AgentSessionKind}. `ctx.agents` reports the same fields for both;
|
|
112
|
+
* this exists for the rare consumer that genuinely needs a PTY tab id or a
|
|
113
|
+
* transcript panel.
|
|
114
|
+
*
|
|
115
|
+
* (This field was the vestigial `TerminalKind` before RFC 0038 — always
|
|
116
|
+
* `"shell"` and read by nothing — and carries the session discriminator
|
|
117
|
+
* now.)
|
|
118
|
+
*/
|
|
119
|
+
readonly kind: AgentSessionKind;
|
|
57
120
|
/**
|
|
58
121
|
* Whether this terminal currently hosts an agent — true if it was created
|
|
59
122
|
* as one, or an agent-specific signal was observed in it (e.g. typing
|
|
@@ -102,6 +165,27 @@ export interface AgentInfo {
|
|
|
102
165
|
* both live and at `activity === "dead"`.
|
|
103
166
|
*/
|
|
104
167
|
readonly resumeCommand?: string;
|
|
168
|
+
/**
|
|
169
|
+
* Whether this session can be resumed **through Silo** — i.e. whether
|
|
170
|
+
* {@link AgentsService.resume} will do something for this id.
|
|
171
|
+
*
|
|
172
|
+
* - Terminal session: `true` once an exact session id was resolved (a
|
|
173
|
+
* Settings → Agents hook, or an agent's native session file), which is
|
|
174
|
+
* also when {@link AgentInfo.resumeCommand} becomes exact rather than an
|
|
175
|
+
* honest note. `resume()` is still a no-op for a Terminal session in this
|
|
176
|
+
* release — the user runs `resumeCommand` themselves; the flag is the
|
|
177
|
+
* forward-looking capability signal RFC 0038 replaces the shell-string
|
|
178
|
+
* `resumeCommand` contract with.
|
|
179
|
+
* - Chat session: `true` when the agent advertises `session/load`, so a
|
|
180
|
+
* fresh process can reload the transcript after the old one died.
|
|
181
|
+
*
|
|
182
|
+
* - Chat session: `true` when the agent advertises **either**
|
|
183
|
+
* `sessionCapabilities.resume` or `agentCapabilities.loadSession` (RFC
|
|
184
|
+
* 0042) — the two are distinct capabilities (see {@link ChatResumeState})
|
|
185
|
+
* but either is enough for {@link AgentsService.resume} to bring the
|
|
186
|
+
* conversation back.
|
|
187
|
+
*/
|
|
188
|
+
readonly canResume: boolean;
|
|
105
189
|
/**
|
|
106
190
|
* Human-readable agent name, e.g. `"Claude Code"` or `"Codex CLI"`. Tells
|
|
107
191
|
* you *which* agent CLI is running in this terminal, independent of
|
|
@@ -119,8 +203,48 @@ export interface AgentInfo {
|
|
|
119
203
|
* the same moment and lifecycle as `agentName`.
|
|
120
204
|
*/
|
|
121
205
|
readonly agentId?: string;
|
|
206
|
+
/**
|
|
207
|
+
* For a `kind: "chat"` session, where it stands in **Chat session
|
|
208
|
+
* resurrection** (RFC 0042) — absent for a `kind: "terminal"` session, which
|
|
209
|
+
* has no such lifecycle. See {@link ChatResumeState}.
|
|
210
|
+
*/
|
|
211
|
+
readonly chatResumeState?: ChatResumeState;
|
|
122
212
|
}
|
|
123
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Where a **Chat** Agent Session stands in Chat session resurrection (RFC
|
|
216
|
+
* 0042) — bringing its transcript, and the ability to keep talking, back after
|
|
217
|
+
* its process died with the app.
|
|
218
|
+
*
|
|
219
|
+
* - `"dormant"` — known only from persistence: the conversation exists and its
|
|
220
|
+
* panel is recorded, but nothing has connected to it this run and no agent
|
|
221
|
+
* process is alive. This is what a Chat session in a workspace you have not
|
|
222
|
+
* visited since launching looks like — listing it is the point, and opening
|
|
223
|
+
* it is what starts the reconnect. Its `activity` is always `"idle"`.
|
|
224
|
+
* - `"live"` — an ordinary, uninterrupted connection (a plain `session/new`
|
|
225
|
+
* this run; never went through the resume machinery).
|
|
226
|
+
* - `"resuming"` — a restore is in flight: `session/resume` or `session/load`
|
|
227
|
+
* is being probed and called.
|
|
228
|
+
* - `"resumed"` — reconnected: `session/resume` (no replay, the transcript
|
|
229
|
+
* comes from the **transcript journal**) or `session/load` (replay,
|
|
230
|
+
* reconciled against the journal) succeeded.
|
|
231
|
+
* - `"journal-only"` — the agent could do neither; the transcript is the
|
|
232
|
+
* journal alone and the session cannot be prompted in place — the panel's
|
|
233
|
+
* own "Continue in a new session" action is the way forward.
|
|
234
|
+
* - `"unavailable"` — no session id and no journal; nothing to show.
|
|
235
|
+
*
|
|
236
|
+
* @category Consumer Services
|
|
237
|
+
* @public
|
|
238
|
+
* @beta
|
|
239
|
+
*/
|
|
240
|
+
export type ChatResumeState =
|
|
241
|
+
| "dormant"
|
|
242
|
+
| "live"
|
|
243
|
+
| "resuming"
|
|
244
|
+
| "resumed"
|
|
245
|
+
| "journal-only"
|
|
246
|
+
| "unavailable";
|
|
247
|
+
|
|
124
248
|
/**
|
|
125
249
|
* A Catalog Agent's brand mark — SVG path data plus the two theme-dependent
|
|
126
250
|
* hexes. Consumed by {@link AgentIconGlyph}; a single hex cannot have enough
|
|
@@ -201,6 +325,22 @@ export interface AgentProfileSummary {
|
|
|
201
325
|
* `"agent-takes-none"` after the user has already typed one.
|
|
202
326
|
*/
|
|
203
327
|
readonly acceptsPrompt: boolean;
|
|
328
|
+
/**
|
|
329
|
+
* Which **interface** this profile starts the agent through — the profile's
|
|
330
|
+
* `launch` arm, expressed in the same {@link AgentSessionKind} vocabulary
|
|
331
|
+
* `AgentInfo.kind` uses (RFC 0038):
|
|
332
|
+
*
|
|
333
|
+
* - `"terminal"` — {@link AgentProfilesService.launch} runs it in a PTY and
|
|
334
|
+
* the agent draws its own TUI.
|
|
335
|
+
* - `"chat"` — {@link AgentSessionsService.connect} speaks the Agent Client
|
|
336
|
+
* Protocol to it and *you* render the conversation.
|
|
337
|
+
*
|
|
338
|
+
* The two are driven through different services, so a picker must filter on
|
|
339
|
+
* this rather than offer every profile to both: `launch()`ing a Chat profile
|
|
340
|
+
* and `connect()`ing a Terminal profile both fail, and the user should never
|
|
341
|
+
* be offered a profile that cannot work in the surface they are looking at.
|
|
342
|
+
*/
|
|
343
|
+
readonly interface: AgentSessionKind;
|
|
204
344
|
}
|
|
205
345
|
|
|
206
346
|
/**
|
|
@@ -353,6 +493,844 @@ export interface AgentProfilesService {
|
|
|
353
493
|
launch(options?: LaunchAgentProfileOptions): LaunchAgentProfileResult;
|
|
354
494
|
}
|
|
355
495
|
|
|
496
|
+
/**
|
|
497
|
+
* One block of an outgoing prompt turn for a **Chat session**
|
|
498
|
+
* ({@link AgentSessionHandle.prompt}). Structured content, never a shell
|
|
499
|
+
* string — the entire quoting/escaping risk surface of a Terminal session's
|
|
500
|
+
* opening prompt (RFC 0033 phase 3) does not exist here.
|
|
501
|
+
*
|
|
502
|
+
* - `"text"` — a run of plain text. `$HOME`, backticks, quotes and newlines
|
|
503
|
+
* are all literal.
|
|
504
|
+
* - `"resource_link"` — a pointer to a file (or other URI) the agent may read
|
|
505
|
+
* if it chooses. `uri` is typically a `file://` path inside the workspace.
|
|
506
|
+
* Always accepted — this is a pointer, not embedded content, so it needs no
|
|
507
|
+
* {@link AgentPromptCapabilities} gate.
|
|
508
|
+
* - `"resource"` — the file's own text **inlined** into the prompt, rather
|
|
509
|
+
* than a pointer the agent may or may not follow. Only send this to a
|
|
510
|
+
* session whose {@link AgentSessionHandle.promptCapabilities} has
|
|
511
|
+
* `embeddedContext: true`.
|
|
512
|
+
*
|
|
513
|
+
* Images are deferred — no probe has yet checked what each agent actually
|
|
514
|
+
* accepts for one, and this union grows behind that once one has (RFC 0040
|
|
515
|
+
* open question 3).
|
|
516
|
+
*
|
|
517
|
+
* @category Consumer Services
|
|
518
|
+
* @public
|
|
519
|
+
* @beta
|
|
520
|
+
*/
|
|
521
|
+
export type AgentPromptBlock =
|
|
522
|
+
| { readonly type: "text"; readonly text: string }
|
|
523
|
+
| {
|
|
524
|
+
readonly type: "resource_link";
|
|
525
|
+
readonly uri: string;
|
|
526
|
+
/** A short display name for the link; defaults to the last path segment. */
|
|
527
|
+
readonly name?: string;
|
|
528
|
+
}
|
|
529
|
+
| {
|
|
530
|
+
readonly type: "resource";
|
|
531
|
+
/** The resource's own URI, e.g. a `file://` path. */
|
|
532
|
+
readonly uri: string;
|
|
533
|
+
readonly mimeType?: string;
|
|
534
|
+
/** The embedded text content. Binary (blob) resources are not modelled —
|
|
535
|
+
* nothing in Silo has needed to embed one yet. */
|
|
536
|
+
readonly text: string;
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Why a prompt turn ended, straight from the agent's Agent Client Protocol
|
|
541
|
+
* stop reason. `"end_turn"` is the normal completion; `"max_tokens"` /
|
|
542
|
+
* `"max_turn_requests"` are budget cutoffs; `"refusal"` means the agent
|
|
543
|
+
* declined the request; `"cancelled"` follows {@link AgentSessionHandle.cancel}.
|
|
544
|
+
*
|
|
545
|
+
* @category Consumer Services
|
|
546
|
+
* @public
|
|
547
|
+
* @beta
|
|
548
|
+
*/
|
|
549
|
+
export type AgentStopReason =
|
|
550
|
+
| "end_turn"
|
|
551
|
+
| "max_tokens"
|
|
552
|
+
| "max_turn_requests"
|
|
553
|
+
| "refusal"
|
|
554
|
+
| "cancelled";
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* What {@link AgentSessionHandle.prompt} resolves to. A prompt that cannot be
|
|
558
|
+
* delivered — the connection dropped, the agent errored mid-turn — **rejects**
|
|
559
|
+
* instead, with the agent's own message where there is one.
|
|
560
|
+
*
|
|
561
|
+
* @category Consumer Services
|
|
562
|
+
* @public
|
|
563
|
+
* @beta
|
|
564
|
+
*/
|
|
565
|
+
export interface AgentPromptResult {
|
|
566
|
+
readonly stopReason: AgentStopReason;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* One piece of agent-authored content — the Agent Client Protocol content
|
|
571
|
+
* block, which appears both in a streamed message chunk
|
|
572
|
+
* ({@link AgentSessionUpdate.content}) and inside a tool call's output
|
|
573
|
+
* ({@link AgentToolCallContent.content}).
|
|
574
|
+
*
|
|
575
|
+
* **`type` is deliberately a `string`, not a union.** The protocol names
|
|
576
|
+
* `"text"`, `"image"`, `"audio"`, `"resource_link"` and `"resource"`, and a
|
|
577
|
+
* vendor may add its own; the same tolerance rule the rest of this surface
|
|
578
|
+
* follows applies — render the types you know and **name, rather than drop**,
|
|
579
|
+
* one you do not, so a transcript never silently loses a block.
|
|
580
|
+
*
|
|
581
|
+
* Every field but `type` is optional because which ones arrive depends on the
|
|
582
|
+
* type _and_ the agent: only `"text"` blocks were seen from Cursor
|
|
583
|
+
* (2026.09.02) and `claude-agent-acp` (0.75.1) in the 2026-09-08 probe, so
|
|
584
|
+
* treat the rest as modelled-but-unverified and check before you read.
|
|
585
|
+
*
|
|
586
|
+
* @category Consumer Services
|
|
587
|
+
* @public
|
|
588
|
+
* @beta
|
|
589
|
+
*/
|
|
590
|
+
export interface AgentContentBlock {
|
|
591
|
+
/** The protocol's block type — `"text"`, `"image"`, `"audio"`,
|
|
592
|
+
* `"resource_link"`, `"resource"`, or a vendor's own. */
|
|
593
|
+
readonly type: string;
|
|
594
|
+
/** The text, for a `"text"` block. */
|
|
595
|
+
readonly text?: string;
|
|
596
|
+
/** MIME type, for an `"image"` / `"audio"` / `"resource_link"` block. */
|
|
597
|
+
readonly mimeType?: string;
|
|
598
|
+
/** Where the content lives, for a `"resource_link"` (or an image given by
|
|
599
|
+
* reference rather than inline). Typically a `file://` URI. */
|
|
600
|
+
readonly uri?: string;
|
|
601
|
+
/** A short display name, for a `"resource_link"`. */
|
|
602
|
+
readonly name?: string;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* One block of a tool call's content — what the agent wants shown _inside_ the
|
|
607
|
+
* tool row. The protocol wraps each in a `{ type }` envelope, and the three it
|
|
608
|
+
* names are `"content"` (an {@link AgentContentBlock}), `"diff"` (a file edit)
|
|
609
|
+
* and `"terminal"` (output streaming into an agent-side terminal).
|
|
610
|
+
*
|
|
611
|
+
* `type` is a `string` for the usual reason: tolerate what you do not know.
|
|
612
|
+
* The remaining fields are the union's arms flattened, so check `type` before
|
|
613
|
+
* reading them.
|
|
614
|
+
*
|
|
615
|
+
* @category Consumer Services
|
|
616
|
+
* @public
|
|
617
|
+
* @beta
|
|
618
|
+
*/
|
|
619
|
+
export interface AgentToolCallContent {
|
|
620
|
+
/** `"content"`, `"diff"`, `"terminal"`, or a vendor's own. */
|
|
621
|
+
readonly type: string;
|
|
622
|
+
/** The content block, when `type` is `"content"`. */
|
|
623
|
+
readonly content?: AgentContentBlock;
|
|
624
|
+
/** The file being edited, when `type` is `"diff"`. */
|
|
625
|
+
readonly path?: string;
|
|
626
|
+
/** The file's contents before the edit, when `type` is `"diff"`. Absent for
|
|
627
|
+
* a file the agent is creating — `claude-agent-acp` 0.75.1 sends `null`
|
|
628
|
+
* there, which arrives here as `undefined`. */
|
|
629
|
+
readonly oldText?: string;
|
|
630
|
+
/** The file's contents after the edit, when `type` is `"diff"`. */
|
|
631
|
+
readonly newText?: string;
|
|
632
|
+
/** The agent-side terminal this call is streaming into, when `type` is
|
|
633
|
+
* `"terminal"`. Silo declines `terminal/*` today, so a consumer can name the
|
|
634
|
+
* block but cannot read its output. */
|
|
635
|
+
readonly terminalId?: string;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* A file (and optionally a line) a tool call touches — the protocol's
|
|
640
|
+
* `locations`, which is how an agent says "this call is about _this_ file"
|
|
641
|
+
* independently of its title. A Chat UI can turn these into "open the file"
|
|
642
|
+
* affordances.
|
|
643
|
+
*
|
|
644
|
+
* Both agents probed on 2026-09-08 send it; only `claude-agent-acp` 0.75.1 was
|
|
645
|
+
* seen sending `line`.
|
|
646
|
+
*
|
|
647
|
+
* @category Consumer Services
|
|
648
|
+
* @public
|
|
649
|
+
* @beta
|
|
650
|
+
*/
|
|
651
|
+
export interface AgentToolCallLocation {
|
|
652
|
+
/** Absolute path to the file. */
|
|
653
|
+
readonly path: string;
|
|
654
|
+
/** 1-based line the call is focused on, when the agent named one. */
|
|
655
|
+
readonly line?: number;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* One tool call an agent is making, carried by a `tool_call` (the call opening)
|
|
660
|
+
* or a `tool_call_update` (a change to one already open) — see
|
|
661
|
+
* {@link AgentSessionUpdate.toolCall}.
|
|
662
|
+
*
|
|
663
|
+
* **Both kinds map to this same type, so only {@link toolCallId} is
|
|
664
|
+
* guaranteed.** A `tool_call` typically carries `title`, `kind` and `status`;
|
|
665
|
+
* a `tool_call_update` carries **only the fields that changed** (in the
|
|
666
|
+
* 2026-09-08 probe, most updates were `{ toolCallId, status }` alone). So a
|
|
667
|
+
* consumer keys rows by {@link toolCallId} and patches the fields that are
|
|
668
|
+
* present — never overwrite a title with `undefined`.
|
|
669
|
+
*
|
|
670
|
+
* A `tool_call_update` for a call whose opening `tool_call` never arrived is a
|
|
671
|
+
* real shape; render it rather than dropping it.
|
|
672
|
+
*
|
|
673
|
+
* @category Consumer Services
|
|
674
|
+
* @public
|
|
675
|
+
* @beta
|
|
676
|
+
*/
|
|
677
|
+
export interface AgentToolCall {
|
|
678
|
+
/** The call's id — stable across its `tool_call` and every
|
|
679
|
+
* `tool_call_update`, and the same id an
|
|
680
|
+
* {@link AgentPermissionRequest.toolCallId} refers to. */
|
|
681
|
+
readonly toolCallId: string;
|
|
682
|
+
/** Human-readable label, e.g. `"Read notes.md"`. Present on the opening
|
|
683
|
+
* `tool_call`, and on an update only when it changed — agents do relabel a
|
|
684
|
+
* call as it progresses. */
|
|
685
|
+
readonly title?: string;
|
|
686
|
+
/** The protocol's coarse category: `"read"`, `"edit"`, `"delete"`,
|
|
687
|
+
* `"move"`, `"search"`, `"execute"`, `"think"`, `"fetch"`,
|
|
688
|
+
* `"switch_mode"`, `"other"`, or a vendor's own. Tolerate unknown values;
|
|
689
|
+
* do not switch exhaustively. */
|
|
690
|
+
readonly kind?: string;
|
|
691
|
+
/** `"pending"`, `"in_progress"`, `"completed"`, `"failed"`, or a vendor's
|
|
692
|
+
* own. Not every agent walks the whole ladder — `claude-agent-acp` 0.75.1
|
|
693
|
+
* went `pending` → `completed` with no `in_progress`. */
|
|
694
|
+
readonly status?: string;
|
|
695
|
+
/** What to show inside the row. Absent on an update that changed something
|
|
696
|
+
* else — treat that as "unchanged", not "now empty". */
|
|
697
|
+
readonly content?: readonly AgentToolCallContent[];
|
|
698
|
+
/** The files this call touches. */
|
|
699
|
+
readonly locations?: readonly AgentToolCallLocation[];
|
|
700
|
+
/** The arguments the agent passed to its own tool, exactly as it sent them.
|
|
701
|
+
* **Vendor-shaped by definition** — the protocol places no schema on it, so
|
|
702
|
+
* it is typed `unknown`: narrow it yourself, and expect a different shape
|
|
703
|
+
* from each agent. */
|
|
704
|
+
readonly rawInput?: unknown;
|
|
705
|
+
/** The tool's own result, same caveat as {@link rawInput}. */
|
|
706
|
+
readonly rawOutput?: unknown;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* One row of the agent's plan — the protocol's `plan` update entry.
|
|
711
|
+
*
|
|
712
|
+
* @category Consumer Services
|
|
713
|
+
* @public
|
|
714
|
+
* @beta
|
|
715
|
+
*/
|
|
716
|
+
export interface AgentPlanEntry {
|
|
717
|
+
/** What the step is, in the agent's words. */
|
|
718
|
+
readonly content: string;
|
|
719
|
+
/** `"pending"`, `"in_progress"`, `"completed"`, or a vendor's own. */
|
|
720
|
+
readonly status?: string;
|
|
721
|
+
/** `"high"`, `"medium"`, `"low"`, or a vendor's own, when the agent ranked
|
|
722
|
+
* the step. */
|
|
723
|
+
readonly priority?: string;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* One `session/update` notification from a **Chat session**, lightly
|
|
728
|
+
* normalized. The Agent Client Protocol streams a turn as a sequence of these:
|
|
729
|
+
* assistant text, agent "thinking", tool-call rows, plan updates, and more.
|
|
730
|
+
*
|
|
731
|
+
* **A consumer must tolerate `kind` values it does not recognize** — agents
|
|
732
|
+
* emit different subsets and vendors add their own (recon §3). Switch on the
|
|
733
|
+
* kinds you render and ignore the rest; never treat an unknown kind as an
|
|
734
|
+
* error.
|
|
735
|
+
*
|
|
736
|
+
* Everything a Chat UI must draw to render a transcript is a modelled field:
|
|
737
|
+
* {@link text} / {@link content} for the message kinds, {@link toolCall} for
|
|
738
|
+
* `tool_call` and `tool_call_update`, {@link plan} for `plan`. {@link raw} is
|
|
739
|
+
* for what is deliberately left out — see its own note.
|
|
740
|
+
*
|
|
741
|
+
* @category Consumer Services
|
|
742
|
+
* @public
|
|
743
|
+
* @beta
|
|
744
|
+
*/
|
|
745
|
+
export interface AgentSessionUpdate {
|
|
746
|
+
/**
|
|
747
|
+
* The Agent Client Protocol `sessionUpdate` discriminator, e.g.
|
|
748
|
+
* `"agent_message_chunk"`, `"agent_thought_chunk"`, `"tool_call"`,
|
|
749
|
+
* `"tool_call_update"`, `"plan"`, `"available_commands_update"`.
|
|
750
|
+
*/
|
|
751
|
+
readonly kind: string;
|
|
752
|
+
/**
|
|
753
|
+
* Text payload for the streaming-text kinds (`agent_message_chunk`,
|
|
754
|
+
* `agent_thought_chunk`, `user_message_chunk`); `undefined` for every other
|
|
755
|
+
* kind — and also for a text kind whose block is not text, in which case
|
|
756
|
+
* read {@link content}.
|
|
757
|
+
*/
|
|
758
|
+
readonly text?: string;
|
|
759
|
+
/**
|
|
760
|
+
* The whole content block a streaming-text kind carried, of which
|
|
761
|
+
* {@link text} is the `"text"` shorthand. Read it when you want to render an
|
|
762
|
+
* image or a resource link an agent sent as a message rather than dropping
|
|
763
|
+
* it. `undefined` for every non-message kind.
|
|
764
|
+
*/
|
|
765
|
+
readonly content?: AgentContentBlock;
|
|
766
|
+
/**
|
|
767
|
+
* Stable id for the run of chunks this update belongs to. Consecutive
|
|
768
|
+
* same-kind chunks share one — **synthesized by Silo when the agent omits
|
|
769
|
+
* `messageId`**, which real agents do (recon Finding 7), so a consumer can
|
|
770
|
+
* group a streamed sentence into one bubble without minting ids itself.
|
|
771
|
+
*/
|
|
772
|
+
readonly messageId?: string;
|
|
773
|
+
/**
|
|
774
|
+
* The tool call, for `kind` `"tool_call"` and `"tool_call_update"`;
|
|
775
|
+
* `undefined` otherwise. Key rows by {@link AgentToolCall.toolCallId} and
|
|
776
|
+
* patch in place — an update carries only what changed.
|
|
777
|
+
*/
|
|
778
|
+
readonly toolCall?: AgentToolCall;
|
|
779
|
+
/**
|
|
780
|
+
* The agent's plan **in full**, for `kind` `"plan"`; `undefined` otherwise.
|
|
781
|
+
* The agent reissues the entire list every time, so replace the plan you are
|
|
782
|
+
* showing rather than appending to it. May be empty.
|
|
783
|
+
*/
|
|
784
|
+
readonly plan?: readonly AgentPlanEntry[];
|
|
785
|
+
/**
|
|
786
|
+
* The raw Agent Client Protocol `update` object — the **escape hatch**, for
|
|
787
|
+
* the kinds and fields this surface does not model.
|
|
788
|
+
*
|
|
789
|
+
* What is deliberately not modelled, and why: `available_commands_update`
|
|
790
|
+
* (an agent's slash commands — a menu, not a transcript row),
|
|
791
|
+
* `usage_update` (token counts, which no agent reports the same way),
|
|
792
|
+
* `current_mode_update` (already surfaced as
|
|
793
|
+
* {@link AgentSessionConfigOption.currentValue}), `session_info_update`
|
|
794
|
+
* (already surfaced as {@link AgentInfo.title}), and vendor extensions such
|
|
795
|
+
* as `claude-agent-acp`'s `_meta`. None is needed to draw a transcript; all
|
|
796
|
+
* are readable here.
|
|
797
|
+
*
|
|
798
|
+
* **`raw` tracks the protocol, not this SDK's semver.** A field inside it can
|
|
799
|
+
* change shape, or vanish, when an agent or its adapter changes — no SDK
|
|
800
|
+
* major required, and that has already happened three times inside one sprint.
|
|
801
|
+
* Read it defensively, and if you find yourself needing it for something
|
|
802
|
+
* every Chat UI must render, that is a gap in this surface worth reporting.
|
|
803
|
+
* Treat it as read-only.
|
|
804
|
+
*/
|
|
805
|
+
readonly raw: Readonly<Record<string, unknown>>;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* One choice offered by an {@link AgentPermissionRequest} — pass its
|
|
810
|
+
* {@link AgentPermissionOption.optionId} to {@link AgentPermissionRequest.respond}.
|
|
811
|
+
*
|
|
812
|
+
* @category Consumer Services
|
|
813
|
+
* @public
|
|
814
|
+
* @beta
|
|
815
|
+
*/
|
|
816
|
+
export interface AgentPermissionOption {
|
|
817
|
+
readonly optionId: string;
|
|
818
|
+
/** Label to show on the button, e.g. `"Allow"`, `"Allow for this session"`,
|
|
819
|
+
* `"Reject"`. */
|
|
820
|
+
readonly name: string;
|
|
821
|
+
/** The protocol's coarse category for the option, e.g. `"allow_once"`,
|
|
822
|
+
* `"allow_always"`, `"reject_once"` — for styling a set of buttons
|
|
823
|
+
* consistently. Tolerate unknown values. */
|
|
824
|
+
readonly kind: string;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* The agent is **blocked on the user**: it wants permission to run a tool and
|
|
829
|
+
* the turn will not proceed until {@link AgentPermissionRequest.respond} is
|
|
830
|
+
* called. Delivered to {@link AgentSessionHandle.onPermission}.
|
|
831
|
+
*
|
|
832
|
+
* **This is not a safety boundary.** An agent routes a request through here
|
|
833
|
+
* only if it chooses to; nothing stops it touching the filesystem directly
|
|
834
|
+
* (recon Finding 1). Do not present this as Silo gating the agent's actions.
|
|
835
|
+
*
|
|
836
|
+
* If no `onPermission` listener is registered, Silo answers `cancelled` on the
|
|
837
|
+
* extension's behalf so the agent is never left hanging.
|
|
838
|
+
*
|
|
839
|
+
* @category Consumer Services
|
|
840
|
+
* @public
|
|
841
|
+
* @beta
|
|
842
|
+
*/
|
|
843
|
+
export interface AgentPermissionRequest {
|
|
844
|
+
/** The tool call this permission is for — the same id as the matching
|
|
845
|
+
* {@link AgentToolCall.toolCallId} in the update stream. */
|
|
846
|
+
readonly toolCallId: string;
|
|
847
|
+
/** A human-readable description of what the agent wants to do. */
|
|
848
|
+
readonly title: string;
|
|
849
|
+
/** The choices to present. Always at least one; order is the agent's. */
|
|
850
|
+
readonly options: readonly AgentPermissionOption[];
|
|
851
|
+
/**
|
|
852
|
+
* The call the agent is asking to make, when it sent one — the protocol's
|
|
853
|
+
* `toolCall` params, the same shape the update stream carries. It is how a
|
|
854
|
+
* Chat UI can show the diff **before** the user answers rather than only the
|
|
855
|
+
* title; `claude-agent-acp` 0.75.1 sends a full one including `content`.
|
|
856
|
+
*/
|
|
857
|
+
readonly toolCall?: AgentToolCall;
|
|
858
|
+
/** The raw Agent Client Protocol `session/request_permission` params. */
|
|
859
|
+
readonly raw: Readonly<Record<string, unknown>>;
|
|
860
|
+
/**
|
|
861
|
+
* Answer the request with one of {@link AgentPermissionRequest.options}.
|
|
862
|
+
* Idempotent — the first call wins, later calls are ignored. Passing an
|
|
863
|
+
* `optionId` that is not in `options` answers `cancelled`.
|
|
864
|
+
*/
|
|
865
|
+
respond(optionId: string): void;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* One selectable value inside an {@link AgentSessionConfigOption}.
|
|
870
|
+
*
|
|
871
|
+
* @category Consumer Services
|
|
872
|
+
* @public
|
|
873
|
+
* @beta
|
|
874
|
+
*/
|
|
875
|
+
export interface AgentSessionConfigChoice {
|
|
876
|
+
/** The value to pass to {@link AgentSessionHandle.setConfigOption}. */
|
|
877
|
+
readonly value: string;
|
|
878
|
+
/** Label to show, e.g. `"Claude Sonnet"`, `"Plan"`. */
|
|
879
|
+
readonly name: string;
|
|
880
|
+
/** A longer explanation, when the agent gave one. */
|
|
881
|
+
readonly description?: string;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* One session-level control the agent advertised at connect — the Agent Client
|
|
886
|
+
* Protocol `session/new` `configOptions` list (recon 2026-09-08). It is
|
|
887
|
+
* **self-describing and subsumes** the older `modes` / `models` fields: Cursor
|
|
888
|
+
* advertises a `"mode"` entry and a `"model"` entry, Claude a single `"mode"`
|
|
889
|
+
* entry (its permission mode), and a vendor may add its own.
|
|
890
|
+
*
|
|
891
|
+
* The write path is **generic too**:
|
|
892
|
+
* {@link AgentSessionHandle.setConfigOption} goes through the protocol's
|
|
893
|
+
* `session/set_config_option`, so a category Silo has never heard of still
|
|
894
|
+
* works — Claude's `"thought_level"` (Effort) has no typed method anywhere in
|
|
895
|
+
* the protocol and sets fine. Silo keeps `session/set_mode` /
|
|
896
|
+
* `session/set_model` only as a fallback for an agent that does not implement
|
|
897
|
+
* the generic setter.
|
|
898
|
+
*
|
|
899
|
+
* Render one control per entry and **skip an entry whose {@link type} you do
|
|
900
|
+
* not recognise** — the same tolerance rule {@link AgentSessionUpdate} follows
|
|
901
|
+
* for unknown `kind`s. Do not assume every advertised entry is settable: an
|
|
902
|
+
* adapter may list an id its own handler rejects, so treat a failed
|
|
903
|
+
* {@link AgentSessionHandle.setConfigOption} as "stop offering this one".
|
|
904
|
+
*
|
|
905
|
+
* @category Consumer Services
|
|
906
|
+
* @public
|
|
907
|
+
* @beta
|
|
908
|
+
*/
|
|
909
|
+
export interface AgentSessionConfigOption {
|
|
910
|
+
/** Stable id — pass it to {@link AgentSessionHandle.setConfigOption}. */
|
|
911
|
+
readonly id: string;
|
|
912
|
+
/** Label for the control, e.g. `"Mode"`, `"Model"`. */
|
|
913
|
+
readonly name: string;
|
|
914
|
+
/** A longer explanation, when the agent gave one. */
|
|
915
|
+
readonly description?: string;
|
|
916
|
+
/**
|
|
917
|
+
* The protocol category that decides how a write is delivered — `"mode"`,
|
|
918
|
+
* `"model"`, or a vendor's own. {@link AgentSessionHandle.setConfigOption}
|
|
919
|
+
* rejects a category with no verified writer.
|
|
920
|
+
*/
|
|
921
|
+
readonly category: string;
|
|
922
|
+
/**
|
|
923
|
+
* The control shape. Only `"select"` is modelled today; treat any other
|
|
924
|
+
* value as "do not render".
|
|
925
|
+
*/
|
|
926
|
+
readonly type: string;
|
|
927
|
+
/**
|
|
928
|
+
* The currently-selected {@link AgentSessionConfigChoice.value}. Kept
|
|
929
|
+
* current when the agent moves it itself (an ACP `current_mode_update`), so
|
|
930
|
+
* a bound control always reflects reality — subscribe with
|
|
931
|
+
* {@link AgentSessionHandle.onConfigOptionsChanged}.
|
|
932
|
+
*/
|
|
933
|
+
readonly currentValue: string;
|
|
934
|
+
/** The choices, in the agent's order. */
|
|
935
|
+
readonly options: readonly AgentSessionConfigChoice[];
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* One command the agent advertises (Agent Client Protocol
|
|
940
|
+
* `available_commands_update`, RFC 0040). **Skills arrive in this list too**
|
|
941
|
+
* — the protocol has no separate skills concept, and the two agents probed
|
|
942
|
+
* mark them differently: pi prefixes the name (`skill:code-review`), Claude
|
|
943
|
+
* does not mark them at all outside a `(user)` / `(project)` suffix in
|
|
944
|
+
* {@link description}'s prose. Splitting them back out would mean
|
|
945
|
+
* vendor-sniffing a label neither agent is contractually bound to keep, so
|
|
946
|
+
* this surface deliberately does not try.
|
|
947
|
+
*
|
|
948
|
+
* @category Consumer Services
|
|
949
|
+
* @public
|
|
950
|
+
* @beta
|
|
951
|
+
*/
|
|
952
|
+
export interface AgentCommand {
|
|
953
|
+
/** What to send after `/` to invoke it — `prompt([{ type: "text", text:
|
|
954
|
+
* "/" + name }])`. There is no separate invocation method. */
|
|
955
|
+
readonly name: string;
|
|
956
|
+
/** One-line summary for a palette row. */
|
|
957
|
+
readonly description?: string;
|
|
958
|
+
/**
|
|
959
|
+
* Present when the command takes a free-text argument; `hint` is the
|
|
960
|
+
* agent's own placeholder, when it gave one. Absent means the command takes
|
|
961
|
+
* none — normalised at the host boundary, since Claude sends `input: null`
|
|
962
|
+
* where pi omits the key entirely; this type only ever sees one spelling.
|
|
963
|
+
*/
|
|
964
|
+
readonly input?: { readonly hint?: string };
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* What a Chat session accepts as prompt content, read once from `initialize`
|
|
969
|
+
* and fixed for the session's life (RFC 0040). A UI gates its attachment
|
|
970
|
+
* affordance on this rather than offering one that fails at prompt time.
|
|
971
|
+
*
|
|
972
|
+
* A field the agent's `initialize` response omitted defaults to `false`
|
|
973
|
+
* rather than being left `undefined` — both `codex-acp` and `claude-agent-acp`
|
|
974
|
+
* omit `audio` entirely (recon 2026-09-09), so treating an absent field as
|
|
975
|
+
* "not accepted" is the only reading that doesn't require every consumer to
|
|
976
|
+
* repeat the same `?? false`.
|
|
977
|
+
*
|
|
978
|
+
* @category Consumer Services
|
|
979
|
+
* @public
|
|
980
|
+
* @beta
|
|
981
|
+
*/
|
|
982
|
+
export interface AgentPromptCapabilities {
|
|
983
|
+
/** Image content blocks are accepted. Not yet a sendable
|
|
984
|
+
* {@link AgentPromptBlock} — see its doc comment. */
|
|
985
|
+
readonly image: boolean;
|
|
986
|
+
/** Audio content blocks are accepted. */
|
|
987
|
+
readonly audio: boolean;
|
|
988
|
+
/**
|
|
989
|
+
* Embedded `"resource"` {@link AgentPromptBlock}s are accepted. A
|
|
990
|
+
* `"resource_link"` (a pointer the agent may read itself) is always
|
|
991
|
+
* allowed and is not gated on this.
|
|
992
|
+
*/
|
|
993
|
+
readonly embeddedContext: boolean;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* A live handle to one **Chat session** (RFC 0038) — an Agent Client Protocol
|
|
998
|
+
* child Silo spawned from a user-authored Chat profile, speaking structured
|
|
999
|
+
* JSON-RPC over piped stdio. Returned by {@link AgentSessionsService.connect}.
|
|
1000
|
+
*
|
|
1001
|
+
* The same session shows up in {@link AgentsService.getState} as an
|
|
1002
|
+
* {@link AgentInfo} with `kind: "chat"` and this handle's {@link id} — so the
|
|
1003
|
+
* Agents navigator, attention badges and status all work for it exactly as for
|
|
1004
|
+
* a Terminal session, with no extra wiring.
|
|
1005
|
+
*
|
|
1006
|
+
* Drive one turn at a time: call {@link prompt}, await its
|
|
1007
|
+
* {@link AgentPromptResult}, then prompt again.
|
|
1008
|
+
*
|
|
1009
|
+
* @category Consumer Services
|
|
1010
|
+
* @public
|
|
1011
|
+
* @beta
|
|
1012
|
+
*/
|
|
1013
|
+
export interface AgentSessionHandle {
|
|
1014
|
+
/**
|
|
1015
|
+
* This session's {@link AgentInfo.id} — the key {@link AgentsService.reveal},
|
|
1016
|
+
* {@link AgentsService.resume} and {@link AgentsService.acknowledge} take.
|
|
1017
|
+
* Stable across a {@link AgentsService.resume}.
|
|
1018
|
+
*/
|
|
1019
|
+
readonly id: string;
|
|
1020
|
+
/**
|
|
1021
|
+
* The agent's own session id (`session/new`'s `sessionId`, or the fresh id
|
|
1022
|
+
* `session/load` adopted) — what to pass back as
|
|
1023
|
+
* {@link AgentSessionRestore.sessionId} on a future {@link
|
|
1024
|
+
* AgentSessionsService.connect} to restore this conversation (RFC 0042).
|
|
1025
|
+
* Distinct from {@link AgentSessionHandle.id}, which is namespaced for
|
|
1026
|
+
* `ctx.agents` and does not change even when this does (an adopted id after
|
|
1027
|
+
* `session/load`).
|
|
1028
|
+
*/
|
|
1029
|
+
readonly sessionId: string;
|
|
1030
|
+
/** The user's asserted catalog agent id for the profile, if any — the same
|
|
1031
|
+
* value that selects the profile's `+`-menu icon. */
|
|
1032
|
+
readonly agentId?: string;
|
|
1033
|
+
/** Display name for the agent: what it declared at connect (`initialize`),
|
|
1034
|
+
* falling back to the profile label when it declared none (recon Finding 4). */
|
|
1035
|
+
readonly agentName: string;
|
|
1036
|
+
/**
|
|
1037
|
+
* Whether the agent advertises `session/resume` or `session/load`, i.e.
|
|
1038
|
+
* whether {@link AgentsService.resume} can bring this conversation back
|
|
1039
|
+
* after the process dies. Mirrors {@link AgentInfo.canResume}.
|
|
1040
|
+
*/
|
|
1041
|
+
readonly canResume: boolean;
|
|
1042
|
+
/**
|
|
1043
|
+
* How this handle came to be connected (RFC 0042) — see
|
|
1044
|
+
* {@link AgentSessionRestore}:
|
|
1045
|
+
*
|
|
1046
|
+
* - `"new"` — an ordinary `session/new` (no {@link
|
|
1047
|
+
* AgentSessionConnectOptions.resume} was given, or the persisted session
|
|
1048
|
+
* had gone stale and `connect()` fell through to a fresh one).
|
|
1049
|
+
* - `"resumed"` — a persisted session reconnected, via `session/resume` or
|
|
1050
|
+
* `session/load`.
|
|
1051
|
+
* - `"journal-only"` — the agent could do neither. There is no live
|
|
1052
|
+
* session: {@link prompt} rejects. Reconnect with
|
|
1053
|
+
* `resume: { sessionId, startFresh: true }` to continue in a new one,
|
|
1054
|
+
* preserving {@link journal} for continuity.
|
|
1055
|
+
*/
|
|
1056
|
+
readonly resumeOutcome: "new" | "resumed" | "journal-only";
|
|
1057
|
+
/**
|
|
1058
|
+
* Prior turns to paint **before** subscribing to {@link onUpdate} — the
|
|
1059
|
+
* **transcript journal** (RFC 0042), read from disk. Empty when
|
|
1060
|
+
* {@link resumeOutcome} is `"new"` and nothing preceded this connection;
|
|
1061
|
+
* populated for `"resumed"` (a `session/resume` reconnect, which itself
|
|
1062
|
+
* replays nothing — this is the only record) and `"journal-only"`. Feed
|
|
1063
|
+
* these through the same reducer as {@link onUpdate} before subscribing to
|
|
1064
|
+
* it, e.g. `journal.reduce(applyUpdate, emptyTranscript)`.
|
|
1065
|
+
*/
|
|
1066
|
+
readonly journal: readonly AgentSessionUpdate[];
|
|
1067
|
+
/**
|
|
1068
|
+
* Send a prompt turn and resolve when it ends. Content is structured blocks,
|
|
1069
|
+
* never a shell string. Rejects if the turn cannot be completed (connection
|
|
1070
|
+
* lost, agent error) — with the agent's message where it gave one. Also
|
|
1071
|
+
* rejects immediately when {@link resumeOutcome} is `"journal-only"` — there
|
|
1072
|
+
* is no live agent to prompt.
|
|
1073
|
+
*/
|
|
1074
|
+
prompt(blocks: readonly AgentPromptBlock[]): Promise<AgentPromptResult>;
|
|
1075
|
+
/**
|
|
1076
|
+
* Ask the agent to stop the current turn (Agent Client Protocol
|
|
1077
|
+
* `session/cancel`). The in-flight {@link prompt} promise then resolves with
|
|
1078
|
+
* `stopReason: "cancelled"` rather than rejecting.
|
|
1079
|
+
*/
|
|
1080
|
+
cancel(): void;
|
|
1081
|
+
/**
|
|
1082
|
+
* Subscribe to the turn's {@link AgentSessionUpdate} stream — streaming text,
|
|
1083
|
+
* tool calls, plans. **Not confined to between {@link prompt} and its
|
|
1084
|
+
* resolution**: some agents send one at connect, before any prompt at all
|
|
1085
|
+
* (pi's own startup banner arrives as an `agent_message_chunk` — recon
|
|
1086
|
+
* 2026-09-09), and a replay of prior turns can follow
|
|
1087
|
+
* {@link AgentsService.resume}. Subscribe before assuming nothing will
|
|
1088
|
+
* arrive yet. Returns a {@link Disposable}.
|
|
1089
|
+
*/
|
|
1090
|
+
onUpdate(listener: (update: AgentSessionUpdate) => void): Disposable;
|
|
1091
|
+
/**
|
|
1092
|
+
* Subscribe to {@link AgentPermissionRequest}s. Returns a {@link Disposable}.
|
|
1093
|
+
* With at least one listener registered, answering is your responsibility;
|
|
1094
|
+
* with none, Silo answers `cancelled`.
|
|
1095
|
+
*/
|
|
1096
|
+
onPermission(listener: (request: AgentPermissionRequest) => void): Disposable;
|
|
1097
|
+
/**
|
|
1098
|
+
* The session-level controls the agent advertised at connect — mode, model,
|
|
1099
|
+
* and whatever else it offers, each a self-describing
|
|
1100
|
+
* {@link AgentSessionConfigOption}. Empty when it advertised none.
|
|
1101
|
+
*
|
|
1102
|
+
* This is a **live snapshot**: {@link setConfigOption} and a mode the agent
|
|
1103
|
+
* changes itself both update it in place. Subscribe with
|
|
1104
|
+
* {@link onConfigOptionsChanged} and re-read.
|
|
1105
|
+
*/
|
|
1106
|
+
readonly configOptions: readonly AgentSessionConfigOption[];
|
|
1107
|
+
/**
|
|
1108
|
+
* Change one advertised control. `id` names an entry in
|
|
1109
|
+
* {@link configOptions}; `value` is one of that entry's
|
|
1110
|
+
* {@link AgentSessionConfigChoice.value}s.
|
|
1111
|
+
*
|
|
1112
|
+
* Works for **any** category the agent advertises, including ones Silo has
|
|
1113
|
+
* never heard of — the host writes through the protocol's generic
|
|
1114
|
+
* `session/set_config_option`, falling back to the typed
|
|
1115
|
+
* `session/set_mode` / `session/set_model` only for an agent that does not
|
|
1116
|
+
* implement it.
|
|
1117
|
+
*
|
|
1118
|
+
* Rejects, with nothing written, on an unknown `id` or a `value` outside
|
|
1119
|
+
* that entry's options — and with the agent's own message when the agent
|
|
1120
|
+
* refuses (an adapter may advertise an entry its own handler does not know).
|
|
1121
|
+
* A rejection is a signal to stop offering that control.
|
|
1122
|
+
*
|
|
1123
|
+
* Resolves once the agent has acknowledged the change. {@link configOptions}
|
|
1124
|
+
* is then replaced from the agent's own updated list — setting one option can
|
|
1125
|
+
* move another — and {@link onConfigOptionsChanged} fires just before it
|
|
1126
|
+
* resolves.
|
|
1127
|
+
*/
|
|
1128
|
+
setConfigOption(id: string, value: string): Promise<void>;
|
|
1129
|
+
/**
|
|
1130
|
+
* Fires whenever {@link configOptions} changes — a {@link setConfigOption}
|
|
1131
|
+
* landing, or the agent moving a value on its own (an ACP
|
|
1132
|
+
* `current_mode_update`). Re-read {@link configOptions} from the handle.
|
|
1133
|
+
* Returns a {@link Disposable}.
|
|
1134
|
+
*/
|
|
1135
|
+
onConfigOptionsChanged(listener: () => void): Disposable;
|
|
1136
|
+
/**
|
|
1137
|
+
* The commands the agent advertises, live — a `/` palette's data source
|
|
1138
|
+
* (RFC 0040). **Empty until the agent's first
|
|
1139
|
+
* `available_commands_update`**: both agents probed send one at connect,
|
|
1140
|
+
* before any prompt, but nothing in the protocol requires it, and pi took
|
|
1141
|
+
* ~12.5s to get there (behind its startup banner) — `connect()` does not
|
|
1142
|
+
* wait for it. Subscribe with {@link onCommandsChanged} and re-read.
|
|
1143
|
+
*
|
|
1144
|
+
* There is no `runCommand()`. Invoke one the same way you'd send anything
|
|
1145
|
+
* else: `prompt([{ type: "text", text: "/" + command.name }])`.
|
|
1146
|
+
*/
|
|
1147
|
+
readonly commands: readonly AgentCommand[];
|
|
1148
|
+
/** Fires whenever {@link commands} is replaced. Returns a
|
|
1149
|
+
* {@link Disposable}. */
|
|
1150
|
+
onCommandsChanged(listener: () => void): Disposable;
|
|
1151
|
+
/**
|
|
1152
|
+
* What this session accepts as prompt content, read once from `initialize`
|
|
1153
|
+
* and fixed for its life (RFC 0040) — unlike {@link commands}, this never
|
|
1154
|
+
* changes, so there is no change event for it.
|
|
1155
|
+
*/
|
|
1156
|
+
readonly promptCapabilities: AgentPromptCapabilities;
|
|
1157
|
+
/**
|
|
1158
|
+
* Tear the session down: kill the agent process and drop it from
|
|
1159
|
+
* {@link AgentsService.getState}. Idempotent. The process is a piped child of
|
|
1160
|
+
* Silo — it does **not** survive this, and closing the workspace or quitting
|
|
1161
|
+
* the app reaps it the same way.
|
|
1162
|
+
*/
|
|
1163
|
+
dispose(): void;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* Options for {@link AgentSessionsService.connect}.
|
|
1168
|
+
*
|
|
1169
|
+
* @category Consumer Services
|
|
1170
|
+
* @public
|
|
1171
|
+
* @beta
|
|
1172
|
+
*/
|
|
1173
|
+
export interface AgentSessionConnectOptions {
|
|
1174
|
+
/** Working directory for the agent. Defaults to the workspace folder. */
|
|
1175
|
+
cwd?: string;
|
|
1176
|
+
/** Which workspace the session belongs to (for
|
|
1177
|
+
* {@link AgentInfo.workspaceId} and `reveal`). Defaults to the active one. */
|
|
1178
|
+
workspaceId?: string;
|
|
1179
|
+
/**
|
|
1180
|
+
* A last-known display title, shown immediately with {@link resume} — before
|
|
1181
|
+
* the connection (`initialize` alone can take several seconds) or a
|
|
1182
|
+
* `session/resume` / `session/load` round trip resolves. Without it, a
|
|
1183
|
+
* restoring session's dock tab, workspace row, and Agents navigator entry
|
|
1184
|
+
* fall back to the profile's plain label for however long reconnecting
|
|
1185
|
+
* takes, even when the agent volunteered a real title before the app
|
|
1186
|
+
* closed. It **outlives the handshake**: it is still the session's title
|
|
1187
|
+
* once the connection is up, until the agent volunteers a real one of its
|
|
1188
|
+
* own (a `session_info_update`). The agent's product name from `initialize`
|
|
1189
|
+
* (`agentInfo.title`, e.g. "Claude Agent") does *not* supersede it — that is
|
|
1190
|
+
* the fallback for a session that has never had a title, not a replacement
|
|
1191
|
+
* for one this conversation already earned. Persist whatever
|
|
1192
|
+
* {@link AgentInfo.title} last was and pass it back here on the next
|
|
1193
|
+
* restore. Ignored without {@link resume} — a fresh session has no prior
|
|
1194
|
+
* title to show early.
|
|
1195
|
+
*/
|
|
1196
|
+
title?: string;
|
|
1197
|
+
/**
|
|
1198
|
+
* How to bring **your** UI for this session into view. Silo calls this from
|
|
1199
|
+
* {@link AgentsService.reveal} — after activating the session's workspace —
|
|
1200
|
+
* so a kind-agnostic caller (the Agents navigator, a notification, a
|
|
1201
|
+
* command) can focus a Chat session's transcript without knowing that a
|
|
1202
|
+
* transcript is what it is.
|
|
1203
|
+
*
|
|
1204
|
+
* Implement it with whatever "come to the front" means for your surface: a
|
|
1205
|
+
* dock panel calls `api.setActive()` on its own `DockPanelApi`, a side
|
|
1206
|
+
* panel reveals itself through `ctx.layout`. Called on the main thread,
|
|
1207
|
+
* possibly more than once; keep it cheap and idempotent, and expect it after
|
|
1208
|
+
* an `await` — capture the handle you need in a ref rather than closing over
|
|
1209
|
+
* render-scoped state.
|
|
1210
|
+
*
|
|
1211
|
+
* Omit it and `reveal(id)` still activates the workspace, which is all Silo
|
|
1212
|
+
* can honestly do for a session whose UI it does not own.
|
|
1213
|
+
*/
|
|
1214
|
+
reveal?: () => void;
|
|
1215
|
+
/**
|
|
1216
|
+
* Reconnect a previously-persisted session instead of starting a fresh one
|
|
1217
|
+
* — Chat session resurrection (RFC 0042). Pass the {@link
|
|
1218
|
+
* AgentSessionHandle.sessionId} a prior `connect()` on this same profile
|
|
1219
|
+
* returned (what a recorded panel keeps in its `DockPanelState`).
|
|
1220
|
+
*
|
|
1221
|
+
* `connect()` never rejects because the target has gone stale: it probes
|
|
1222
|
+
* `session/resume` and `session/load` separately (`resume` wins when both
|
|
1223
|
+
* are advertised), and falls through to a fresh `session/new` if neither
|
|
1224
|
+
* works and there is no journal to fall back to. See {@link
|
|
1225
|
+
* AgentSessionHandle.resumeOutcome} for which path was taken.
|
|
1226
|
+
*/
|
|
1227
|
+
resume?: AgentSessionRestore;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* A restore target for {@link AgentSessionConnectOptions.resume} — Chat
|
|
1232
|
+
* session resurrection (RFC 0042).
|
|
1233
|
+
*
|
|
1234
|
+
* @category Consumer Services
|
|
1235
|
+
* @public
|
|
1236
|
+
* @beta
|
|
1237
|
+
*/
|
|
1238
|
+
export interface AgentSessionRestore {
|
|
1239
|
+
/**
|
|
1240
|
+
* The session id to reconnect — an earlier connection's {@link
|
|
1241
|
+
* AgentSessionHandle.sessionId}.
|
|
1242
|
+
*/
|
|
1243
|
+
sessionId: string;
|
|
1244
|
+
/**
|
|
1245
|
+
* Skip probing `session/resume` / `session/load` and start a fresh
|
|
1246
|
+
* `session/new` directly, carrying the **transcript journal** kept under
|
|
1247
|
+
* {@link sessionId} into the new session's — "Continue in a new session"
|
|
1248
|
+
* from a `"journal-only"` {@link AgentSessionHandle.resumeOutcome}. The new
|
|
1249
|
+
* handle's {@link AgentSessionHandle.sessionId} is whatever `session/new`
|
|
1250
|
+
* mints, **not** this `sessionId` — persist that returned id for the next
|
|
1251
|
+
* restore, exactly as after a plain `connect()`. Keeping the original,
|
|
1252
|
+
* already-known-unresumable id instead would mean every future restore
|
|
1253
|
+
* keeps retrying an id the agent will never resume, forever, even while the
|
|
1254
|
+
* new conversation itself works fine turn after turn (found live,
|
|
1255
|
+
* 2026-09-09).
|
|
1256
|
+
*/
|
|
1257
|
+
startFresh?: boolean;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* Connect to a **Chat session** and drive it — exposed as
|
|
1262
|
+
* `ctx.agents.sessions` (RFC 0038 phase 2). The counterpart to
|
|
1263
|
+
* {@link AgentProfilesService} for the `chat` launch arm: where `profiles`
|
|
1264
|
+
* *starts a terminal and walks away*, this *holds a live connection* an
|
|
1265
|
+
* extension can prompt, watch and cancel.
|
|
1266
|
+
*
|
|
1267
|
+
* **Sourced from user-authored profiles only.** There is deliberately no
|
|
1268
|
+
* "connect to this command" — an extension cannot point Silo at an arbitrary
|
|
1269
|
+
* binary to spawn with pipes. The user defines a Chat profile on
|
|
1270
|
+
* Settings → Agents; an extension names it.
|
|
1271
|
+
*
|
|
1272
|
+
* Needs the **`"agents"` {@link Permission}**, declared in the extension's
|
|
1273
|
+
* `silo.permissions` and granted at install: {@link connect} throws without it.
|
|
1274
|
+
*
|
|
1275
|
+
* @example
|
|
1276
|
+
* ```ts
|
|
1277
|
+
* const session = await ctx.agents.sessions.connect("claude-chat");
|
|
1278
|
+
* const off = session.onUpdate((u) => {
|
|
1279
|
+
* if (u.kind === "agent_message_chunk") appendToTranscript(u.messageId, u.text);
|
|
1280
|
+
* });
|
|
1281
|
+
* const result = await session.prompt([{ type: "text", text: "Explain this repo." }]);
|
|
1282
|
+
* ctx.log.info(`turn ended: ${result.stopReason}`);
|
|
1283
|
+
* ctx.subscriptions.push(off, { dispose: () => session.dispose() });
|
|
1284
|
+
* ```
|
|
1285
|
+
*
|
|
1286
|
+
* @category Consumer Services
|
|
1287
|
+
* @public
|
|
1288
|
+
* @beta
|
|
1289
|
+
*/
|
|
1290
|
+
export interface AgentSessionsService {
|
|
1291
|
+
/**
|
|
1292
|
+
* Spawn the agent for the named Chat profile, run the Agent Client Protocol
|
|
1293
|
+
* `initialize` + `session/new` handshake, and resolve with a live
|
|
1294
|
+
* {@link AgentSessionHandle}.
|
|
1295
|
+
*
|
|
1296
|
+
* Rejects when: the extension lacks the `"agents"` permission; no profile has
|
|
1297
|
+
* that id; the profile is a Terminal profile, not a Chat one; there is no
|
|
1298
|
+
* target workspace; the agent needs authentication (its `session/new`
|
|
1299
|
+
* failed); or the agent reported a startup error (the rejection carries its
|
|
1300
|
+
* message).
|
|
1301
|
+
*
|
|
1302
|
+
* With {@link AgentSessionConnectOptions.resume}, `connect()` still never
|
|
1303
|
+
* rejects merely because the target has gone stale — it falls all the way
|
|
1304
|
+
* through to a fresh `session/new` (RFC 0042); see {@link
|
|
1305
|
+
* AgentSessionHandle.resumeOutcome}.
|
|
1306
|
+
*
|
|
1307
|
+
* @param profileId — an {@link AgentProfileSummary.id} whose profile uses the
|
|
1308
|
+
* `chat` launch arm.
|
|
1309
|
+
*/
|
|
1310
|
+
connect(
|
|
1311
|
+
profileId: string,
|
|
1312
|
+
options?: AgentSessionConnectOptions,
|
|
1313
|
+
): Promise<AgentSessionHandle>;
|
|
1314
|
+
/**
|
|
1315
|
+
* Read a session's **transcript journal** (RFC 0042) without connecting —
|
|
1316
|
+
* for painting a restored panel *before* `connect({ resume })` resolves,
|
|
1317
|
+
* which itself also pays for the `session/resume` / `session/load` network
|
|
1318
|
+
* round trip. This is the "paint from journal" half of the restore flow;
|
|
1319
|
+
* `connect()` is the "reconnect the agent" half, and the two run
|
|
1320
|
+
* independently on purpose.
|
|
1321
|
+
*
|
|
1322
|
+
* Resolves `[]` for a session that never wrote a journal entry, or an
|
|
1323
|
+
* unknown id — never rejects on a merely-missing journal. Needs the
|
|
1324
|
+
* `"agents"` {@link Permission}, same as {@link connect}.
|
|
1325
|
+
*
|
|
1326
|
+
* @param sessionId — an {@link AgentSessionHandle.sessionId}.
|
|
1327
|
+
*/
|
|
1328
|
+
readJournal(
|
|
1329
|
+
sessionId: string,
|
|
1330
|
+
options?: { workspaceId?: string },
|
|
1331
|
+
): Promise<readonly AgentSessionUpdate[]>;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
356
1334
|
/**
|
|
357
1335
|
* Host-computed coding-agent observability — exposed as
|
|
358
1336
|
* {@link ExtensionContext.agents}. Detection (what OSC/output signals mean
|
|
@@ -383,7 +1361,10 @@ export interface AgentsService {
|
|
|
383
1361
|
* instead.
|
|
384
1362
|
*/
|
|
385
1363
|
getState(options?: { allWorkspaces?: boolean }): AgentInfo[];
|
|
386
|
-
/** Look up {@link AgentInfo} for a specific terminal tab by its record id.
|
|
1364
|
+
/** Look up {@link AgentInfo} for a specific terminal tab by its record id.
|
|
1365
|
+
* Only ever resolves a `kind: "terminal"` session — use
|
|
1366
|
+
* {@link AgentsService.getState} and match on {@link AgentInfo.id} to find a
|
|
1367
|
+
* Chat session. */
|
|
387
1368
|
getByTerminalId(terminalId: string): AgentInfo | undefined;
|
|
388
1369
|
/**
|
|
389
1370
|
* Subscribe to changes in the active workspace's agent state. Pass
|
|
@@ -416,8 +1397,134 @@ export interface AgentsService {
|
|
|
416
1397
|
* }),
|
|
417
1398
|
* );
|
|
418
1399
|
* ```
|
|
1400
|
+
*
|
|
1401
|
+
* @param id — an {@link AgentInfo.id}. A terminal record id is one (every
|
|
1402
|
+
* Terminal session's `id` equals its `terminalId`), so existing callers
|
|
1403
|
+
* passing a terminal id keep working; a Chat session's id works too.
|
|
1404
|
+
*/
|
|
1405
|
+
acknowledge(id: string): void;
|
|
1406
|
+
/**
|
|
1407
|
+
* The Agent Session the user is currently **looking at** — the one whose
|
|
1408
|
+
* surface is the active tab — or `null` when the active tab is not an agent
|
|
1409
|
+
* at all (an editor, a settings page, nothing).
|
|
1410
|
+
*
|
|
1411
|
+
* Kind-agnostic by construction: a terminal tab reports its Terminal
|
|
1412
|
+
* session, and a dock panel that declared
|
|
1413
|
+
* `DockPanelApi.setAgentSession` reports its Chat session. That is
|
|
1414
|
+
* what lets one consumer implement "clear the badge for the session I'm
|
|
1415
|
+
* watching" or "hide the row for the session I'm already looking at" without
|
|
1416
|
+
* knowing which kind it got.
|
|
1417
|
+
*
|
|
1418
|
+
* @example
|
|
1419
|
+
* ```ts
|
|
1420
|
+
* // Hide the status row for whatever the user is already watching.
|
|
1421
|
+
* const watching = ctx.agents.getActive();
|
|
1422
|
+
* const rows = ctx.agents.getState().filter((a) => a.id !== watching);
|
|
1423
|
+
* ```
|
|
1424
|
+
*/
|
|
1425
|
+
getActive(): string | null;
|
|
1426
|
+
/**
|
|
1427
|
+
* Subscribe to changes in {@link AgentsService.getActive} — fired with the
|
|
1428
|
+
* new value (or `null`) whenever the active surface moves. Returns a
|
|
1429
|
+
* {@link Disposable} that cancels the subscription.
|
|
1430
|
+
*/
|
|
1431
|
+
subscribeActive(listener: (id: string | null) => void): Disposable;
|
|
1432
|
+
/**
|
|
1433
|
+
* Bind a provider of **activity badges** for Agent Session tabs — the
|
|
1434
|
+
* host-owned `Activity` chrome (spinner / ready / warn / error) on the
|
|
1435
|
+
* trailing edge of a tab.
|
|
1436
|
+
*
|
|
1437
|
+
* The binder's `provide` is handed an {@link AgentInfo.id}, and the host
|
|
1438
|
+
* routes the result to whichever tab is showing that session: a terminal tab
|
|
1439
|
+
* for a Terminal session, or the dock panel that declared
|
|
1440
|
+
* `DockPanelApi.setAgentSession` for a Chat session. One binder,
|
|
1441
|
+
* both kinds — which is the point: the `ctx.terminals` equivalent takes a
|
|
1442
|
+
* *terminal* id, so an extension literally could not badge a Chat session.
|
|
1443
|
+
*
|
|
1444
|
+
* `provide` is called synchronously for every visible tab during render.
|
|
1445
|
+
* Keep it a lookup: no allocation, no async, no work proportional to the
|
|
1446
|
+
* number of sessions.
|
|
1447
|
+
*
|
|
1448
|
+
* @example
|
|
1449
|
+
* ```ts
|
|
1450
|
+
* ctx.subscriptions.push(
|
|
1451
|
+
* ctx.agents.bindActivity({
|
|
1452
|
+
* id: "my-ext.agent-badge",
|
|
1453
|
+
* provide(agentSessionId) {
|
|
1454
|
+
* const info = ctx.agents
|
|
1455
|
+
* .getState({ allWorkspaces: true })
|
|
1456
|
+
* .find((a) => a.id === agentSessionId);
|
|
1457
|
+
* if (info?.activity !== "working") return null;
|
|
1458
|
+
* return { activity: "working", tooltip: "Agent working" };
|
|
1459
|
+
* },
|
|
1460
|
+
* }),
|
|
1461
|
+
* );
|
|
1462
|
+
* ```
|
|
1463
|
+
*/
|
|
1464
|
+
bindActivity(binder: TabActivityBinder): Disposable;
|
|
1465
|
+
/**
|
|
1466
|
+
* Bind a provider of **leading icons** for Agent Session tabs — a brand mark
|
|
1467
|
+
* so a tab running an agent is identifiable at a glance. Same routing and
|
|
1468
|
+
* same synchronous-`provide` contract as
|
|
1469
|
+
* {@link AgentsService.bindActivity}.
|
|
1470
|
+
*
|
|
1471
|
+
* Return `null` for "no icon". Take care that a component which renders
|
|
1472
|
+
* nothing produces `null` here rather than a truthy element descriptor, or
|
|
1473
|
+
* the host reserves tab space for an icon that never appears.
|
|
1474
|
+
*/
|
|
1475
|
+
bindIcon(binder: TabIconBinder): Disposable;
|
|
1476
|
+
/**
|
|
1477
|
+
* Re-query every bound {@link AgentsService.bindActivity} /
|
|
1478
|
+
* {@link AgentsService.bindIcon} provider. Call it when something *outside*
|
|
1479
|
+
* the agent snapshot changed what a provider would return — a setting, the
|
|
1480
|
+
* active theme — since the host cannot know about those.
|
|
1481
|
+
*/
|
|
1482
|
+
invalidateAdornments(): void;
|
|
1483
|
+
/**
|
|
1484
|
+
* End an Agent Session, either kind: close its terminal tab, or close the
|
|
1485
|
+
* dock panel that declared `DockPanelApi.setAgentSession` for it
|
|
1486
|
+
* (which reaps the agent process as the panel unmounts).
|
|
1487
|
+
*
|
|
1488
|
+
* A no-op for an unknown id, or for a Chat session with no panel mounted —
|
|
1489
|
+
* Silo will not kill a connection whose UI it cannot account for.
|
|
1490
|
+
*
|
|
1491
|
+
* @param id — an {@link AgentInfo.id}.
|
|
1492
|
+
*/
|
|
1493
|
+
close(id: string): void;
|
|
1494
|
+
/**
|
|
1495
|
+
* Bring an Agent Session into view: focus its terminal tab if it is a
|
|
1496
|
+
* Terminal session, or its transcript panel if it is a Chat session —
|
|
1497
|
+
* activating the owning workspace first. The caller does not need to know
|
|
1498
|
+
* which kind it is, which is the whole point of the verb (RFC 0038): a
|
|
1499
|
+
* consumer holding an {@link AgentInfo.id} should never have to branch to
|
|
1500
|
+
* `ctx.terminals.focus` vs. some chat-panel API.
|
|
1501
|
+
*
|
|
1502
|
+
* A no-op for an unknown id, or when the session's backing surface is gone
|
|
1503
|
+
* (a closed terminal, a disposed panel).
|
|
1504
|
+
*
|
|
1505
|
+
* @param id — an {@link AgentInfo.id}.
|
|
419
1506
|
*/
|
|
420
|
-
|
|
1507
|
+
reveal(id: string): void;
|
|
1508
|
+
/**
|
|
1509
|
+
* Resume a session **through Silo**, when {@link AgentInfo.canResume} is
|
|
1510
|
+
* `true`.
|
|
1511
|
+
*
|
|
1512
|
+
* - Chat session: spawn a fresh agent process and reconnect the persisted
|
|
1513
|
+
* id over `session/resume` or `session/load` — whichever the agent
|
|
1514
|
+
* advertises, `resume` preferred (RFC 0042) — so the transcript and
|
|
1515
|
+
* context come back after the old process died. Then
|
|
1516
|
+
* {@link AgentsService.reveal} it.
|
|
1517
|
+
* - Terminal session: currently a no-op — a dead PTY cannot be re-run in
|
|
1518
|
+
* place, and the resume path stays "the user runs
|
|
1519
|
+
* {@link AgentInfo.resumeCommand}". Present on the surface so a
|
|
1520
|
+
* kind-agnostic consumer can call it unconditionally once Chat sessions
|
|
1521
|
+
* ship (RFC 0038 phase 4).
|
|
1522
|
+
*
|
|
1523
|
+
* A no-op for an unknown id or one whose `canResume` is `false`.
|
|
1524
|
+
*
|
|
1525
|
+
* @param id — an {@link AgentInfo.id}.
|
|
1526
|
+
*/
|
|
1527
|
+
resume(id: string): void;
|
|
421
1528
|
/**
|
|
422
1529
|
* Every coding agent Silo knows about, as read-only
|
|
423
1530
|
* {@link CatalogAgentSummary} records. Detection stays sealed (ADR 0028) —
|
|
@@ -440,4 +1547,15 @@ export interface AgentsService {
|
|
|
440
1547
|
* @beta
|
|
441
1548
|
*/
|
|
442
1549
|
readonly profiles: AgentProfilesService;
|
|
1550
|
+
/**
|
|
1551
|
+
* Connect to and drive a **Chat session** — a user-authored `chat` profile
|
|
1552
|
+
* spawned as an Agent Client Protocol child (RFC 0038 phase 2). See
|
|
1553
|
+
* {@link AgentSessionsService}. Needs the `"agents"` {@link Permission}:
|
|
1554
|
+
* every `connect()` throws without it.
|
|
1555
|
+
*
|
|
1556
|
+
* @category Consumer Services
|
|
1557
|
+
* @public
|
|
1558
|
+
* @beta
|
|
1559
|
+
*/
|
|
1560
|
+
readonly sessions: AgentSessionsService;
|
|
443
1561
|
}
|