@robota-sdk/agent-transport 3.0.0-beta.82 → 3.0.0-beta.84

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 (62) hide show
  1. package/CHANGELOG.md +795 -0
  2. package/README.md +80 -118
  3. package/dist/browser/client.d.ts +2 -2
  4. package/dist/browser/client.js +1 -1
  5. package/dist/browser/index.d.ts +87 -13
  6. package/dist/browser/index.d.ts.map +1 -1
  7. package/dist/browser/index.js +1 -1
  8. package/dist/browser/index.js.map +1 -1
  9. package/dist/browser/observer-messages-CEeisRaF.js +2 -0
  10. package/dist/browser/observer-messages-CEeisRaF.js.map +1 -0
  11. package/dist/browser/observer-messages-Dnff2Tdb.d.ts +471 -0
  12. package/dist/browser/observer-messages-Dnff2Tdb.d.ts.map +1 -0
  13. package/dist/node/index.cjs +1 -1
  14. package/dist/node/index.d.cts +308 -16
  15. package/dist/node/index.d.cts.map +1 -1
  16. package/dist/node/index.d.ts +308 -16
  17. package/dist/node/index.d.ts.map +1 -1
  18. package/dist/node/index.js +1 -1
  19. package/dist/node/index.js.map +1 -1
  20. package/dist/node/node.cjs +1 -1
  21. package/dist/node/node.d.cts +69 -1
  22. package/dist/node/node.d.cts.map +1 -1
  23. package/dist/node/node.d.ts +70 -2
  24. package/dist/node/node.d.ts.map +1 -1
  25. package/dist/node/node.js +1 -1
  26. package/dist/node/node.js.map +1 -1
  27. package/package.json +17 -14
  28. package/src/__tests__/bearer-resource-gate.test.ts +120 -0
  29. package/src/__tests__/history-attached-budget.test.ts +216 -0
  30. package/src/__tests__/message-decoders.test.ts +496 -6
  31. package/src/__tests__/project-read-messages.test.ts +118 -0
  32. package/src/__tests__/session-detail-loop-status.test.ts +169 -0
  33. package/src/__tests__/session-directory-messages.test.ts +322 -0
  34. package/src/__tests__/session-error-classification.test.ts +113 -0
  35. package/src/__tests__/session-history-wire.test.ts +342 -0
  36. package/src/__tests__/session-message-handler-observe.test.ts +217 -0
  37. package/src/__tests__/session-message-handler.test.ts +139 -4
  38. package/src/__tests__/settings-messages.test.ts +193 -0
  39. package/src/__tests__/ws-broadcast-events.test.ts +10 -0
  40. package/src/client.ts +9 -1
  41. package/src/index.ts +18 -2
  42. package/src/loop-control-messages.ts +34 -0
  43. package/src/message-decoders.ts +238 -6
  44. package/src/node/bearer-resource-gate.ts +291 -0
  45. package/src/node/index.ts +13 -0
  46. package/src/observer-messages.ts +47 -0
  47. package/src/open-prompts.ts +76 -0
  48. package/src/outbound-delivery.ts +11 -2
  49. package/src/project-read-messages.ts +74 -0
  50. package/src/protocol-session.ts +23 -1
  51. package/src/session-directory-messages.ts +162 -0
  52. package/src/session-error-classification.ts +91 -0
  53. package/src/session-events.ts +100 -19
  54. package/src/session-message-handler.ts +110 -15
  55. package/src/session-query-messages.ts +123 -5
  56. package/src/session-resume-bridge.ts +8 -0
  57. package/src/settings-messages.ts +103 -0
  58. package/src/wire-messages.ts +238 -11
  59. package/dist/browser/message-decoders-BB8n-H2X.d.ts +0 -252
  60. package/dist/browser/message-decoders-BB8n-H2X.d.ts.map +0 -1
  61. package/dist/browser/message-decoders-CDmDoZPR.js +0 -2
  62. package/dist/browser/message-decoders-CDmDoZPR.js.map +0 -1
