@agent-compose/sdk 0.7.0 → 0.8.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.
Files changed (116) hide show
  1. package/README.md +66 -39
  2. package/dist/agent/__tests__/runtime-json-schema.test.d.ts +10 -0
  3. package/dist/agent/agent-context.d.ts +21 -1
  4. package/dist/agent/agent-loop.d.ts +24 -1
  5. package/dist/client.d.ts +338 -534
  6. package/dist/directives.d.ts +112 -0
  7. package/dist/display.d.ts +242 -0
  8. package/dist/errors.d.ts +24 -1
  9. package/dist/index.d.ts +24 -12
  10. package/dist/index.js +3545 -1667
  11. package/dist/pause/wrappers.d.ts +31 -9
  12. package/dist/runtimes/_acp-client.d.ts +46 -1
  13. package/dist/runtimes/_cli-agent.d.ts +49 -4
  14. package/dist/runtimes/_jsonl-guard.d.ts +103 -0
  15. package/dist/runtimes/amp.d.ts +2 -2
  16. package/dist/runtimes/claude-code.d.ts +59 -0
  17. package/dist/runtimes/claude-code.test.d.ts +14 -0
  18. package/dist/runtimes/claude.d.ts +16 -0
  19. package/dist/runtimes/claude.test.d.ts +8 -0
  20. package/dist/runtimes/codex.d.ts +9 -3
  21. package/dist/runtimes/cursor.d.ts +2 -2
  22. package/dist/runtimes/droid.d.ts +2 -2
  23. package/dist/runtimes/jsonl-guard.test.d.ts +19 -0
  24. package/dist/runtimes/openai-desktop.js +2691 -864
  25. package/dist/runtimes/opencode.d.ts +2 -2
  26. package/dist/runtimes/vercel.js +12 -1
  27. package/dist/sandbox/devbox.d.ts +42 -0
  28. package/dist/sandbox/exec-stream.d.ts +14 -0
  29. package/dist/sandbox/network-policy.d.ts +100 -0
  30. package/dist/sandbox/provider-def.d.ts +79 -0
  31. package/dist/sandbox/providers/desktop.d.ts +10 -0
  32. package/dist/sandbox/providers/e2b.d.ts +17 -0
  33. package/dist/sandbox/providers/local.d.ts +11 -0
  34. package/dist/sandbox/providers/vercel.d.ts +18 -0
  35. package/dist/sandbox/registry.d.ts +45 -0
  36. package/dist/sandbox/sizes.d.ts +68 -0
  37. package/dist/sandbox.d.ts +24 -299
  38. package/dist/step-invocation/__tests__/foreground-recovery.test.d.ts +1 -0
  39. package/dist/step-invocation/invoker.d.ts +10 -0
  40. package/dist/step-invocation/protocol.d.ts +5 -0
  41. package/dist/types/api-compliance.d.ts +71 -0
  42. package/dist/types/api-conversations.d.ts +492 -0
  43. package/dist/types/api-factory.d.ts +309 -0
  44. package/dist/types/api-projects.d.ts +131 -0
  45. package/dist/types/api-runs.d.ts +377 -0
  46. package/dist/types/api-scopes.d.ts +102 -0
  47. package/dist/types/conversation-stream.d.ts +191 -0
  48. package/dist/types/execution-context.d.ts +12 -2
  49. package/dist/types/protocol.d.ts +30 -1
  50. package/dist/types/sandbox-environment.d.ts +8 -5
  51. package/dist/types/sandbox.d.ts +74 -4
  52. package/dist/types/workflow-metadata.d.ts +33 -8
  53. package/dist/types/workflow-plan.d.ts +10 -0
  54. package/dist/types/workflow.d.ts +18 -205
  55. package/dist/utils/bundler.d.ts +12 -1
  56. package/dist/workflow-steps/index.d.ts +1 -1
  57. package/dist/workflow-steps/observability.d.ts +8 -1
  58. package/dist/workflow-steps/runner.d.ts +3 -3
  59. package/dist/workflow-steps/step.d.ts +15 -1
  60. package/dist/workflow-steps/types.d.ts +19 -5
  61. package/dist/workflow-steps/workflow.d.ts +22 -1
  62. package/dist/workflows/engine.d.ts +3 -2
  63. package/dist/workflows/invoke-child.d.ts +2 -2
  64. package/package.json +1 -1
  65. package/src/agent/agent-context.ts +186 -3
  66. package/src/agent/agent-loop.ts +31 -2
  67. package/src/client.ts +909 -621
  68. package/src/directives.ts +184 -0
  69. package/src/display.ts +788 -0
  70. package/src/errors.ts +39 -0
  71. package/src/index.ts +104 -10
  72. package/src/pause/wrappers.ts +44 -9
  73. package/src/runtimes/_acp-client.ts +72 -3
  74. package/src/runtimes/_cli-agent.ts +159 -36
  75. package/src/runtimes/_jsonl-guard.ts +219 -0
  76. package/src/runtimes/claude-code.ts +246 -0
  77. package/src/runtimes/claude.ts +32 -2
  78. package/src/runtimes/codex.ts +55 -3
  79. package/src/runtimes/openai-desktop.ts +59 -14
  80. package/src/sandbox/devbox.ts +48 -0
  81. package/src/sandbox/exec-stream.ts +48 -0
  82. package/src/sandbox/network-policy.ts +181 -0
  83. package/src/sandbox/provider-def.ts +94 -0
  84. package/src/sandbox/providers/desktop.ts +57 -0
  85. package/src/sandbox/providers/e2b.ts +354 -0
  86. package/src/sandbox/providers/local.ts +106 -0
  87. package/src/sandbox/providers/vercel.ts +331 -0
  88. package/src/sandbox/registry.ts +198 -0
  89. package/src/sandbox/sizes.ts +95 -0
  90. package/src/sandbox.ts +59 -1275
  91. package/src/step-invocation/invoker.ts +151 -28
  92. package/src/step-invocation/protocol.ts +8 -0
  93. package/src/types/api-compliance.ts +79 -0
  94. package/src/types/api-conversations.ts +522 -0
  95. package/src/types/api-factory.ts +336 -0
  96. package/src/types/api-projects.ts +140 -0
  97. package/src/types/api-runs.ts +412 -0
  98. package/src/types/api-scopes.ts +102 -0
  99. package/src/types/conversation-stream.ts +231 -0
  100. package/src/types/execution-context.ts +10 -2
  101. package/src/types/protocol.ts +33 -0
  102. package/src/types/sandbox-environment.ts +28 -9
  103. package/src/types/sandbox.ts +73 -4
  104. package/src/types/workflow-metadata.ts +35 -8
  105. package/src/types/workflow-plan.ts +11 -0
  106. package/src/types/workflow.ts +25 -292
  107. package/src/utils/bundler.ts +32 -5
  108. package/src/utils/errors.ts +16 -1
  109. package/src/workflow-steps/index.ts +1 -0
  110. package/src/workflow-steps/observability.ts +19 -8
  111. package/src/workflow-steps/runner.ts +4 -4
  112. package/src/workflow-steps/step.ts +49 -1
  113. package/src/workflow-steps/types.ts +20 -5
  114. package/src/workflow-steps/workflow.ts +22 -1
  115. package/src/workflows/engine.ts +3 -2
  116. package/src/workflows/invoke-child.ts +2 -2
