@nuvin/session 0.1.0-rc.5

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 (72) hide show
  1. package/dist/chunk-3IPZO7LP.js +66 -0
  2. package/dist/chunk-7G3R25NS.js +6 -0
  3. package/dist/chunk-DGC7JSCG.js +0 -0
  4. package/dist/chunk-FR3R66RD.js +678 -0
  5. package/dist/chunk-UVJIG4DT.js +17 -0
  6. package/dist/client/directory.d.ts +141 -0
  7. package/dist/client/directory.d.ts.map +1 -0
  8. package/dist/client/endpoint.d.ts +33 -0
  9. package/dist/client/endpoint.d.ts.map +1 -0
  10. package/dist/client/index.d.ts +8 -0
  11. package/dist/client/index.d.ts.map +1 -0
  12. package/dist/client/index.js +1060 -0
  13. package/dist/client/session-client.d.ts +60 -0
  14. package/dist/client/session-client.d.ts.map +1 -0
  15. package/dist/client/socket.d.ts +38 -0
  16. package/dist/client/socket.d.ts.map +1 -0
  17. package/dist/client/transport.d.ts +9 -0
  18. package/dist/client/transport.d.ts.map +1 -0
  19. package/dist/client/uds-socket.d.ts +23 -0
  20. package/dist/client/uds-socket.d.ts.map +1 -0
  21. package/dist/client/websocket.d.ts +54 -0
  22. package/dist/client/websocket.d.ts.map +1 -0
  23. package/dist/controller/agent-channel.d.ts +58 -0
  24. package/dist/controller/agent-channel.d.ts.map +1 -0
  25. package/dist/controller/index.d.ts +3 -0
  26. package/dist/controller/index.d.ts.map +1 -0
  27. package/dist/controller/index.js +441 -0
  28. package/dist/controller/session-controller.d.ts +147 -0
  29. package/dist/controller/session-controller.d.ts.map +1 -0
  30. package/dist/controller/test-utils.d.ts +15 -0
  31. package/dist/controller/test-utils.d.ts.map +1 -0
  32. package/dist/grant/index.d.ts +33 -0
  33. package/dist/grant/index.d.ts.map +1 -0
  34. package/dist/grant/index.js +12 -0
  35. package/dist/protocol/index.d.ts +3 -0
  36. package/dist/protocol/index.d.ts.map +1 -0
  37. package/dist/protocol/index.js +7 -0
  38. package/dist/protocol/types.d.ts +855 -0
  39. package/dist/protocol/types.d.ts.map +1 -0
  40. package/dist/protocol/version.d.ts +8 -0
  41. package/dist/protocol/version.d.ts.map +1 -0
  42. package/dist/state/approvals.d.ts +34 -0
  43. package/dist/state/approvals.d.ts.map +1 -0
  44. package/dist/state/dir-access.d.ts +9 -0
  45. package/dist/state/dir-access.d.ts.map +1 -0
  46. package/dist/state/index.d.ts +6 -0
  47. package/dist/state/index.d.ts.map +1 -0
  48. package/dist/state/index.js +48 -0
  49. package/dist/state/json.d.ts +7 -0
  50. package/dist/state/json.d.ts.map +1 -0
  51. package/dist/state/messages.d.ts +105 -0
  52. package/dist/state/messages.d.ts.map +1 -0
  53. package/dist/state/session.d.ts +9 -0
  54. package/dist/state/session.d.ts.map +1 -0
  55. package/dist/state/tool-preview.d.ts +31 -0
  56. package/dist/state/tool-preview.d.ts.map +1 -0
  57. package/dist/state/tool-preview.js +300 -0
  58. package/dist/state/workflow-view.d.ts +10 -0
  59. package/dist/state/workflow-view.d.ts.map +1 -0
  60. package/dist/test-utils/fake-relay.d.ts +24 -0
  61. package/dist/test-utils/fake-relay.d.ts.map +1 -0
  62. package/dist/test-utils/index.d.ts +2 -0
  63. package/dist/test-utils/index.d.ts.map +1 -0
  64. package/dist/test-utils/index.js +250 -0
  65. package/dist/ui/history-grouping.d.ts +36 -0
  66. package/dist/ui/history-grouping.d.ts.map +1 -0
  67. package/dist/ui/index.d.ts +3 -0
  68. package/dist/ui/index.d.ts.map +1 -0
  69. package/dist/ui/index.js +135 -0
  70. package/dist/ui/picker-rows.d.ts +62 -0
  71. package/dist/ui/picker-rows.d.ts.map +1 -0
  72. package/package.json +70 -0