@@ -0,0 +1,103 @@
1
+ import type { TOutboundDeliver } from './outbound-delivery.js';
2
+ import type { IProtocolSession } from './protocol-session.js';
3
+ import type { TClientMessage } from './wire-messages.js';
4
+ import type { TCommandSurfaceLocality } from '@robota-sdk/agent-interface-command';
5
+ import type { ISettingsSnapshot, TSettingsPatch } from '@robota-sdk/agent-interface-session';
6
+
7
+ type TSettingsMessage = Extract<TClientMessage, { type: 'get-settings' | 'update-settings' }>;
8
+
9
+ export function isSettingsMessage(msg: TClientMessage): msg is TSettingsMessage {
10
+ return msg.type === 'get-settings' || msg.type === 'update-settings';
11
+ }
12
+
13
+ /** What applying a patch did: the fresh snapshot, or why nothing was written. */
14
+ export type TSettingsUpdateOutcome =
15
+ | { readonly ok: true; readonly settings: ISettingsSnapshot }
16
+ | {
17
+ readonly ok: false;
18
+ readonly code: 'not_available' | 'invalid' | 'refused' | 'update_failed';
19
+ readonly message: string;
20
+ };
21
+
22
+ /**
23
+ * #3282 §4a: host-owned read/write for the Settings screen, supplied by the composition root the
24
+ * same way `IUsageQueryReporters` supplies usage reads — this package only correlates the request
25
+ * and carries the result. `updateSettings` applies through the SAME function the matching slash
26
+ * command uses (issue #3282 §4: "one code path, so the behavior matches the TUI"), so whatever a
27
+ * command would refuse on this surface is refused here too.
28
+ */
29
+ export interface ISettingsReporter {
30
+ getSettings(
31
+ session: IProtocolSession,
32
+ locality?: TCommandSurfaceLocality,
33
+ ): ISettingsSnapshot | Promise<ISettingsSnapshot>;
34
+ /**
35
+ * Applies through the SAME function the matching slash command uses. `locality` (#3282 §4 part
36
+ * b-2) is forwarded to that command exactly as the transport received it, so a command that
37
+ * refuses a remote surface (installing a plugin) refuses identically here.
38
+ */
39
+ updateSettings(
40
+ session: IProtocolSession,
41
+ patch: TSettingsPatch,
42
+ locality?: TCommandSurfaceLocality,
43
+ ): TSettingsUpdateOutcome | Promise<TSettingsUpdateOutcome>;
44
+ }
45
+
46
+ export function handleSettingsMessage(
47
+ session: IProtocolSession,
48
+ deliver: TOutboundDeliver,
49
+ msg: TSettingsMessage,
50
+ reporter: ISettingsReporter | undefined,
51
+ locality?: TCommandSurfaceLocality,
52
+ ): void {
53
+ if (!reporter) {
54
+ deliverSettingsError(
55
+ deliver,
56
+ msg.requestId,
57
+ 'not_available',
58
+ 'Settings are not available on this host.',
59
+ );
60
+ return;
61
+ }
62
+ try {
63
+ if (msg.type === 'get-settings') {
64
+ void Promise.resolve(reporter.getSettings(session, locality)).then(
65
+ (settings) => deliver({ type: 'settings', requestId: msg.requestId, settings }),
66
+ (error) =>
67
+ deliverSettingsError(deliver, msg.requestId, 'update_failed', failureMessage(error)),
68
+ );
69
+ return;
70
+ }
71
+ void Promise.resolve(reporter.updateSettings(session, msg.patch, locality)).then(
72
+ (outcome) => {
73
+ if (outcome.ok) {
74
+ deliver({ type: 'settings', requestId: msg.requestId, settings: outcome.settings });
75
+ } else {
76
+ deliverSettingsError(deliver, msg.requestId, outcome.code, outcome.message);
77
+ }
78
+ },
79
+ (error) =>
80
+ deliverSettingsError(deliver, msg.requestId, 'update_failed', failureMessage(error)),
81
+ );
82
+ } catch (error) {
83
+ deliverSettingsError(
84
+ deliver,
85
+ msg.requestId,
86
+ 'update_failed',
87
+ failureMessage(error instanceof Error ? error : String(error)),
88
+ );
89
+ }
90
+ }
91
+
92
+ function deliverSettingsError(
93
+ deliver: TOutboundDeliver,
94
+ requestId: string,
95
+ code: 'not_available' | 'invalid' | 'refused' | 'update_failed',
96
+ message: string,
97
+ ): void {
98
+ deliver({ type: 'settings_error', requestId, code, message });
99
+ }
100
+
101
+ function failureMessage(error: Error | string): string {
102
+ return error instanceof Error ? error.message : String(error);
103
+ }
@@ -3,9 +3,15 @@ import type {
3
3
  IPersonalUsageRequest,
4
4
  IUsageBySourceReport,
5
5
  } from '@robota-sdk/agent-interface-analytics';
6
- import type { ICommandResult } from '@robota-sdk/agent-interface-command';
6
+ import type {
7
+ ICommandListEntry,
8
+ ICommandResult,
9
+ ICommandSkillListEntry,
10
+ } from '@robota-sdk/agent-interface-command';
7
11
  import type {
8
12
  IBackgroundJobGroupState,
13
+ IExecutionDetailCursor,
14
+ IExecutionDetailPage,
9
15
  IExecutionWorkspaceSnapshot,
10
16
  TBackgroundJobGroupEvent,
11
17
  } from '@robota-sdk/agent-interface-execution';