@@ -0,0 +1,522 @@
1
+ /**
2
+ * Conversation-facing wire types for `AgentComposeClient` — channels, solo
3
+ * chats, sessions, threads, presence, team agents, and the
4
+ * teammate-mention surface.
5
+ *
6
+ * These are deliberate contract pins mirrored in `dashboard/src/lib/api.ts`;
7
+ * when the server changes a response shape, both update in the same change.
8
+ */
9
+
10
+ import type { ConversationAgentPresence } from "./conversation-stream.js";
11
+ import type { ConversationMemberRole } from "./api-scopes.js";
12
+ import type { SandboxSize } from "../sandbox/sizes.js";
13
+ import type { SandboxNetworkPolicy } from "../sandbox/network-policy.js";
14
+
15
+ /** A human member of your team — the people an agent (or you) can @-flag. */
16
+ export interface TeamMember {
17
+ /** Membership row id. */
18
+ id: string;
19
+ /** The user id — what you pass to `createMentions({ mentionedUserIds })`. */
20
+ userId: string;
21
+ role: string;
22
+ email: string;
23
+ name: string;
24
+ /** OAuth provider image URL, when present. */
25
+ image?: string | null;
26
+ /** Server-relative uploaded-avatar URL (ADR-0056), when set — takes
27
+ * precedence over `image`. Prefix with your API base. */
28
+ avatarUrl?: string | null;
29
+ joinedAt: string;
30
+ }
31
+
32
+ /** A "you were flagged" ping, persisted server-side so it reaches the
33
+ * mentioned teammate in their Workbench. */
34
+ export interface Mention {
35
+ id: string;
36
+ factoryId: string;
37
+ mentionedUserId: string;
38
+ /** Who flagged: 'user' | 'api_key' | 'run' | 'system'. */
39
+ actorKind: string;
40
+ actorId: string | null;
41
+ actorLabel: string | null;
42
+ /** Where it lives: 'doc' | 'comment' | 'plan' | 'run'. */
43
+ contextKind: string;
44
+ contextPath: string | null;
45
+ /** Ready-made relative dashboard URL the Workbench card links to. */
46
+ contextUrl: string | null;
47
+ text: string;
48
+ runId: string | null;
49
+ seenAt: string | null;
50
+ resolvedAt: string | null;
51
+ createdAt: string;
52
+ }
53
+
54
+ export interface CreateMentionsInput {
55
+ /** Team-member user ids to flag (1–20). Discover them via `listMembers()`.
56
+ * Non-members are dropped server-side. */
57
+ mentionedUserIds: string[];
58
+ /** The flag message shown in the teammate's Workbench. */
59
+ text: string;
60
+ contextKind: "doc" | "comment" | "plan" | "run";
61
+ /** Factory-relative file path or comment thread id, when applicable. */
62
+ contextPath?: string;
63
+ /** Ready-made relative dashboard URL the Workbench card links to (e.g.
64
+ * `/factories/<slug>/files/view?path=<plan>`). */
65
+ contextUrl?: string;
66
+ runId?: string;
67
+ factorySlug?: string;
68
+ }
69
+
70
+ // ── Conversations (wire shapes mirror routes/conversations.ts) ──────────────
71
+
72
+ /** One part of a conversation message. Text/tool/error parts as persisted by
73
+ * the server; unknown future part types flow through untyped. */
74
+ export type ConversationMessagePart = { type: string } & Record<string, unknown>;
75
+
76
+ export interface ConversationRow {
77
+ id: string;
78
+ /** null ⇔ visibility "dm" (human pair, no agent). */
79
+ agentId: string | null;
80
+ title: string | null;
81
+ visibility: "solo" | "shared" | "dm";
82
+ access: "public" | "private";
83
+ kind: "chat" | "session";
84
+ /** The dm pair's user ids (sorted); null for solo/shared. */
85
+ dmParticipantIds: string[] | null;
86
+ createdBy: string | null;
87
+ /** Per-conversation agent overrides. The wire always carries the
88
+ * normalized full object (the server defaults every field);
89
+ * `model: "default"` passes through to the agent's own model. */
90
+ agentConfig: { model: string; effort: string; instructions: string };
91
+ createdAt: string;
92
+ updatedAt: string;
93
+ /** Per-viewer unread count — present on the list wire for session
94
+ * callers; key callers see 0. */
95
+ unreadCount?: number;
96
+ }
97
+
98
+ export interface ConversationMessageRow {
99
+ id: string;
100
+ authorKind: string;
101
+ authorId: string | null;
102
+ parts: ConversationMessagePart[];
103
+ threadRootId: string | null;
104
+ reactions: Record<string, string[]>;
105
+ replyCount: number;
106
+ lastReplyAt: string | null;
107
+ createdAt: string;
108
+ /** Thread-root facepile (≤3) — present only on roots with replies. */
109
+ replyAuthors?: Array<{ kind: string; id: string | null }>;
110
+ /** Set on a channel message an ATTACHED SESSION posted (ADR-0057 Seam 4)
111
+ * — the session conversation it speaks as. Renderers attribute these
112
+ * rows to the session (title + runtime mark), never a resident agent.
113
+ * Null once the session is deleted; absent on older servers. */
114
+ sourceSessionId?: string | null;
115
+ /** Set on a session-transcript row RELAYED from a channel mention
116
+ * (ADR-0057 Seam 3) — the channel it arrived from. Null once the
117
+ * channel is deleted; absent on older servers. */
118
+ sourceChannelId?: string | null;
119
+ /** Set on relay rows alongside sourceChannelId — the ORIGIN channel
120
+ * message the mention rode in on (the session's default post-back
121
+ * threads under it). Null once deleted; absent on older servers. */
122
+ sourceMessageId?: string | null;
123
+ }
124
+
125
+ export interface ConversationsPage {
126
+ conversations: ConversationRow[];
127
+ next_cursor: string | null;
128
+ }
129
+
130
+ /** Sessions page — each row additionally carries its resident
131
+ * executor substrate (ADR-0037 §8): `"cloud"` = persistent server-side
132
+ * sandbox (pickers render the ☁ marker), `"local"` = bridge daemon,
133
+ * null = no durable session record yet. */
134
+ export interface SessionsPage {
135
+ conversations: Array<ConversationRow & { sessionExecutor: "local" | "cloud" | null }>;
136
+ next_cursor: string | null;
137
+ }
138
+
139
+ export interface ConversationDetail {
140
+ conversation: ConversationRow;
141
+ /** Resident executor substrate (ADR-0037 §8): `"local"` = a developer's
142
+ * bridge daemon; `"cloud"` = a persistent server-side sandbox on the
143
+ * factory drive; null = no durable session record (platform agents, or
144
+ * a bridge conversation before its first turn). Surfaces use this for
145
+ * presence labels only — attaching/driving is identical either way. */
146
+ sessionExecutor?: "local" | "cloud" | null;
147
+ /** The session's type (ADR-0055 §9) — the ONE surface decision, fixed at
148
+ * creation: `chat` = transcript + composer (no terminal, ever);
149
+ * `terminal`/`custom` = the PTY (`agentc session terminal`, no chat
150
+ * pane). Null = no durable session record; absent on older servers
151
+ * (read loosely). */
152
+ sessionType?: "chat" | "terminal" | "custom" | null;
153
+ /** The resident agent's slash-command palette, as reported by the
154
+ * executor (ACP `available_commands_update`) — drives "/" autocomplete
155
+ * in drivers. Null/absent when never reported (platform agents, older
156
+ * daemons/servers). */
157
+ sessionCommands?: Array<{ name: string; description: string }> | null;
158
+ viewerLastReadAt: string | null;
159
+ /** The caller's role in this conversation (ADR-0045) — drives client
160
+ * affordances only; the server remains the authority on every action.
161
+ * Public channels report implicit `write` for non-member teammates. */
162
+ viewerRole: ConversationMemberRole;
163
+ /** SSE replay watermark: the conversation's highest durable stream-event
164
+ * id at hydrate time — everything at or below it is already folded into
165
+ * `messages`. Seed the stream's first `Last-Event-ID` from it instead of
166
+ * 0 (0 replays the entire history). Absent on older servers. */
167
+ streamCursor?: number;
168
+ messages: ConversationMessageRow[];
169
+ }
170
+
171
+ /** Input for `createCloudSession` (ADR-0037 Phase 3b / ADR-0055 §9). The
172
+ * session runs against a CLOUD CHECKOUT on the factory's drive — never the
173
+ * caller's local files. Its TYPE is fixed at creation, never switched. */
174
+ export interface CreateCloudSessionInput {
175
+ /** Session type (ADR-0055 §9): `chat` = the wrapped agent experience (a
176
+ * coding runtime driven via ACP — no terminal, ever); `terminal` = a raw
177
+ * machine surface (the PTY is the surface — no chat pane); `custom` = a
178
+ * terminal born with a pre-start recipe (`presetId`/`preStart`). Omitted
179
+ * = derived server-side (runtime `terminal` → terminal, else chat) — the
180
+ * wire-level grandfather for older callers; send it explicitly. */
181
+ type?: "chat" | "terminal" | "custom";
182
+ /** In-sandbox coding runtime hosting a CHAT session. Required for chat;
183
+ * omitted (or `"terminal"`) for terminal/custom sessions — those have no
184
+ * harness, the shell is the surface. */
185
+ runtime?: "codex" | "cursor" | "droid" | "opencode" | "claude-code" | "terminal";
186
+ /** Custom sessions: a saved launch preset, resolved server-side and
187
+ * FROZEN into the session's pre-start at create (a later preset edit
188
+ * never changes an existing session). Exactly one of `presetId` /
189
+ * `preStart` is required for custom; both are 400s on other types. */
190
+ presetId?: string;
191
+ /** Custom sessions: an inline pre-start recipe — the bootstrap exec'd
192
+ * into the session's first pty (e.g. boot the claude-code TUI), with
193
+ * `preLaunch` an invisible shell fragment run before it. */
194
+ preStart?: {
195
+ command: string;
196
+ args?: string[];
197
+ env?: Record<string, string>;
198
+ preLaunch?: string | null;
199
+ };
200
+ /** Factory whose durable drive backs the session's working tree. */
201
+ factoryId: string;
202
+ title?: string;
203
+ /** Optional repo bootstrap: an https git URL cloned onto the drive on
204
+ * first turn (idempotent — survives sandbox eviction). */
205
+ repoUrl?: string;
206
+ repoBranch?: string;
207
+ /** Spawn-in-channel (ADR-0057 Seam 1): attach the new session to this
208
+ * channel in the same transaction — born attached, every channel member
209
+ * has derived access from birth. Requires channel role ≥ write. */
210
+ channelId?: string;
211
+ // ── Spawn options (ADR-0055 §2.5b) — the workflow-run call pattern ──────
212
+ /** Boot from a saved session template (its provider snapshot resolves
213
+ * server-side — snapshot refs never cross the wire). Mutually exclusive
214
+ * with `sandboxSize`: a snapshot captures its machine spec (400). */
215
+ templateId?: string;
216
+ /** Machine size in the run `sandboxSize` vocabulary. Sessions are E2B,
217
+ * so only the E2B-buildable sizes are accepted (400 otherwise).
218
+ * Omitted = the platform default. */
219
+ sandboxSize?: SandboxSize;
220
+ /** Egress policy — the same `SandboxNetworkPolicy` allow-map runs carry
221
+ * ("deny-all" = connectors-only: the platform re-widens it with the
222
+ * drive gateway + attached connector hosts at every acquire). Omitted =
223
+ * open egress. */
224
+ networkPolicy?: SandboxNetworkPolicy;
225
+ /** Birth connector attachment (ADR-0044): one of the owner's connector
226
+ * profile ids, or "none" for a bare session. Omitted = the owner's
227
+ * default profile. */
228
+ connectorProfileId?: string;
229
+ }
230
+
231
+ export interface CloudSessionCreated {
232
+ conversation: ConversationRow;
233
+ session: {
234
+ executor: "cloud";
235
+ /** The session's type (ADR-0055 §9) — decides its ONE surface: chat
236
+ * opens the transcript TUI; terminal/custom open with
237
+ * `agentc session terminal <id>` (the PTY, no chat pane). */
238
+ type: "chat" | "terminal" | "custom";
239
+ runtime: string;
240
+ sandboxProvider: string;
241
+ /** Drive-backed working directory inside the session sandbox. */
242
+ cwd: string;
243
+ repoUrl: string | null;
244
+ repoBranch: string | null;
245
+ };
246
+ /** Present when the session was spawned in a channel (`channelId` input)
247
+ * — the channel it was born attached to. */
248
+ attachedChannelId?: string;
249
+ }
250
+
251
+ export interface ConversationThread {
252
+ root: ConversationMessageRow;
253
+ /** Oldest→newest; the root is not repeated. */
254
+ messages: ConversationMessageRow[];
255
+ }
256
+
257
+ /** One live dev preview on a cloud session (ADR-0052 §4). The directory row
258
+ * the UI lists; the security boundary is the member-gated proxy + the
259
+ * short-lived token, never this row. */
260
+ export interface SessionPreview {
261
+ id: string;
262
+ /** Port bound inside the session sandbox. */
263
+ port: number;
264
+ name: string | null;
265
+ /** The preview's stable SUBDOMAIN label — it serves fully navigable at
266
+ * `<label>.<preview wildcard host>` when the deployment configures
267
+ * `PREVIEW_WILDCARD_ORIGIN`. Null on rows opened before labels existed
268
+ * (they stay on the path-prefixed proxy URL). */
269
+ label: string | null;
270
+ /** The app's announced landing path (e.g. `/dashboard`); null = `/`. */
271
+ path: string | null;
272
+ status: "active" | "closed";
273
+ /** The member-gated preview URL, bound to the requesting viewer — the
274
+ * subdomain bootstrap URL when the deployment supports it, else the
275
+ * path-prefixed proxy URL on the isolated preview origin (ADR-0052 §2.3).
276
+ * `null` when previews are disabled or the caller is not a user. */
277
+ url: string | null;
278
+ createdAt: string;
279
+ lastSeenAt: string;
280
+ }
281
+
282
+ /** Result of opening a dev preview (`POST /conversations/:id/preview`). */
283
+ export interface PreviewOpened {
284
+ preview: SessionPreview;
285
+ /** The member-gated proxy URL to open in a browser (never the raw host). */
286
+ url: string;
287
+ }
288
+
289
+ /** Input for `openPreview`. */
290
+ export interface OpenPreviewInput {
291
+ /** Port a dev server is LISTENING on inside the session sandbox (1–65535). */
292
+ port: number;
293
+ name?: string;
294
+ /** The app's landing path (e.g. `/dashboard`) — where the preview card and
295
+ * chips land the human. Root-relative (`/…`, never `//…`). */
296
+ path?: string;
297
+ }
298
+
299
+ /** Result of forking a cloud session (`POST /conversations/:id/fork`). */
300
+ export interface SessionForked {
301
+ /** The new child conversation to switch to. */
302
+ conversationId: string;
303
+ }
304
+
305
+ // ── Session branch proposals (ADR-0053) ─────────────────────────────────────
306
+ // Every cloud session works on its own factory-drive branch; the whole branch
307
+ // is the unit of review, like a PR. Wire shapes mirror
308
+ // server/src/routes/session-changes.ts verbatim (camelCase).
309
+
310
+ /** One changed file on a session's drive branch relative to `main`. */
311
+ export interface SessionFileChange {
312
+ /** Factory-relative path. */
313
+ path: string;
314
+ kind: "add" | "modify" | "delete";
315
+ /** Size of the proposed object; null for deletes. */
316
+ sizeBytes: number | null;
317
+ /** The proposed object's etag; null for deletes. */
318
+ etag: string | null;
319
+ }
320
+
321
+ /** The session's proposed change set (`GET /conversations/:id/changes`).
322
+ * `state: "unbranched"` = the session predates branching and still writes
323
+ * `main` directly (it transitions at its next fresh sandbox acquire);
324
+ * `branch`/`changes` are null/empty then. The `label` states the freshness
325
+ * contract: the list reflects the branch's last flush and is advisory —
326
+ * the merge re-verifies against fresh state. */
327
+ export interface SessionChangeSet {
328
+ object: "session_change_set";
329
+ conversationId: string;
330
+ branch: string | null;
331
+ state: "unbranched" | "clean" | "dirty";
332
+ label: string;
333
+ /** False = the branch can only be discarded: a FORKED session's branch
334
+ * carries its parent's unmerged work with no main-derived merge base, so
335
+ * `mergeSessionChanges` 409s `fork_branch_unmergeable`. */
336
+ mergeable: boolean;
337
+ changes: SessionFileChange[];
338
+ /** True when the listing was capped — more files changed than shown. */
339
+ truncated: boolean;
340
+ /** Open `factory_file_conflicts` rows from this session's prior merges. */
341
+ openConflicts: number;
342
+ }
343
+
344
+ /** Per-file accounting of one session branch merge — the merge core's
345
+ * honest report, returned verbatim. */
346
+ export interface SessionMergeReportDetail {
347
+ branch: string | null;
348
+ merged: number;
349
+ fastForwarded: number;
350
+ autoMerged: number;
351
+ created: number;
352
+ deleted: number;
353
+ skipped: number;
354
+ untouched: number;
355
+ skippedLossy: number;
356
+ droppedPaths: string[];
357
+ conflicts: number;
358
+ }
359
+
360
+ /** Result of approving a session's changes
361
+ * (`POST /conversations/:id/changes/merge`): the old branch was 3-way
362
+ * merged into `main` and the session continues on `newBranch`, freshly
363
+ * forked off post-merge `main`. `merged_with_conflicts` means some paths
364
+ * landed as `factory_file_conflicts` rows for human resolution. */
365
+ export interface SessionMergeReport {
366
+ object: "session_merge_report";
367
+ conversationId: string;
368
+ /** The branch that was merged. */
369
+ branch: string;
370
+ /** The session's fresh branch off post-merge `main`. */
371
+ newBranch: string;
372
+ status: "merged" | "merged_with_conflicts";
373
+ report: SessionMergeReportDetail;
374
+ }
375
+
376
+ /** Result of rejecting a session's changes
377
+ * (`POST /conversations/:id/changes/discard`): the old branch is abandoned
378
+ * (retained dormant, recoverable by an admin re-merge — never deleted) and
379
+ * the session continues on `newBranch`, freshly forked off `main`. */
380
+ export interface SessionDiscardReport {
381
+ object: "session_discard_report";
382
+ conversationId: string;
383
+ /** The abandoned branch. */
384
+ branch: string;
385
+ /** The session's fresh branch off `main`. */
386
+ newBranch: string;
387
+ discarded: true;
388
+ }
389
+
390
+ /** The sender's page stamp (HUD bar sends) — persisted server-side, never
391
+ * echoed back on the wire. Mirrors the server's `PageContext` schema. */
392
+ export interface ConversationPageContext {
393
+ kind: string;
394
+ path: string;
395
+ entity?: { type: string; id: string; label?: string };
396
+ }
397
+
398
+ export interface SendConversationMessageInput {
399
+ text: string;
400
+ /** Land the message in this ROOT message's thread (channels only; the
401
+ * triggered turn streams into the same thread). */
402
+ threadRootId?: string;
403
+ pageContext?: ConversationPageContext;
404
+ }
405
+
406
+ /** The server's authoritative turn verdict on a send (ADR-0037 §4):
407
+ * `none` = no turn owed (dm, or a channel message that doesn't @-tag the
408
+ * agent); `started` = this send won the turn claim and the reply is
409
+ * streaming; `queued` = a turn is already in flight — the message is owed
410
+ * work and a coalesced follow-up turn will answer it. NOT an error. */
411
+ export type ConversationTurnState = "none" | "started" | "queued";
412
+
413
+ export interface SendConversationMessageResult {
414
+ /** The persisted user message's id. */
415
+ messageId: string;
416
+ /** Whether an agent turn was kicked — the "replying" indicator keys off
417
+ * this, never a client-side re-parse. */
418
+ turnTriggered: boolean;
419
+ turnState: ConversationTurnState;
420
+ /** Channel sends only (ADR-0057 Seam 3): the attached sessions this
421
+ * message was relayed into via `[@Title](session:<id>)` mentions —
422
+ * each runs its own turn in its own conversation. Absent on older
423
+ * servers and non-channel sends. */
424
+ relayedSessionIds?: string[];
425
+ }
426
+
427
+ /** Response of the presence heartbeat (ADR-0037 §6): a fresh agent-liveness
428
+ * snapshot + the roster TTL. The attach roster itself is NOT returned —
429
+ * there is no server-side roster; subscribers assemble it from the
430
+ * live-only `presence` beats on the conversation stream. */
431
+ export interface ConversationPresenceSnapshot {
432
+ agent: ConversationAgentPresence | null;
433
+ /** A roster entry whose last beat is older than this has detached. */
434
+ ttlMs: number;
435
+ }
436
+
437
+ // ── Channel-attached sessions (ADR-0057 — sessions VISIT channels) ──────────
438
+ // A session can be brought into a channel for as long as a piece of work
439
+ // lasts: attachment is a link (never a membership copy) granting the
440
+ // channel's members derived access, and the session posts back into the
441
+ // channel attributed as itself. Wire shapes mirror the channel-session
442
+ // routes in server/src/routes/conversations.ts verbatim (camelCase).
443
+
444
+ /** Durable rail status for one attached session (ADR-0057 Seam 5) — all
445
+ * derived from existing signals server-side; no background polling. */
446
+ export interface ChannelSessionStatus {
447
+ /** `running` = a turn is executing (turn claim fresh); else `idle`. */
448
+ state: "running" | "idle";
449
+ /** Viewer-scoped unread count (conversation_reads — works for derived
450
+ * viewers). 0 for key callers. */
451
+ unreadCount: number;
452
+ /** An agent ask is awaiting a human answer (bounded transcript scan). */
453
+ awaiting: boolean;
454
+ /** The most recent agent-authored message contains an error part. */
455
+ lastTurnFailed: boolean;
456
+ lastAgentMessageAt: string | null;
457
+ }
458
+
459
+ /** One session attached to a channel, as listed by
460
+ * `GET /conversations/:channelId/sessions` (newest attach first). */
461
+ export interface ChannelSessionRow {
462
+ conversationId: string;
463
+ title: string | null;
464
+ /** The hosting in-sandbox runtime (agent_sessions.agent_kind); null for
465
+ * platform-loop sessions. */
466
+ runtime: string | null;
467
+ /** The session's type (ADR-0055 §9); null for platform-loop sessions,
468
+ * absent on older servers (read loosely). */
469
+ sessionType?: "chat" | "terminal" | "custom" | null;
470
+ /** Executor substrate; null = platform-loop session (no durable
471
+ * agent_sessions row). */
472
+ executor: "local" | "cloud" | null;
473
+ attachedAt: string;
474
+ /** Who attached it (provenance only); null when the user is gone. */
475
+ addedBy: string | null;
476
+ status: ChannelSessionStatus;
477
+ }
478
+
479
+ /** Response of `GET /conversations/:channelId/sessions`, verbatim. */
480
+ export interface ChannelSessionsResponse {
481
+ sessions: ChannelSessionRow[];
482
+ }
483
+
484
+ /** Result of posting into a channel AS a session
485
+ * (`POST /conversations/:channelId/session-messages`, ADR-0057 Seam 4).
486
+ * The post never triggers any turn — it is a report, not an address. */
487
+ export interface SessionChannelMessagePosted {
488
+ messageId: string;
489
+ /** Where the post landed: the thread root it replied under (by default,
490
+ * the message that last addressed the session from that channel), or
491
+ * null for a room post. Absent on older servers. */
492
+ threadRootId?: string | null;
493
+ }
494
+
495
+ /** One agent on the team, as listed by `GET /api/v1/agents`. Bridge-runtime
496
+ * rows additionally carry live presence (`online`, from the daemon's
497
+ * heartbeat within the server's TTL) and the hosting machine's label. */
498
+ export interface AgentListRow {
499
+ id: string;
500
+ name: string;
501
+ runtime: string;
502
+ avatarSeed: string;
503
+ model: string;
504
+ systemPrompt: string | null;
505
+ factoryIds: string[];
506
+ /** Verbatim jsonb from the server — tool-policy shape is runtime-defined. */
507
+ toolPolicy: Record<string, unknown>;
508
+ createdAt: string;
509
+ online?: boolean;
510
+ machineName?: string;
511
+ /** Stamped `"cloud"` on rows whose sessions are hosted server-side —
512
+ * session pickers filter on this rather than presence. */
513
+ sessionHost?: "cloud";
514
+ }
515
+
516
+ export interface StreamConversationOptions {
517
+ /** Highest durable stream-event id already processed — the server replays
518
+ * only rows with id greater than this. Never advance it from live-only
519
+ * events (`part_partial` / `turn_state` / `presence` / `session_status`). */
520
+ lastEventId?: number;
521
+ signal?: AbortSignal;
522
+ }