@@ -0,0 +1,855 @@
1
+ import type { AgentMetricsSnapshot } from "@nuvin/agent-core/agent";
2
+ import type { AgentEvent, AgentInput, AskUserAnswers, AskUserQuestionRequest, JsonObject, JsonValue } from "@nuvin/agent-core/shared";
3
+ import type { ApprovalQueueState } from "../state/approvals.ts";
4
+ import type { MessageState, ToolMessageStatus } from "../state/messages.ts";
5
+ /** Delegated child-agent context, mirrored from the agent channel. */
6
+ export type WireDelegationScope = {
7
+ agentId: string;
8
+ parentToolCallId: string;
9
+ };
10
+ /** A tool call wants a path outside allowedDirs; the user may grant a dir. */
11
+ export type DirAccessRequest = {
12
+ /** Absolute resolved path the tool asked for. */
13
+ requestedPath: string;
14
+ /** Default grant offered to the user (parent dir for file paths). */
15
+ proposedDir: string;
16
+ };
17
+ /** Wire-safe pending approval: IDs and display data only — no resolve fn. */
18
+ export type ApprovalDescriptor = {
19
+ agentId: string;
20
+ /** Present when this approval is a directory-access grant prompt. */
21
+ dirAccess?: DirAccessRequest;
22
+ input?: JsonObject;
23
+ nickname?: string;
24
+ parentToolCallId?: string;
25
+ summary: string;
26
+ toolCallId: string;
27
+ toolName: string;
28
+ };
29
+ /** Wire-safe pending question. */
30
+ export type QuestionDescriptor = {
31
+ questionId: string;
32
+ request: AskUserQuestionRequest;
33
+ };
34
+ /** Display metadata replicated to clients alongside session state. */
35
+ export type SessionMeta = {
36
+ approvalMode: string;
37
+ /**
38
+ * Total context-window token budget for the active model, when known. Absent
39
+ * when no provider/model is configured or the model's limit is unknown
40
+ * (spec §4.3). The TUI uses it to render the context gauge.
41
+ */
42
+ contextWindowLimit?: number;
43
+ cwd: string;
44
+ modelName: string;
45
+ /** Active main-agent persona id (spec §4.3); `"default"` for the built-in coordinator. */
46
+ persona: string;
47
+ /**
48
+ * Active config profile the daemon runtime resolves config/agents/workspaces
49
+ * under (the `profiles.yaml` active profile). Display-only — clients render it
50
+ * as a header indicator. Omitted for the built-in `"default"` profile so the
51
+ * common case carries nothing on the wire.
52
+ */
53
+ profile?: string;
54
+ /**
55
+ * Resolved workspace name for this session (spec §7 — fixed at session
56
+ * creation). The session's active provider/model are fully determined by this
57
+ * workspace. Absent on legacy/host-less seeds that did not resolve one.
58
+ */
59
+ workspace?: string;
60
+ /**
61
+ * Current human-readable session topic; absent until the first user message
62
+ * or an UpdateTopic call sets it.
63
+ */
64
+ topic?: string;
65
+ /**
66
+ * Active provider reasoning (thinking) level (LOW/MEDIUM/HIGH), display-only.
67
+ * Absent when thinking is OFF/unset so the common case carries nothing on the
68
+ * wire. Clients render it as a suffix to the model name.
69
+ */
70
+ thinking?: string;
71
+ };
72
+ /**
73
+ * Wire-safe descriptor for a server-executed slash command. The daemon sends
74
+ * these in the welcome frame (spec §4.1); in-process they ride the attach
75
+ * snapshot. The Composer merges these with client-local command names for
76
+ * autocomplete. No execution logic crosses the wire — only display metadata.
77
+ */
78
+ export type SlashCommandDescriptor = {
79
+ name: string;
80
+ description: string;
81
+ argumentHint?: string;
82
+ };
83
+ /**
84
+ * Raw model-completion data for the `/model` autocomplete popup, returned by
85
+ * the `complete-models` command (spec §6). The daemon gathers it from its
86
+ * runtime (authenticated providers + each provider's live model list + recent
87
+ * selections); the client builds the grouped ComboBox items. JSON-safe by rule
88
+ * — the round-trip test guards it.
89
+ */
90
+ export type ModelCompletion = {
91
+ /** Authenticated providers, in registration order. */
92
+ providers: string[];
93
+ /** Per-provider model lists, keyed by provider. */
94
+ perProvider: Record<string, {
95
+ id: string;
96
+ name?: string;
97
+ }[]>;
98
+ /** Recently selected models (most recent first), for the "Recent" group. */
99
+ recentModels: {
100
+ provider: string;
101
+ model: string;
102
+ }[];
103
+ };
104
+ /**
105
+ * The replicated session state. Everything here survives JSON round-trip.
106
+ * The server holds exactly this plus private side tables (resolvers, queue
107
+ * texts, abort controller) that never cross the wire.
108
+ */
109
+ export type SessionViewState = {
110
+ approval: ApprovalQueueState<ApprovalDescriptor>;
111
+ /**
112
+ * Active MCP OAuth login flow (spec §10/§12), or null when no login is in
113
+ * flight. Set/cleared by `auth-flow` events; the client renders McpAuthModal
114
+ * from it. NOT part of the message transcript.
115
+ */
116
+ authFlow: McpAuthFlowState | null;
117
+ busy: boolean;
118
+ messages: MessageState;
119
+ meta: SessionMeta;
120
+ /**
121
+ * Latest replicated agent metrics (token/usage counters), or null before the
122
+ * first `metrics` event (spec §4.3). The TUI renders this in the status
123
+ * footer; it is NOT part of the message transcript.
124
+ */
125
+ metrics: AgentMetricsSnapshot | null;
126
+ queuedCount: number;
127
+ /**
128
+ * Messages waiting in the auto-send queue while a turn runs, in send order.
129
+ * Reduced from `turn-status.queued`; UI-only, never agent context. `[]` when
130
+ * nothing is queued.
131
+ */
132
+ queued: QueuedMessageView[];
133
+ /**
134
+ * Reduced live summaries of background workflow runs, keyed by runId (spec
135
+ * §8.2/§8.3). Folded from `workflow-progress` events; UI-only, NEVER part of
136
+ * the message transcript or agent context. `{}` until the first run emits.
137
+ */
138
+ workflows: Record<string, WorkflowViewState>;
139
+ question: {
140
+ active: QuestionDescriptor | null;
141
+ };
142
+ };
143
+ export type SessionSnapshot = {
144
+ seq: number;
145
+ state: SessionViewState;
146
+ /**
147
+ * Descriptors for server-executed slash commands, surfaced to the client for
148
+ * Composer autocomplete. In-process this is the stand-in for the daemon's
149
+ * `welcome.serverCommands` (spec §4.1). Empty when the host registers none.
150
+ */
151
+ serverCommands: SlashCommandDescriptor[];
152
+ };
153
+ /**
154
+ * Why an approval settled without (or with) a human decision.
155
+ * "client": a connected client decided via decide-approval.
156
+ * "abort": the turn ended (user abort, error, or session close) and pending
157
+ * approvals were flushed as rejected without a decision.
158
+ */
159
+ export type ApprovalSettledBy = "abort" | "client";
160
+ /**
161
+ * Wire-safe MCP OAuth login flow state (spec §10/§12). The daemon drives the
162
+ * flow and publishes each state change as an `auth-flow` event; the client
163
+ * renders the URL/status and opens the URL itself. The server-side cancel
164
+ * handle never crosses the wire — clients abort via the `cancel-auth-flow`
165
+ * command. `null` clears the modal (login completed, errored, or cancelled).
166
+ */
167
+ export type McpAuthFlowState = {
168
+ serverName: string;
169
+ status: string;
170
+ /** Authorization URL the client opens / displays. Absent before the redirect. */
171
+ url?: string;
172
+ } | null;
173
+ /** Coarse per-step status surfaced in the titled tree (§8.3). */
174
+ export type WorkflowNodeWireStatus = "running" | "done" | "failed" | "skipped" | "stopped";
175
+ /** One node in the coarse titled tree (phase → titled steps). No transcript. */
176
+ export type WorkflowStatusNode = {
177
+ title: string;
178
+ status?: WorkflowNodeWireStatus;
179
+ children?: WorkflowStatusNode[];
180
+ };
181
+ /**
182
+ * The reduced, UI-facing summary of one run (§8.3). The reducer folds each
183
+ * `WorkflowProgressDelta` into this copy-on-write. It is the SAME model
184
+ * `WorkflowStatus` reads — titles/phases/statuses/counts/tokens only, NEVER
185
+ * prompts, tool-calls, agent messages, or node outputs.
186
+ */
187
+ export type WorkflowViewState = {
188
+ runId: string;
189
+ status: "running" | "completed" | "failed" | "stopped";
190
+ /** Phase of the most recently started node, for a one-line live header. */
191
+ currentPhase?: string;
192
+ /** Phase → titled steps → latest status (§7.3 tree). */
193
+ tree: WorkflowStatusNode[];
194
+ /** Per-node-kind started counts (e.g. {agent: 3, transform: 1}). */
195
+ counts: Record<string, number>;
196
+ /** Summed `tokens` across finished nodes. */
197
+ tokens: number;
198
+ /** Highest `seq` folded in; lets clients dedupe replay vs live. */
199
+ lastSeq: number;
200
+ /**
201
+ * nodeId → [phase, title] index, so an incremental `node-finished` delta can
202
+ * re-locate its tree position (the tree keys on title, not nodeId). JSON-safe;
203
+ * UI-only; carries no transcript.
204
+ */
205
+ nodeKeys: Record<string, [string, string]>;
206
+ };
207
+ /**
208
+ * The wire form of one journal event (spec §7.2). Discriminated on `type`;
209
+ * mirrors @nuvin/workflow's JournalEvent member-for-member but with
210
+ * payload-bearing fields typed JsonValue (JSON-safe by rule — the json-safety
211
+ * test guards every member). Large prompts/outputs are NOT inlined: they ride
212
+ * as `promptRef`/`outputRef` strings the client fetches on demand.
213
+ */
214
+ export type WorkflowProgressDelta = {
215
+ type: "run-started";
216
+ seq: number;
217
+ at: number;
218
+ runId: string;
219
+ file: string;
220
+ args: JsonValue;
221
+ irDigest: string;
222
+ } | {
223
+ type: "node-started";
224
+ seq: number;
225
+ at: number;
226
+ nodeId: string;
227
+ kind: string;
228
+ phase?: string;
229
+ title?: string;
230
+ instanceKey?: string;
231
+ roundKey?: string;
232
+ inputsDigest: string;
233
+ } | {
234
+ type: "agent-prompt";
235
+ seq: number;
236
+ at: number;
237
+ nodeId: string;
238
+ promptRef: string;
239
+ } | {
240
+ type: "agent-step";
241
+ seq: number;
242
+ at: number;
243
+ nodeId: string;
244
+ kind: "model_request" | "tool_call" | "tool_result" | "assistant_message";
245
+ detail?: JsonValue;
246
+ } | {
247
+ type: "agent-output";
248
+ seq: number;
249
+ at: number;
250
+ nodeId: string;
251
+ outputRef: string;
252
+ model?: string;
253
+ tokens?: number;
254
+ } | {
255
+ type: "fanout-expanded";
256
+ seq: number;
257
+ at: number;
258
+ nodeId: string;
259
+ n: number;
260
+ keys: string[];
261
+ } | {
262
+ type: "gate-decided";
263
+ seq: number;
264
+ at: number;
265
+ nodeId: string;
266
+ branch: string;
267
+ } | {
268
+ type: "loop-round";
269
+ seq: number;
270
+ at: number;
271
+ nodeId: string;
272
+ round: number;
273
+ until: boolean;
274
+ } | {
275
+ type: "node-finished";
276
+ seq: number;
277
+ at: number;
278
+ nodeId: string;
279
+ status: "ran" | "replayed" | "skipped" | "retried" | "fallback" | "failed" | "aborted";
280
+ outputDigest: string;
281
+ instanceKey?: string;
282
+ roundKey?: string;
283
+ durationMs?: number;
284
+ tokens?: number;
285
+ } | {
286
+ type: "run-finished";
287
+ seq: number;
288
+ at: number;
289
+ status: "completed" | "failed" | "stopped";
290
+ summary?: JsonValue;
291
+ };
292
+ /**
293
+ * A message submitted during an active turn and waiting in the controller's
294
+ * auto-send queue (spec: removable-queued-messages). `id` is a controller-local
295
+ * monotonic handle used to cancel a specific item via the `dequeue` command.
296
+ */
297
+ export type QueuedMessageView = {
298
+ id: string;
299
+ displayText: string;
300
+ attachmentLabels?: string[];
301
+ };
302
+ export type ServerEventBody = {
303
+ type: "agent-event";
304
+ event: AgentEvent;
305
+ scope?: WireDelegationScope;
306
+ /**
307
+ * Set on `tool_call` events to carry the initial status decided by the
308
+ * controller. Auto-approved tools arrive as ONE event with
309
+ * `toolStatus: "approved"` and never produce approval-requested
310
+ * (anti-flash rule, spec §4.5).
311
+ */
312
+ toolStatus?: ToolMessageStatus;
313
+ } | {
314
+ type: "user-message";
315
+ text: string;
316
+ attachmentLabels?: string[];
317
+ } | {
318
+ type: "approval-requested";
319
+ approval: ApprovalDescriptor;
320
+ } | {
321
+ type: "approval-settled";
322
+ by: ApprovalSettledBy;
323
+ status: "approved" | "rejected";
324
+ toolCallId: string;
325
+ } | {
326
+ type: "question-asked";
327
+ question: QuestionDescriptor;
328
+ } | {
329
+ type: "question-settled";
330
+ questionId: string;
331
+ } | {
332
+ type: "turn-status";
333
+ busy: boolean;
334
+ queuedCount: number;
335
+ queued?: QueuedMessageView[];
336
+ } | {
337
+ type: "info";
338
+ message: string;
339
+ } | {
340
+ type: "error";
341
+ message: string;
342
+ } | {
343
+ type: "session-meta";
344
+ meta: SessionMeta;
345
+ } | {
346
+ /**
347
+ * Live agent metrics (token/usage counters), throttled to ~1/s during a
348
+ * turn and flushed once when the turn ends (spec §4.3). The snapshot is
349
+ * the JSON-safe AgentMetricsSnapshot from @nuvin/agent-core; AgentEvent's
350
+ * wire-frozen rule applies here too — changes require a PROTOCOL_VERSION
351
+ * bump enforced by the json-safety test.
352
+ */
353
+ type: "metrics";
354
+ snapshot: AgentMetricsSnapshot;
355
+ } | {
356
+ /**
357
+ * MCP OAuth login flow update (spec §10/§12). Carries the wire-safe
358
+ * `McpAuthFlowState`; the client renders `McpAuthModal` from it and opens
359
+ * the URL client-side. The host emits these from its `McpAuthController`
360
+ * subscription. JSON-safe by rule (json-safety test covers it).
361
+ */
362
+ type: "auth-flow";
363
+ flow: McpAuthFlowState;
364
+ } | {
365
+ /**
366
+ * UI progress for a background workflow run (spec §8.2). emit-only — the
367
+ * reducer folds `delta` into `state.workflows[runId]`; it NEVER calls
368
+ * submit and never touches agent messages. JSON-safe by rule (json-safety
369
+ * test covers it). `delta` is one journal event minus offloaded payloads.
370
+ */
371
+ type: "workflow-progress";
372
+ runId: string;
373
+ delta: WorkflowProgressDelta;
374
+ } | {
375
+ type: "state-reset";
376
+ state: SessionViewState;
377
+ };
378
+ export type ServerEvent = ServerEventBody & {
379
+ /** Epoch ms assigned by the controller; reducers use this as "now". */
380
+ at: number;
381
+ /** Monotonic per-session sequence number. */
382
+ seq: number;
383
+ };
384
+ export type ClientCommand = {
385
+ id: string;
386
+ type: "submit";
387
+ input: AgentInput;
388
+ displayText: string;
389
+ attachmentLabels?: string[];
390
+ } | {
391
+ id: string;
392
+ type: "decide-approval";
393
+ toolCallId: string;
394
+ decision: "a" | "n" | "y";
395
+ comment?: string;
396
+ /** Dir-access approvals only: the user-edited grant directory. */
397
+ grantDir?: string;
398
+ } | {
399
+ id: string;
400
+ type: "answer-question";
401
+ questionId: string;
402
+ answers: AskUserAnswers;
403
+ } | {
404
+ id: string;
405
+ type: "abort";
406
+ } | {
407
+ id: string;
408
+ type: "dequeue";
409
+ queuedId: string;
410
+ } | {
411
+ id: string;
412
+ type: "slash-command";
413
+ raw: string;
414
+ } | {
415
+ id: string;
416
+ type: "complete-path";
417
+ prefix: string;
418
+ }
419
+ /**
420
+ * Fetch the available models for the `/model` autocomplete popup (spec §6).
421
+ * The ack `result` is a {@link ModelCompletion}. Carries no payload — the
422
+ * daemon reads its own runtime config. Answered by the attached host; an
423
+ * empty completion is returned when no host is attached.
424
+ */
425
+ | {
426
+ id: string;
427
+ type: "complete-models";
428
+ } | {
429
+ id: string;
430
+ type: "load-history";
431
+ historyId: string;
432
+ }
433
+ /**
434
+ * Ask the daemon to re-read provider keys / config from the shared config
435
+ * dir after local onboarding wrote them (spec §10/§12). Acked once the
436
+ * reload completes; carries no payload — the daemon re-reads its own files.
437
+ */
438
+ | {
439
+ id: string;
440
+ type: "reload-config";
441
+ }
442
+ /**
443
+ * Abort the in-flight MCP OAuth login (spec §10). The daemon invokes the
444
+ * live cancel handle on its `McpAuthController`, which rejects the pending
445
+ * wait-for-callback and publishes a cleared `auth-flow` event. Carries no
446
+ * payload. Acked ok when a host is attached; unsupported otherwise.
447
+ */
448
+ | {
449
+ id: string;
450
+ type: "cancel-auth-flow";
451
+ }
452
+ /**
453
+ * Fetch a paginated raw slice of a run's journal as wire deltas (spec §8.2,
454
+ * D7). Session-scoped — answered only after `attach`. `sinceSeq` is the first
455
+ * seq to include (default 0). The ack `result` is
456
+ * `{ events: WorkflowProgressDelta[]; nextSeq?: number }`; page size 500. The
457
+ * client renders the collapsible audit tree from these (rendering is
458
+ * client-side, mirroring the reducer).
459
+ */
460
+ | {
461
+ id: string;
462
+ type: "get-workflow-trace";
463
+ runId: string;
464
+ sinceSeq?: number;
465
+ };
466
+ export type CommandAck = {
467
+ type: "ack";
468
+ id: string;
469
+ ok: boolean;
470
+ error?: {
471
+ code: string;
472
+ message: string;
473
+ };
474
+ /**
475
+ * Optional request/response payload (e.g. `complete-path` returns a string[]
476
+ * of completions). JSON-safe by rule; the round-trip test guards it.
477
+ */
478
+ result?: JsonValue;
479
+ };
480
+ /** Everything a server sends over a transport. */
481
+ export type ServerFrame = ServerEvent | CommandAck | {
482
+ type: "snapshot";
483
+ snapshot: SessionSnapshot;
484
+ };
485
+ /** Session lifecycle state surfaced by list-sessions (spec §4.2). */
486
+ export type SessionStatus = "awaiting-approval" | "busy" | "idle";
487
+ /** Session metadata returned by list-sessions / create-session (spec §4.2). */
488
+ export type SessionDescriptor = {
489
+ id: string;
490
+ name: string;
491
+ cwd: string;
492
+ /** Resolved workspace name for this session (spec §7). */
493
+ workspace?: string;
494
+ /** Resolved config profile for this session (spec §7). */
495
+ profile?: string;
496
+ model: string;
497
+ status: SessionStatus;
498
+ attachedClients: number;
499
+ /** Epoch ms. */
500
+ createdAt: number;
501
+ /** Epoch ms of the last ServerEvent this session emitted. */
502
+ lastActivityAt: number;
503
+ /** Current human-readable session topic; absent until set by the first user message or UpdateTopic. */
504
+ topic?: string;
505
+ /**
506
+ * First line of the most recent user message (trimmed, truncated), if any.
507
+ * Used as the dashboard title fallback when no topic is set yet (spec §4):
508
+ * topic → lastMessage → name. Absent when the session has no user turn.
509
+ */
510
+ lastMessage?: string;
511
+ };
512
+ /**
513
+ * The signed payload inside a relay grant (relay spec §7): a short-lived
514
+ * permission slip minted per client connection. Timestamps are epoch ms.
515
+ * The wire form is `base64url(JSON payload) + "." + base64url(signature)`;
516
+ * sign/verify live in the Node-only @nuvin/session/grant module — this type
517
+ * is here so browser-safe code can name the shape without touching crypto.
518
+ */
519
+ export type GrantPayload = {
520
+ /** The grant is only valid for this daemon. */
521
+ daemonId: string;
522
+ /** Clerk user id of who is connecting (authz + audit). */
523
+ userId: string;
524
+ /** Unique per grant, for logging/audit. */
525
+ grantId: string;
526
+ /** Epoch ms. Must not be absurdly in the future (±5 min skew window). */
527
+ issuedAt: number;
528
+ /** Epoch ms. Gates ONLY the handshake — established sessions live on. */
529
+ expiresAt: number;
530
+ };
531
+ /**
532
+ * Server → daemon frames on the relay CONTROL WebSocket (relay spec §4/§8.2).
533
+ * This is the relay control plane, NOT the session protocol: these frames
534
+ * never ride a daemon session socket, are not part of DaemonClientMessage /
535
+ * DaemonServerMessage, and are NOT covered by the PROTOCOL_VERSION exact-match
536
+ * handshake (the control connection authenticates via device token instead).
537
+ * Shared here so @nuvin/server and @nuvin/daemon agree on the envelope shape
538
+ * (relay spec §5: the server depends on @nuvin/session for protocol types only).
539
+ */
540
+ /** Methods the relay may RPC to a daemon over the control connection (spec §4.1).
541
+ * Params/results are method-specific and typed at each call site / handler. */
542
+ export type RelayRpcMethod = "get-transcript" | "list-history" | "delete-session" | "list-workspaces" | "create-workspace";
543
+ export type RelayControlMessage =
544
+ /** Dial wss://{server}/tunnel/{channelId} now — a client is waiting (spec §9.1). */
545
+ {
546
+ type: "open-channel";
547
+ channelId: string;
548
+ }
549
+ /** Registration revoked: close tunnels, drop control, clear local relay config (spec §8.2/§10). */
550
+ | {
551
+ type: "revoked";
552
+ }
553
+ /** Relay → daemon request/reply (spec §4.1). Correlated by `rpcId`. */
554
+ | {
555
+ type: "rpc";
556
+ rpcId: string;
557
+ method: RelayRpcMethod;
558
+ params: unknown;
559
+ };
560
+ /**
561
+ * Daemon → relay control frames (spec §4.1). The daemon pushes its live-session
562
+ * list (relay caches it for GET /api/daemons) and replies to relay RPCs.
563
+ */
564
+ export type DaemonControlMessage = {
565
+ type: "sessions";
566
+ sessions: SessionDescriptor[];
567
+ } | {
568
+ type: "rpc-result";
569
+ rpcId: string;
570
+ ok: true;
571
+ result: unknown;
572
+ } | {
573
+ type: "rpc-result";
574
+ rpcId: string;
575
+ ok: false;
576
+ error: string;
577
+ };
578
+ /**
579
+ * First frame on every daemon socket, client → daemon. The credential travels
580
+ * HERE and never in the URL (query strings leak into proxy logs — spec §4.1).
581
+ * Two credential variants (relay spec §8.3, ONE protocol bump):
582
+ * - `token`: the daemon's bearer token — UDS/TCP sockets only.
583
+ * - `grant`: an Ed25519-signed, short-lived permission slip minted by the
584
+ * relay server — relay-sourced sockets only. Opaque to clients; verified on
585
+ * the daemon (@nuvin/session/grant). The `?: never` markers make the two
586
+ * variants mutually exclusive while keeping property access narrowable.
587
+ */
588
+ export type HelloFrame = {
589
+ type: "hello";
590
+ protocolVersion: number;
591
+ } & ({
592
+ token: string;
593
+ grant?: never;
594
+ } | {
595
+ grant: string;
596
+ token?: never;
597
+ });
598
+ /**
599
+ * Successful handshake reply. Version policy is EXACT match on
600
+ * PROTOCOL_VERSION; serverCommands feed the Composer autocomplete (spec §4.1).
601
+ */
602
+ export type WelcomeFrame = {
603
+ type: "welcome";
604
+ protocolVersion: number;
605
+ daemonVersion: string;
606
+ serverCommands: SlashCommandDescriptor[];
607
+ };
608
+ /** Handshake refusal; the daemon closes the socket after sending it. */
609
+ export type RejectedFrame = {
610
+ type: "rejected";
611
+ reason: "auth" | "version";
612
+ };
613
+ /**
614
+ * A profile as advertised to directory clients (per-daemon). Returned as a
615
+ * JSON-safe array in a `CommandAck.result` by the `list-profiles` command.
616
+ * `active` ⇒ the daemon's currently-resolved active profile. Distinct from
617
+ * config's richer ProfileMetadata.
618
+ */
619
+ export type ProfileSummary = {
620
+ name: string;
621
+ active: boolean;
622
+ description?: string;
623
+ };
624
+ /**
625
+ * A provider as advertised to directory clients (web spec §7). Returned as a
626
+ * JSON-safe array in a `CommandAck.result` by the `list-providers` command.
627
+ * `id` is the provider's config name; `label` is display copy (the name in
628
+ * v1); `hasAuth` mirrors the config store's entry-scoped credential flag.
629
+ */
630
+ export type ProviderSummary = {
631
+ id: string;
632
+ label: string;
633
+ hasAuth: boolean;
634
+ isDefault: boolean;
635
+ };
636
+ /**
637
+ * A workspace as advertised to directory clients (spec §7). Returned (as a
638
+ * JSON-safe array in a `CommandAck.result`) by the `list-workspaces` command,
639
+ * and singly by `create-workspace`. Distinct from config's richer
640
+ * `WorkspaceMetadata`: this is the wire-facing projection.
641
+ */
642
+ export type WorkspaceSummary = {
643
+ name: string;
644
+ root: string;
645
+ adhoc: boolean;
646
+ sessionCount: number;
647
+ };
648
+ /** Pre-attach session management commands (spec §4.2). */
649
+ export type DaemonCommand = {
650
+ id: string;
651
+ type: "list-sessions";
652
+ } | {
653
+ id: string;
654
+ type: "list-history";
655
+ profile?: string;
656
+ cwd?: string;
657
+ } | {
658
+ id: string;
659
+ type: "create-session";
660
+ name?: string;
661
+ cwd: string;
662
+ workspace?: string;
663
+ /**
664
+ * Client-driven config profile (e.g. the local CLI's `--profile`). The
665
+ * daemon stays profile-agnostic and CONSUMES this only at session
666
+ * creation: the per-session runtime resolves config/agents under it.
667
+ * Omitted ⇒ the daemon's `profiles.yaml` active profile.
668
+ */
669
+ profile?: string;
670
+ }
671
+ /**
672
+ * Switch-and-resume (spec §3/§7): rehydrate a COLD history id into a NEW live
673
+ * SessionController and reply with a `session-created` frame carrying the live
674
+ * descriptor. The cold transcript has no daemon-side cwd, so the caller
675
+ * supplies the `cwd` the rehydrated runtime runs in. IDEMPOTENT daemon-side:
676
+ * resuming an already-live id returns the existing live session (no duplicate).
677
+ */
678
+ | {
679
+ id: string;
680
+ type: "resume-session";
681
+ historyId: string;
682
+ cwd: string;
683
+ name?: string;
684
+ workspace?: string;
685
+ /** Client-driven config profile, consumed at session creation (see create-session). */
686
+ profile?: string;
687
+ } | {
688
+ id: string;
689
+ type: "attach";
690
+ sessionId: string;
691
+ lastSeq?: number;
692
+ } | {
693
+ id: string;
694
+ type: "kill-session";
695
+ sessionId: string;
696
+ delete?: boolean;
697
+ }
698
+ /**
699
+ * Permanently delete a cold session transcript from the daemon's SessionStore
700
+ * (spec §8). Acked ok when something was deleted; `unknown-history` when the
701
+ * id was already gone. Refuses to delete live sessions — kill-session first
702
+ * (returns `history-live`).
703
+ */
704
+ | {
705
+ id: string;
706
+ type: "delete-history";
707
+ historyId: string;
708
+ workspace: string;
709
+ profile?: string;
710
+ }
711
+ /** Start the daemon-hosted web UI on `port` (default 3000). Replies `ack`
712
+ * with result { running, url, port } or an EADDRINUSE error (spec §4.5). */
713
+ | {
714
+ id: string;
715
+ type: "web-start";
716
+ port?: number;
717
+ }
718
+ /** Stop the daemon-hosted web UI. Replies `ack` with result { running:false }. */
719
+ | {
720
+ id: string;
721
+ type: "web-stop";
722
+ }
723
+ /**
724
+ * List the daemon's workspaces (spec §7). Pre-attach, directory-level. Replies
725
+ * with an `ack` whose `result` is a {@link WorkspaceSummary}[]. Per-daemon: the
726
+ * web new-session screen issues this against the SELECTED server's socket.
727
+ * `profile` omitted ⇒ the daemon's active profile.
728
+ */
729
+ | {
730
+ id: string;
731
+ type: "list-workspaces";
732
+ profile?: string;
733
+ }
734
+ /**
735
+ * Create a named workspace on the daemon (spec §7). `root` omitted ⇒ the daemon
736
+ * provisions an isolated `workdir/`. `profile` omitted ⇒ active profile. Replies
737
+ * with an `ack` whose `result` is the created {@link WorkspaceSummary}; ack
738
+ * `ok:false` `workspace-exists` on duplicate.
739
+ */
740
+ | {
741
+ id: string;
742
+ type: "create-workspace";
743
+ name: string;
744
+ root?: string;
745
+ profile?: string;
746
+ }
747
+ /**
748
+ * List the daemon's config profiles (spec §7-style, per-daemon). Pre-attach,
749
+ * directory-level. Replies with an `ack` whose `result` is a {@link ProfileSummary}[].
750
+ */
751
+ | {
752
+ id: string;
753
+ type: "list-profiles";
754
+ }
755
+ /**
756
+ * Provider-config management (relay spec §8.4, web spec §7): pre-attach,
757
+ * directory-level, so the cloud web UI can configure a REMOTE daemon over
758
+ * the protocol. Mirrors the CLI web server's /api/providers* REST surface.
759
+ * Replies with an `ack` whose `result` is a {@link ProviderSummary}[].
760
+ */
761
+ | {
762
+ id: string;
763
+ type: "list-providers";
764
+ }
765
+ /** Set the daemon's default (active) provider. Acks ok; mutations reload running sessions like `reload-config`. */
766
+ | {
767
+ id: string;
768
+ type: "set-default-provider";
769
+ providerId: string;
770
+ }
771
+ /**
772
+ * Set a provider's credential (API key). The credential transits the wire
773
+ * (TLS / spliced relay frames — accepted v1 threat, web spec §7) and is
774
+ * stored only in the daemon's config store.
775
+ */
776
+ | {
777
+ id: string;
778
+ type: "set-provider-auth";
779
+ providerId: string;
780
+ credential: string;
781
+ }
782
+ /** Remove a provider's stored credential. */
783
+ | {
784
+ id: string;
785
+ type: "clear-provider-auth";
786
+ providerId: string;
787
+ };
788
+ /**
789
+ * A cold/resumable session from the daemon's JSONL SessionStore (spec §8).
790
+ * Display-only metadata; resuming it sends a `load-history` command after
791
+ * attaching to a (new or existing) live session.
792
+ */
793
+ export type HistorySummary = {
794
+ id: string;
795
+ title: string;
796
+ /** Epoch ms of the last write to the session dir. */
797
+ updatedAt: number;
798
+ parentSessionId?: string;
799
+ /** Owning workspace name — the group key for /history (spec §5.1). */
800
+ workspace: string;
801
+ /** Workspace root path, for clients with no local registry (web). */
802
+ workspaceRoot?: string;
803
+ };
804
+ /** Reply to list-history; `id` echoes the command id. */
805
+ export type HistoryListFrame = {
806
+ type: "history-list";
807
+ id: string;
808
+ sessions: HistorySummary[];
809
+ /** Set only when the client sent a cwd that resolved to a workspace. */
810
+ currentWorkspace?: string;
811
+ };
812
+ /**
813
+ * A paginated slice of cold history (spec §4.3): the `list-history` RPC result,
814
+ * the `GET /api/daemons/:id/history` body, and the cloud `fetchHistory()` return.
815
+ * `nextCursor` is an OPAQUE token — present only when more pages remain (decision
816
+ * §10.2: cursor, not offset). `currentWorkspace` is echoed when the caller sent a
817
+ * cwd that resolved to a workspace.
818
+ */
819
+ export type HistoryPage = {
820
+ sessions: HistorySummary[];
821
+ nextCursor?: string;
822
+ currentWorkspace?: string;
823
+ };
824
+ /** Reply to list-sessions; `id` echoes the command id. */
825
+ export type SessionListFrame = {
826
+ type: "session-list";
827
+ id: string;
828
+ sessions: SessionDescriptor[];
829
+ };
830
+ /** Reply to create-session; `id` echoes the command id. */
831
+ export type SessionCreatedFrame = {
832
+ type: "session-created";
833
+ id: string;
834
+ session: SessionDescriptor;
835
+ };
836
+ /**
837
+ * Reply to attach when lastSeq is still inside the ring buffer: every missed
838
+ * event in seq order (spec §4.6). The client reduces them exactly like live
839
+ * events; an empty list means nothing was missed. When the buffer no longer
840
+ * covers the gap the daemon answers with a snapshot frame instead.
841
+ */
842
+ export type ReplayFrame = {
843
+ type: "replay";
844
+ events: ServerEvent[];
845
+ };
846
+ /** The daemon detached this client (e.g. the session was killed — spec §8). */
847
+ export type DetachedFrame = {
848
+ type: "detached";
849
+ reason: string;
850
+ };
851
+ /** Everything a client may send over a daemon socket. */
852
+ export type DaemonClientMessage = ClientCommand | DaemonCommand | HelloFrame;
853
+ /** Everything a daemon may send over a socket. */
854
+ export type DaemonServerMessage = DetachedFrame | HistoryListFrame | RejectedFrame | ReplayFrame | ServerFrame | SessionCreatedFrame | SessionListFrame | WelcomeFrame;
855
+ //# sourceMappingURL=types.d.ts.map