@@ -22,27 +28,103 @@ import type {
22
28
  IBranchEvent,
23
29
  IExecutionResult,
24
30
  IContextFileRefreshedEvent,
31
+ IModelListGroup,
25
32
  IPermissionRequestEvent,
26
33
  IPromptResolvedEvent,
27
34
  IPlanApprovalEvent,
28
35
  ISessionRenamedEvent,
36
+ ISettingsSnapshot,
29
37
  IToolState,
38
+ ISessionListing,
39
+ ISessionStatusSnapshot,
40
+ ISessionSwitchedEvent,
41
+ TSessionChangeRefusalCode,
42
+ TSessionDeleteRefusalCode,
43
+ TSettingsPatch,
30
44
  IUiIntentEvent,
31
45
  TPermissionResultValue,
46
+ TProjectDiffRead,
47
+ TProjectMemoryRead,
48
+ TProjectStatusRead,
49
+ TWaitingLoopStopOutcome,
50
+ } from '@robota-sdk/agent-interface-session';
51
+ import type {
52
+ ISessionConversationRead,
53
+ TDriverId,
54
+ TTurnSource,
32
55
  } from '@robota-sdk/agent-interface-session';
33
- import type { ISessionConversationRead, TDriverId } from '@robota-sdk/agent-interface-session';
34
56
  import type { TActionResponse } from '@robota-sdk/agent-interface-transport';
35
57
 
36
58
  export type TBackgroundControlAction = 'cancel' | 'close' | 'send';
37
59
 
60
+ type THistoryEntry = ReturnType<ISessionConversationRead['getFullHistory']>[number];
61
+
62
+ /**
63
+ * #3282 §4: one agent the switcher can offer. `definedIn` is a plain-words location — a discovered
64
+ * file's path, or "Built-in" — never a raw internal id.
65
+ */
66
+ export interface IWireAgentDefinitionSummary {
67
+ name: string;
68
+ description: string;
69
+ definedIn: string;
70
+ }
71
+
72
+ /**
73
+ * #3189: one entry of the session's full history as it crosses the wire. The same record the session
74
+ * keeps, except that `timestamp` is an ISO 8601 string: a `Date` does not survive JSON, and a
75
+ * declared `Date` that arrives as a string would let a client call `Date` methods on a string.
76
+ */
77
+ export interface IWireHistoryEntry extends Omit<THistoryEntry, 'timestamp'> {
78
+ /** ISO 8601. */
79
+ timestamp: string;
80
+ }
81
+
82
+ /**
83
+ * #3189: a turn's result as it crosses the wire. The session's whole history stays behind: it only
84
+ * grows, so a client reads it in bounded pages with `get-history` instead.
85
+ */
86
+ export type TWireExecutionResult = Omit<IExecutionResult, 'history'>;
87
+
38
88
  /** Inbound message from client to server. */
39
89
  export type TClientMessage =
40
90
  | { type: 'submit'; prompt: string }
41
- | { type: 'command'; name: string; args?: string }
91
+ // `requestId` is echoed on the command's `command_result` or `protocol_error`, so a client with
92
+ // several commands in flight knows which one each answer settles.
93
+ | { type: 'command'; name: string; args?: string; requestId?: string }
42
94
  | { type: 'abort' }
43
95
  | { type: 'cancel-queue' }
44
96
  | { type: 'get-messages' }
97
+ // #3189: the session's full history — what a client that renders the whole session (the TUI) shows —
98
+ // one bounded page at a time, from `fromIndex` (default 0). The client asks for the next page
99
+ // after the previous one arrived, so replies never pile up in the connection.
100
+ | { type: 'get-history'; fromIndex?: number }
101
+ // #3189: the permission and ask prompts open now, each sent again as the frame that asked it: a
102
+ // client that attached after a prompt was asked has not seen it.
103
+ | { type: 'get-prompts' }
45
104
  | { type: 'get-context' }
105
+ // #3186: what every client needs beside the conversation — the commands and skills it can offer
106
+ // (a `/` menu), and the session's status (model, permission mode, effort, context).
107
+ | { type: 'get-commands' }
108
+ | { type: 'get-status' }
109
+ // #3282 §2: the models the GUI's model menu offers, grouped by configured provider profile — so
110
+ // the GUI never scrapes `/model`'s picker text. Choosing one sends `command` with `name: 'model'`,
111
+ // the SAME path `/model <id>` runs; this message only reads the choices.
112
+ | { type: 'list-models'; requestId: string }
113
+ // #3282 §4: the agent switcher's roster and its currently-selected default — so the GUI never
114
+ // scrapes `/agent`'s text. Choosing one sends `command` with `name: 'agent'`, the SAME path
115
+ // `/agent <name>` runs; this message only reads the roster and the current selection.
116
+ | { type: 'get-agent-definitions'; requestId: string }
117
+ // #3189: the host's sessions — list them, start a new one, make another current. A refused change
118
+ // answers `session_change_failed` with the same `requestId`.
119
+ | { type: 'list-sessions'; requestId: string }
120
+ | { type: 'new-session'; requestId?: string }
121
+ | { type: 'switch-session'; sessionId: string; requestId?: string }
122
+ // #3289 §1: rename a session from the list — current or not. Answered by `session_renamed_in_list`
123
+ // or `session_rename_failed`, both carrying the same `requestId`.
124
+ | { type: 'rename-session'; sessionId: string; name: string; requestId: string }
125
+ // #3289 §1: remove a stored session's record for good. Answered by `session_deleted` or
126
+ // `session_delete_failed`, both carrying the same `requestId`.
127
+ | { type: 'delete-session'; sessionId: string; requestId: string }
46
128
  // SELFHOST-004: request the assembled trace/cost read-model (spans + cost-by-source) for the run.
47
129
  | { type: 'get-usage-report' }
48
130
  | {
@@ -55,6 +137,26 @@ export type TClientMessage =
55
137
  | { type: 'get-executing' }
56
138
  | { type: 'get-pending' }
57
139
  | { type: 'get-execution-workspace' }
140
+ // One page of what a workspace entry recorded, from `cursor` (the previous page's `nextCursor`).
141
+ // Answered by `execution_detail` or `execution_detail_error` with the same `requestId`.
142
+ | {
143
+ type: 'read-execution-detail';
144
+ requestId: string;
145
+ entryId: string;
146
+ cursor?: IExecutionDetailCursor;
147
+ }
148
+ // Stop the self-paced loop that is waiting for its next wake; answered by `waiting_loop_stop`.
149
+ | { type: 'stop-waiting-loop'; requestId: string }
150
+ // #3282 §4c: the Project panel's "Changes" section — this workspace's git status. Answered by
151
+ // `project_status`, echoing `requestId`.
152
+ | { type: 'project-status'; requestId: string }
153
+ // The Project panel's "File diff" section for one file `project_status` reported — `path` exactly
154
+ // as reported (workspace-relative; the server re-checks it stays inside the workspace). Answered by
155
+ // `project_diff`.
156
+ | { type: 'project-diff'; requestId: string; path: string }
157
+ // The Project panel's "Memory" section — project memory as readable text. Answered by
158
+ // `project_memory`.
159
+ | { type: 'project-memory'; requestId: string }
58
160
  | { type: 'get-background-tasks'; filter?: IBackgroundTaskListFilter }
59
161
  | { type: 'get-background-task'; taskId: string }
60
162
  | { type: 'get-background-job-groups' }
@@ -70,7 +172,12 @@ export type TClientMessage =
70
172
  // REMOTE-013 E4 session-resume: `resume` asks the host to replay the tail after `lastSeq` (the last seq the
71
173
  // client applied); `ack` lets the host free its un-acked buffer up to `seq`. Only meaningful post-E3-accept.
72
174
  | { type: 'resume'; lastSeq: number }
73
- | { type: 'ack'; seq: number };
175
+ | { type: 'ack'; seq: number }
176
+ // #3282 §4a: the GUI Settings screen. A snapshot fetch and a discriminated single-field patch,
177
+ // never command text — the server applies a patch through the same function its slash command
178
+ // uses, so the two paths cannot drift. Answered by `settings` or `settings_error`.
179
+ | { type: 'get-settings'; requestId: string }
180
+ | { type: 'update-settings'; requestId: string; patch: TSettingsPatch };
74
181
 
75
182
  /** Outbound message from server to client. */
76
183
  export type TServerMessage =
@@ -82,18 +189,116 @@ export type TServerMessage =
82
189
  | { type: 'tool_start'; state: IToolState; driverId?: TDriverId }
83
190
  | { type: 'tool_end'; state: IToolState; driverId?: TDriverId }
84
191
  | { type: 'thinking'; isThinking: boolean; driverId?: TDriverId }
85
- | { type: 'complete'; result: IExecutionResult; driverId?: TDriverId }
86
- | { type: 'interrupted'; result: IExecutionResult; driverId?: TDriverId }
87
- | { type: 'error'; message: string; driverId?: TDriverId }
192
+ | { type: 'complete'; result: TWireExecutionResult; driverId?: TDriverId }
193
+ | { type: 'interrupted'; result: TWireExecutionResult; driverId?: TDriverId }
194
+ | {
195
+ type: 'error';
196
+ /** The raw failure text — kept for a "Details" disclosure; never the whole story on its own. */
197
+ message: string;
198
+ /**
199
+ * What kind of failure this was, when it's known — so a renderer can say what happened and
200
+ * what to do next in plain words instead of showing `message` verbatim (#3289 §3). Additive:
201
+ * absent for a session error this classification does not recognize (unchanged behavior).
202
+ */
203
+ code?: 'auth' | 'rate_limit' | 'model_unavailable' | 'network' | 'provider';
204
+ /** The provider/profile name the failure came from, when `code` names one. */
205
+ provider?: string;
206
+ /** For `code: 'rate_limit'`, how long until a retry may succeed, when the provider said so. */
207
+ retryAfterSeconds?: number;
208
+ /**
209
+ * For `code: 'model_unavailable'`, the model the failed request tried — captured at the moment
210
+ * of failure so a renderer never has to fall back to whatever model is live by the time it
211
+ * draws this notice (which may already be a different one the person switched to).
212
+ */
213
+ model?: string;
214
+ driverId?: TDriverId;
215
+ }
88
216
  | {
89
217
  type: 'command_result';
90
218
  name: string;
91
219
  message: string;
92
220
  success: boolean;
93
221
  data?: ICommandResult['data'];
222
+ /** The `requestId` of the `command` this answers, when it carried one. */
223
+ requestId?: string;
224
+ }
225
+ | {
226
+ type: 'messages';
227
+ messages: ReturnType<ISessionConversationRead['getMessages']>;
228
+ /**
229
+ * #3288 §2: the SAME history, projected into display segments (text runs and finished tool
230
+ * calls, diffs included) — so a reload/reconnect/resume replay shows tool rows instead of bare
231
+ * text. Optional so an older host that has not grown `getMessagesDisplay()` yet still sends a
232
+ * valid frame; a client without this field falls back to `messages`-only (text bubbles), the
233
+ * same as before this existed.
234
+ */
235
+ display?: ReturnType<ISessionConversationRead['getMessagesDisplay']>;
236
+ /**
237
+ * #3289 §3: this connection's own server-assigned driver id, carried on the first frame every
238
+ * connection already gets — so a client can tell its OWN turns from a co-driver's instead of
239
+ * treating everything but the literal `'owner'` id as someone else (REMOTE-014 E5 display-only).
240
+ */
241
+ driverId?: TDriverId;
94
242
  }
95
- | { type: 'messages'; messages: ReturnType<ISessionConversationRead['getMessages']> }
243
+ // #3189: one page of the full history. `entries` start at `startIndex` of the `total` the session
244
+ // holds now, and stop before a page grows past a bounded size (a single larger entry is sent alone).
245
+ | { type: 'history'; startIndex: number; total: number; entries: IWireHistoryEntry[] }
246
+ // Sent in reply to `get-context`, and pushed whenever the session's context window changes.
96
247
  | { type: 'context'; state: ReturnType<ISessionConversationRead['getContextState']> }
248
+ // #3189: the full history gained entries that no streamed frame carries (a compaction, a skill
249
+ // activation, a memory event). A client that shows the full history re-reads it with `get-history`.
250
+ | { type: 'history_changed' }
251
+ // #3189: where the turn now starting came from (the user, a wake-up, a peer, an external event).
252
+ | { type: 'turn_source'; source: TTurnSource }
253
+ | { type: 'commands'; commands: ICommandListEntry[]; skills: ICommandSkillListEntry[] }
254
+ // Sent in reply to `get-status`, and pushed whenever the session's status changes.
255
+ | { type: 'session_status'; status: ISessionStatusSnapshot }
256
+ // #3282 §2: reply to `list-models`, echoing its `requestId`. `groups` is current-profile first;
257
+ // `currentProfile` is absent only when no provider profile is configured at all.
258
+ | {
259
+ type: 'model_list';
260
+ requestId: string;
261
+ groups: readonly IModelListGroup[];
262
+ currentProfile?: string;
263
+ currentModel: string;
264
+ }
265
+ // #3282 §4: reply to `get-agent-definitions`, echoing its `requestId`. `current` is always one of
266
+ // `agents`' names (the fallback default when nothing was ever selected).
267
+ | {
268
+ type: 'agent_definitions';
269
+ requestId: string;
270
+ agents: readonly IWireAgentDefinitionSummary[];
271
+ current: string;
272
+ }
273
+ | { type: 'sessions'; requestId: string; listing: ISessionListing }
274
+ | {
275
+ type: 'sessions_error';
276
+ requestId: string;
277
+ code: 'not_available' | 'list_failed';
278
+ message: string;
279
+ }
280
+ // This client's session is now another one; it re-reads what it shows. A host that binds each
281
+ // client to its own session sends it only to the client that moved.
282
+ | { type: 'session_switched'; event: ISessionSwitchedEvent }
283
+ // #3189: a new-session or switch-session was refused; `code` says why without parsing `message`.
284
+ | {
285
+ type: 'session_change_failed';
286
+ code: TSessionChangeRefusalCode;
287
+ message: string;
288
+ requestId?: string;
289
+ }
290
+ // #3289 §1: a `rename-session` succeeded (current session or not); the client refreshes its listing.
291
+ | { type: 'session_renamed_in_list'; requestId: string; sessionId: string; name: string }
292
+ | { type: 'session_rename_failed'; requestId: string; message: string }
293
+ // #3289 §1: a `delete-session` succeeded; the client refreshes its listing and, if it was showing
294
+ // the deleted session, re-reads what it now shows (the host has already switched it away).
295
+ | { type: 'session_deleted'; requestId: string; sessionId: string }
296
+ | {
297
+ type: 'session_delete_failed';
298
+ requestId: string;
299
+ code: TSessionDeleteRefusalCode;
300
+ message: string;
301
+ }
97
302
  // SELFHOST-004 (P5, TC-08): carry the assembled trace/cost read-model (per-op span timeline +
98
303
  // cost-by-source) across the sidecar boundary — no existing variant carries per-op `durationMs` or
99
304
  // per-source `costUsd`. The GUI renders it renderer-side.
@@ -119,8 +324,20 @@ export type TServerMessage =
119
324
  message: string;
120
325
  }
121
326
  | { type: 'executing'; executing: boolean }
122
- | { type: 'pending'; pending: string | null }
327
+ // The next queued prompt and how many wait. Sent in reply to `get-pending`, and to a `submit` once
328
+ // the host has taken the prompt (queued it behind a running turn, or run it).
329
+ | { type: 'pending'; pending: string | null; pendingCount?: number }
123
330
  | { type: 'execution_workspace_event'; snapshot: IExecutionWorkspaceSnapshot }
331
+ | { type: 'execution_detail'; requestId: string; page: IExecutionDetailPage }
332
+ | { type: 'execution_detail_error'; requestId: string; message: string }
333
+ | { type: 'waiting_loop_stop'; requestId: string; outcome: TWaitingLoopStopOutcome }
334
+ // #3282 §4c: the Project panel's reads. Each result crosses the wire UNCHANGED as the session
335
+ // method's own return value (same convention as `waiting_loop_stop`'s `outcome` above) — the
336
+ // `kind` discriminant carries the non-repository / outside-workspace / unavailable / failed cases,
337
+ // so there is no separate `_error` sibling to keep in sync with these.
338
+ | { type: 'project_status'; requestId: string; result: TProjectStatusRead }
339
+ | { type: 'project_diff'; requestId: string; result: TProjectDiffRead }
340
+ | { type: 'project_memory'; requestId: string; result: TProjectMemoryRead }
124
341
  | { type: 'background_task_event'; event: TBackgroundTaskEvent }
125
342
  | { type: 'background_job_group_event'; event: TBackgroundJobGroupEvent }
126
343
  | { type: 'plan_event'; event: IPlanApprovalEvent }
@@ -151,10 +368,20 @@ export type TServerMessage =
151
368
  success: boolean;
152
369
  message?: string;
153
370
  }
154
- | { type: 'protocol_error'; message: string }
371
+ // `requestId` names the `command` whose failure this reports; a refusal of anything else has none.
372
+ | { type: 'protocol_error'; message: string; requestId?: string }
155
373
  // REMOTE-013 E4: sent instead of a replay when the client's `lastSeq` predates the host's retained buffer
156
374
  // (overrun) — the client must do a full `get-messages` refresh rather than accept a silent gap.
157
- | { type: 'resume_gap' };
375
+ | { type: 'resume_gap' }
376
+ // #3282 §4a: the Settings screen's snapshot, in reply to `get-settings` or `update-settings`. The
377
+ // same type answers both, the way `session_status` answers `get-status` and a status change alike.
378
+ | { type: 'settings'; requestId: string; settings: ISettingsSnapshot }
379
+ | {
380
+ type: 'settings_error';
381
+ requestId: string;
382
+ code: 'not_available' | 'invalid' | 'refused' | 'update_failed';
383
+ message: string;
384
+ };
158
385
 
159
386
  /**
160
387
  * REMOTE-013 E4: a server message stamped with its monotonic session sequence number (added by the
@@ -1,252 +0,0 @@
1
- import { IPersonalUsageReport, IPersonalUsageRequest, IUsageBySourceReport } from "@robota-sdk/agent-interface-analytics";
2
- import { ICommandResult } from "@robota-sdk/agent-interface-command";
3
- import { IBackgroundJobGroupState, IBackgroundTaskInput, IBackgroundTaskListFilter, IBackgroundTaskLogCursor, IBackgroundTaskLogPage, IBackgroundTaskState, IExecutionWorkspaceSnapshot, TBackgroundJobGroupEvent, TBackgroundTaskEvent } from "@robota-sdk/agent-interface-execution";
4
- import { IAskRequestEvent, IBranchEvent, IContextFileRefreshedEvent, IExecutionResult, IPermissionRequestEvent, IPlanApprovalEvent, IPromptResolvedEvent, ISessionConversationRead, ISessionRenamedEvent, IToolState, IUiIntentEvent, TDriverId, TPermissionResultValue } from "@robota-sdk/agent-interface-session";
5
- import { TActionResponse } from "@robota-sdk/agent-interface-transport";
6
- //#region src/wire-messages.d.ts
7
- type TBackgroundControlAction = 'cancel' | 'close' | 'send';
8
- /** Inbound message from client to server. */
9
- type TClientMessage = {
10
- type: 'submit';
11
- prompt: string;
12
- } | {
13
- type: 'command';
14
- name: string;
15
- args?: string;
16
- } | {
17
- type: 'abort';
18
- } | {
19
- type: 'cancel-queue';
20
- } | {
21
- type: 'get-messages';
22
- } | {
23
- type: 'get-context';
24
- } | {
25
- type: 'get-usage-report';
26
- } | {
27
- type: 'get-personal-usage-report';
28
- requestId: string;
29
- period: IPersonalUsageRequest['period'];
30
- timezone: string;
31
- } | {
32
- type: 'get-stored-session-usage-report';
33
- requestId: string;
34
- sessionId: string;
35
- } | {
36
- type: 'get-executing';
37
- } | {
38
- type: 'get-pending';
39
- } | {
40
- type: 'get-execution-workspace';
41
- } | {
42
- type: 'get-background-tasks';
43
- filter?: IBackgroundTaskListFilter;
44
- } | {
45
- type: 'get-background-task';
46
- taskId: string;
47
- } | {
48
- type: 'get-background-job-groups';
49
- } | {
50
- type: 'get-background-job-group';
51
- groupId: string;
52
- } | {
53
- type: 'wait-background-job-group';
54
- groupId: string;
55
- } | {
56
- type: 'cancel-background-task';
57
- taskId: string;
58
- reason?: string;
59
- } | {
60
- type: 'close-background-task';
61
- taskId: string;
62
- } | {
63
- type: 'send-background-task';
64
- taskId: string;
65
- input: IBackgroundTaskInput;
66
- } | {
67
- type: 'read-background-task-log';
68
- taskId: string;
69
- cursor?: IBackgroundTaskLogCursor;
70
- } | {
71
- type: 'permission-response';
72
- id: string;
73
- result: TPermissionResultValue;
74
- } | {
75
- type: 'ask-response';
76
- id: string;
77
- response: TActionResponse;
78
- } | {
79
- type: 'resume';
80
- lastSeq: number;
81
- } | {
82
- type: 'ack';
83
- seq: number;
84
- };
85
- /** Outbound message from server to client. */
86
- type TServerMessage = {
87
- type: 'text_delta';
88
- delta: string;
89
- driverId?: TDriverId;
90
- } | {
91
- type: 'user_message';
92
- content: string;
93
- driverId?: TDriverId;
94
- } | {
95
- type: 'tool_start';
96
- state: IToolState;
97
- driverId?: TDriverId;
98
- } | {
99
- type: 'tool_end';
100
- state: IToolState;
101
- driverId?: TDriverId;
102
- } | {
103
- type: 'thinking';
104
- isThinking: boolean;
105
- driverId?: TDriverId;
106
- } | {
107
- type: 'complete';
108
- result: IExecutionResult;
109
- driverId?: TDriverId;
110
- } | {
111
- type: 'interrupted';
112
- result: IExecutionResult;
113
- driverId?: TDriverId;
114
- } | {
115
- type: 'error';
116
- message: string;
117
- driverId?: TDriverId;
118
- } | {
119
- type: 'command_result';
120
- name: string;
121
- message: string;
122
- success: boolean;
123
- data?: ICommandResult['data'];
124
- } | {
125
- type: 'messages';
126
- messages: ReturnType<ISessionConversationRead['getMessages']>;
127
- } | {
128
- type: 'context';
129
- state: ReturnType<ISessionConversationRead['getContextState']>;
130
- } | {
131
- type: 'usage_report';
132
- report: IUsageBySourceReport;
133
- } | {
134
- type: 'personal_usage_report';
135
- requestId: string;
136
- report: IPersonalUsageReport;
137
- } | {
138
- type: 'personal_usage_report_error';
139
- requestId: string;
140
- code: 'not_available' | 'report_failed';
141
- message: string;
142
- } | {
143
- type: 'stored_session_usage_report';
144
- requestId: string;
145
- sessionId: string;
146
- report: IUsageBySourceReport;
147
- } | {
148
- type: 'stored_session_usage_report_error';
149
- requestId: string;
150
- sessionId: string;
151
- code: 'not_available' | 'report_failed';
152
- message: string;
153
- } | {
154
- type: 'executing';
155
- executing: boolean;
156
- } | {
157
- type: 'pending';
158
- pending: string | null;
159
- } | {
160
- type: 'execution_workspace_event';
161
- snapshot: IExecutionWorkspaceSnapshot;
162
- } | {
163
- type: 'background_task_event';
164
- event: TBackgroundTaskEvent;
165
- } | {
166
- type: 'background_job_group_event';
167
- event: TBackgroundJobGroupEvent;
168
- } | {
169
- type: 'plan_event';
170
- event: IPlanApprovalEvent;
171
- } | {
172
- type: 'context_file_refreshed';
173
- event: IContextFileRefreshedEvent;
174
- } | {
175
- type: 'branch_event';
176
- event: IBranchEvent;
177
- } | {
178
- type: 'background_tasks';
179
- tasks: IBackgroundTaskState[];
180
- } | {
181
- type: 'background_task';
182
- taskId: string;
183
- task: IBackgroundTaskState | null;
184
- } | {
185
- type: 'background_job_groups';
186
- groups: IBackgroundJobGroupState[];
187
- } | {
188
- type: 'background_job_group';
189
- groupId: string;
190
- group: IBackgroundJobGroupState | null;
191
- } | {
192
- type: 'background_task_log';
193
- taskId: string;
194
- page: IBackgroundTaskLogPage;
195
- } | {
196
- type: 'permission_request';
197
- event: IPermissionRequestEvent;
198
- } | {
199
- type: 'ask_request';
200
- event: IAskRequestEvent;
201
- } | {
202
- type: 'prompt_resolved';
203
- event: IPromptResolvedEvent;
204
- } | {
205
- type: 'ui_intent';
206
- event: IUiIntentEvent;
207
- } | {
208
- type: 'session_renamed';
209
- event: ISessionRenamedEvent;
210
- } | {
211
- type: 'history_cleared';
212
- } | {
213
- type: 'background_task_control_result';
214
- action: TBackgroundControlAction;
215
- taskId: string;
216
- success: boolean;
217
- message?: string;
218
- } | {
219
- type: 'protocol_error';
220
- message: string;
221
- } | {
222
- type: 'resume_gap';
223
- };
224
- /**
225
- * REMOTE-013 E4: a server message stamped with its monotonic session sequence number (added by the
226
- * {@link SessionResumeBridge} on the reconnectable WebRTC path). Intersecting over the union distributes the
227
- * `seq` field onto every variant. The WS localhost path never stamps it (a `type`-dispatching client ignores
228
- * an absent/extra `seq`).
229
- */
230
- type TSeqServerMessage = TServerMessage & {
231
- seq: number;
232
- };
233
- //#endregion
234
- //#region src/message-decoders.d.ts
235
- type TMessageDecodeResult<TMessage> = {
236
- readonly ok: true;
237
- readonly message: TMessage;
238
- } | {
239
- readonly ok: false;
240
- readonly reason: string;
241
- };
242
- /** Serialized frames larger than this are refused before any parse (a bounded inbound budget). */
243
- declare const MAX_INBOUND_FRAME_BYTES: number;
244
- /** Total decode of a parsed client frame. */
245
- declare function decodeClientMessage(value: unknown): TMessageDecodeResult<TClientMessage>;
246
- /** Variant-level decode of a parsed server frame (foreign-owned payloads as records, see header). */
247
- declare function decodeServerMessage(value: unknown): TMessageDecodeResult<TServerMessage>;
248
- /** `raw string → JSON → owner decoder`, the one path a carrier implements. */
249
- declare function decodeFrame<TMessage>(data: string, decode: (value: unknown) => TMessageDecodeResult<TMessage>): TMessageDecodeResult<TMessage>;
250
- //#endregion
251
- export { decodeServerMessage as a, TServerMessage as c, decodeFrame as i, TMessageDecodeResult as n, TClientMessage as o, decodeClientMessage as r, TSeqServerMessage as s, MAX_INBOUND_FRAME_BYTES as t };
252
- //# sourceMappingURL=message-decoders-BB8n-H2X.d.ts.map