@agent-compose/sdk 0.8.5 → 0.8.7

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 (100) hide show
  1. package/README.md +213 -189
  2. package/dist/agent/agent-context.d.ts +3 -3
  3. package/dist/agent/agent-loop.d.ts +6 -5
  4. package/dist/agent/perf-sampler.d.ts +27 -2
  5. package/dist/agent/run-agent.d.ts +1 -1
  6. package/dist/client.d.ts +119 -54
  7. package/dist/directives.d.ts +3 -3
  8. package/dist/display.d.ts +7 -0
  9. package/dist/errors.d.ts +1 -1
  10. package/dist/generated/agentc-commands.d.ts +34 -0
  11. package/dist/index.d.ts +12 -12
  12. package/dist/index.js +771 -204
  13. package/dist/request-context/request-context.d.ts +1 -1
  14. package/dist/runtimes/_cli-agent.d.ts +185 -68
  15. package/dist/runtimes/_reported-model.d.ts +16 -0
  16. package/dist/runtimes/claude-code.d.ts +60 -1
  17. package/dist/runtimes/claude.d.ts +1 -1
  18. package/dist/runtimes/codex.d.ts +94 -6
  19. package/dist/runtimes/codex.mid-turn-hook.test.d.ts +10 -0
  20. package/dist/runtimes/model-report.test.d.ts +14 -0
  21. package/dist/runtimes/openai-desktop.js +741 -200
  22. package/dist/runtimes/opencode.d.ts +48 -11
  23. package/dist/runtimes/opencode.test.d.ts +14 -0
  24. package/dist/sandbox/baked-clis.d.ts +75 -0
  25. package/dist/sandbox/exec-stream.d.ts +1 -2
  26. package/dist/sandbox/network-policy.d.ts +23 -5
  27. package/dist/sandbox.d.ts +4 -2
  28. package/dist/step-invocation/protocol.d.ts +3 -4
  29. package/dist/step-invocation/server.d.ts +2 -2
  30. package/dist/step-invocation/types.d.ts +1 -1
  31. package/dist/types/api-conversations.d.ts +442 -29
  32. package/dist/types/api-factory.d.ts +99 -10
  33. package/dist/types/api-projects.d.ts +521 -0
  34. package/dist/types/api-runs.d.ts +83 -0
  35. package/dist/types/api-scopes.d.ts +32 -3
  36. package/dist/types/conversation-stream.d.ts +5 -0
  37. package/dist/types/execution-context.d.ts +1 -1
  38. package/dist/types/protocol.d.ts +86 -2
  39. package/dist/types/runtime.d.ts +9 -2
  40. package/dist/types/workflow-metadata.d.ts +2 -4
  41. package/dist/types/workflow-plan.d.ts +1 -3
  42. package/dist/utils/bundler.d.ts +23 -0
  43. package/dist/workflow-steps/observability.d.ts +2 -3
  44. package/dist/workflow-steps/runner.d.ts +5 -8
  45. package/dist/workflow-steps/types.d.ts +8 -10
  46. package/dist/workflow-steps/workflow.d.ts +2 -1
  47. package/dist/workflows/engine.d.ts +3 -5
  48. package/dist/workflows/invoke-child.d.ts +2 -2
  49. package/package.json +2 -2
  50. package/src/agent/agent-context.ts +168 -125
  51. package/src/agent/agent-loop.ts +7 -6
  52. package/src/agent/perf-sampler.ts +54 -3
  53. package/src/agent/run-agent.ts +1 -1
  54. package/src/client.ts +226 -71
  55. package/src/directives.ts +3 -3
  56. package/src/display.ts +12 -0
  57. package/src/errors.ts +1 -0
  58. package/src/generated/agentc-commands.ts +571 -0
  59. package/src/index.ts +57 -21
  60. package/src/pause/pause-core.ts +2 -1
  61. package/src/request-context/request-context.ts +1 -1
  62. package/src/runtimes/_cli-agent.ts +318 -122
  63. package/src/runtimes/_reported-model.ts +24 -0
  64. package/src/runtimes/claude-code.ts +195 -12
  65. package/src/runtimes/claude.ts +9 -2
  66. package/src/runtimes/codex.ts +188 -19
  67. package/src/runtimes/opencode.ts +195 -26
  68. package/src/sandbox/baked-clis.ts +86 -0
  69. package/src/sandbox/exec-stream.ts +1 -2
  70. package/src/sandbox/network-policy.ts +51 -7
  71. package/src/sandbox/providers/e2b.ts +3 -3
  72. package/src/sandbox/providers/vercel.ts +6 -6
  73. package/src/sandbox.ts +8 -2
  74. package/src/step-invocation/invoker.ts +2 -6
  75. package/src/step-invocation/protocol.ts +3 -4
  76. package/src/step-invocation/server.ts +2 -2
  77. package/src/types/api-conversations.ts +366 -23
  78. package/src/types/api-factory.ts +95 -10
  79. package/src/types/api-projects.ts +477 -0
  80. package/src/types/api-runs.ts +73 -0
  81. package/src/types/api-scopes.ts +32 -3
  82. package/src/types/conversation-stream.ts +5 -0
  83. package/src/types/execution-context.ts +1 -1
  84. package/src/types/protocol.ts +91 -2
  85. package/src/types/runtime.ts +8 -2
  86. package/src/types/sandbox-environment.ts +1 -2
  87. package/src/types/workflow-metadata.ts +2 -4
  88. package/src/types/workflow-plan.ts +1 -3
  89. package/src/utils/bundler.ts +88 -19
  90. package/src/workflow-steps/observability.ts +2 -3
  91. package/src/workflow-steps/runner.ts +5 -8
  92. package/src/workflow-steps/types.ts +8 -10
  93. package/src/workflow-steps/workflow.ts +2 -1
  94. package/src/workflows/engine.ts +3 -5
  95. package/src/workflows/invoke-child.ts +2 -2
  96. package/dist/generated/verb-synopsis.d.ts +0 -34
  97. package/dist/pause/__tests__/errors.test.d.ts +0 -1
  98. package/dist/pause/__tests__/wrappers.test.d.ts +0 -1
  99. package/dist/step-invocation/__tests__/protocol.test.d.ts +0 -1
  100. package/src/generated/verb-synopsis.ts +0 -544
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import type { ConversationAgentPresence } from "./conversation-stream.js";
10
10
  import type { ConversationMemberRole } from "./api-scopes.js";
11
+ import type { ProjectRole } from "./api-projects.js";
11
12
  import type { SandboxSize } from "../sandbox/sizes.js";
12
13
  import type { SandboxNetworkPolicy } from "../sandbox/network-policy.js";
13
14
  /** A human member of your team — the people an agent (or you) can @-flag. */
@@ -43,6 +44,9 @@ export interface Mention {
43
44
  contextUrl: string | null;
44
45
  text: string;
45
46
  runId: string | null;
47
+ /** The chat message the ping came from (conversation pings from a send
48
+ * or an edit); null otherwise. */
49
+ messageId: string | null;
46
50
  seenAt: string | null;
47
51
  resolvedAt: string | null;
48
52
  createdAt: string;
@@ -87,10 +91,35 @@ export interface ConversationRow {
87
91
  instructions: string;
88
92
  };
89
93
  createdAt: string;
94
+ /** The ROW's write time — every write bumps it, invisible background
95
+ * rows included. Recency people see is `lastMessageAt`. */
90
96
  updatedAt: string;
91
- /** Per-viewer unread count — present on the list wire for session
92
- * callers; key callers see 0. */
97
+ /** Per-viewer unread room messages — present on the list wire for
98
+ * session callers; key callers see 0. */
93
99
  unreadCount?: number;
100
+ /** Per-viewer unread replies in the channel threads the viewer follows
101
+ * (wrote the root or a reply) — read only by opening the thread.
102
+ * List wire; key callers see 0. */
103
+ threadUnreadCount?: number;
104
+ /** When the conversation last had something a person can SEE (a
105
+ * message that renders, room or thread) — "last active" and the rail's
106
+ * order. Null when nothing visible is recent. List wire. */
107
+ lastMessageAt?: string | null;
108
+ /** The projects holding this chat that the caller can see — a member
109
+ * row, or a team-public project (role `read`) — each with the object
110
+ * row that files it. Empty for a chat in no visible project; a private
111
+ * project the caller is outside is never named. List wire. */
112
+ projects?: ConversationProject[];
113
+ }
114
+ /** A project a chat is filed in, as the caller may see it: the project,
115
+ * the caller's role there (a member row's, or a public project's `read`
116
+ * floor) and the object row that files the chat — the id a
117
+ * `removeProjectObject` of that filing takes. */
118
+ export interface ConversationProject {
119
+ id: string;
120
+ name: string;
121
+ role: ProjectRole;
122
+ objectId: string;
94
123
  }
95
124
  /** Agent-author identity decoration (mirrors the server's `agentAuthor`
96
125
  * on message rows and stream payloads): rows authored by a user's agent
@@ -105,6 +134,9 @@ export interface ConversationAgentAuthor {
105
134
  avatarSeed: string;
106
135
  ownerUserId: string | null;
107
136
  ownerName: string | null;
137
+ /** True when this agent IS Ivy (a user's assistant or a shared chat's
138
+ * resident): render Ivy's one mark, never `avatarSeed`. */
139
+ ivy: boolean;
108
140
  }
109
141
  export interface ConversationMessageRow {
110
142
  id: string;
@@ -130,6 +162,11 @@ export interface ConversationMessageRow {
130
162
  * (absent on older servers). */
131
163
  editedAt?: string | null;
132
164
  createdAt: string;
165
+ /** When the row's words first landed, on a reply row its turn opened
166
+ * before it had any; null for a row sent with its words. Read state
167
+ * dates a message by the later of this and `createdAt`: a reply that
168
+ * lands after a read is new. Absent on older servers. */
169
+ publishedAt?: string | null;
133
170
  /** Thread-root facepile (≤3) — present only on roots with replies. */
134
171
  replyAuthors?: Array<{
135
172
  kind: string;
@@ -379,6 +416,44 @@ export interface ConversationThread {
379
416
  root: ConversationMessageRow;
380
417
  /** Oldest→newest; the root is not repeated. */
381
418
  messages: ConversationMessageRow[];
419
+ /** The viewer's thread read cursor before this visit (ISO) — the
420
+ * panel's "new replies" divider; null when they never read it. */
421
+ viewerLastReadAt: string | null;
422
+ }
423
+ /** GET /conversations/unread — every chat with something unread for the
424
+ * caller (the sidebar, project rollups and space totals read this one
425
+ * summary). Session callers only; keys get an empty list. */
426
+ export interface ConversationUnreadSummary {
427
+ conversations: Array<{
428
+ conversationId: string;
429
+ unreadCount: number;
430
+ threadUnreadCount: number;
431
+ }>;
432
+ /** A server bound cut the list. */
433
+ truncated: boolean;
434
+ }
435
+ /** GET /conversations/:id/unread-threads — one channel's threads with
436
+ * unread replies for the caller, in transcript order. */
437
+ export interface ConversationUnreadThreads {
438
+ threads: Array<{
439
+ rootId: string;
440
+ unreadCount: number;
441
+ }>;
442
+ truncated: boolean;
443
+ }
444
+ /** POST /conversations/:id/read — what the caller SAW (`through` the newest
445
+ * message they had in view), or the explicit wholesale read (`all`). */
446
+ export type ConversationReadInput = {
447
+ through: string;
448
+ } | {
449
+ all: true;
450
+ };
451
+ export interface ConversationReadResult {
452
+ ok: true;
453
+ /** The thread the read moved; null = the room. */
454
+ threadRootId: string | null;
455
+ /** That place's cursor after the read (ISO). */
456
+ readAt: string;
382
457
  }
383
458
  /** One live dev preview on a cloud session (ADR-0052 §4). The directory row
384
459
  * the UI lists; the security boundary is the member-gated proxy + the
@@ -403,12 +478,23 @@ export interface SessionPreview {
403
478
  url: string | null;
404
479
  createdAt: string;
405
480
  lastSeenAt: string;
481
+ /** The hosting session (a chat's list spans the sessions attached to the
482
+ * chat; a session's own list names itself) and its liveness: `live` =
483
+ * the machine runs now, `parked` = suspended (an open wakes it), `gone`
484
+ * = the machine was reclaimed. */
485
+ sessionId: string;
486
+ sessionTitle: string | null;
487
+ sessionState: "live" | "parked" | "gone";
406
488
  }
407
489
  /** Result of opening a dev preview (`POST /conversations/:id/preview`). */
408
490
  export interface PreviewOpened {
409
491
  preview: SessionPreview;
410
- /** The member-gated proxy URL to open in a browser (never the raw host). */
411
- url: string;
492
+ /** The member-gated preview URL to open in a browser (never the raw host),
493
+ * minted for the PERSON who opened it. `null` when the opener is the
494
+ * session's own machine key (`agentc preview open` inside the sandbox):
495
+ * a machine gets no link; every person who looks mints their own from
496
+ * the live previews list (the card, the chat's Previews chip). */
497
+ url: string | null;
412
498
  }
413
499
  /** Input for `openPreview`. */
414
500
  export interface OpenPreviewInput {
@@ -443,6 +529,139 @@ export interface BackgroundWorkChildDecl {
443
529
  journalPath?: string;
444
530
  label?: string;
445
531
  }
532
+ export type SessionWaitKind = "mail" | "time" | "need" | "github" | "webhook" | "schedule";
533
+ export type SessionWaitState = "open" | "satisfied" | "expired" | "cancelled";
534
+ /** What a session asks to be woken for. A mail wait names a sender (a full
535
+ * address or "@domain") with an optional subject regex and ONE deadline
536
+ * form (`forMs` from now, or an ISO `until`; default a day); a time wait
537
+ * names its instant, which is also its deadline. */
538
+ export type CreateSessionWaitInput = {
539
+ kind: "mail";
540
+ from: string;
541
+ subject?: string;
542
+ forMs?: number;
543
+ until?: string;
544
+ label?: string;
545
+ } | {
546
+ kind: "time";
547
+ at: string;
548
+ label?: string;
549
+ } | {
550
+ kind: "github";
551
+ repo: string;
552
+ events?: Array<"workflow_run" | "check_run" | "check_suite" | "status" | "release" | "tag">;
553
+ branches?: string[];
554
+ conclusions?: string[];
555
+ label: string;
556
+ cooldownMinutes?: number;
557
+ forDays?: number;
558
+ forHours?: number;
559
+ } | {
560
+ kind: "webhook";
561
+ label: string;
562
+ cooldownMinutes?: number;
563
+ forDays?: number;
564
+ forHours?: number;
565
+ } | {
566
+ kind: "schedule";
567
+ cron?: string;
568
+ everyMinutes?: number;
569
+ timezone: string;
570
+ label: string;
571
+ /** The wait's lifetime (one of the two). A plain schedule more often
572
+ * than hourly is armed with one, or with `noEndReason`. */
573
+ forDays?: number;
574
+ forHours?: number;
575
+ noEndReason?: string;
576
+ /** A SCRIPTED CHECK: the workspace's own registered workflow each tick
577
+ * runs instead of waking anyone, the input every run gets, the vault
578
+ * keys it reads (from the grants of the chat it is armed from, or of
579
+ * that chat's project), the person's message it answers, and a per-run
580
+ * model allowance when it must call a small model. Its first run is
581
+ * reviewed by the thread's agent before its schedule starts; then only
582
+ * what a run reports as needing attention (`attention: { summary,
583
+ * evidence }`), a run that measured nothing (no `measured`), or once a
584
+ * failure reaches the thread's agent. */
585
+ workflow?: string;
586
+ input?: Record<string, unknown>;
587
+ credentials?: string[];
588
+ askedIn?: string;
589
+ modelBudgetUsd?: number;
590
+ };
591
+ /** How one scripted check run came out, as its wait records it. `breach`:
592
+ * it reported no attention and measured nothing (missing data is
593
+ * attention, never all clear). */
594
+ export interface SessionWaitCheckResult {
595
+ runId: string | null;
596
+ at: string;
597
+ result: "clear" | "attention" | "breach" | "failed" | "not_run";
598
+ summary: string;
599
+ /** What the run reported it measured (`measured` in its output). */
600
+ measured?: unknown;
601
+ digest?: string;
602
+ inARow?: number;
603
+ /** Whether it reached the thread's agent. */
604
+ woke: boolean;
605
+ }
606
+ /** One review the thread agent recorded of a scripted check. */
607
+ export interface SessionWaitCheckReview {
608
+ at: string;
609
+ findings: string;
610
+ /** The registered version it reviewed (a content hash). */
611
+ version: string | null;
612
+ confirmed?: boolean;
613
+ }
614
+ /** A scripted check as its wait carries it. */
615
+ export interface SessionWaitCheck {
616
+ /** proving: its first run awaits the thread agent's review; live: it runs
617
+ * on its schedule. */
618
+ state: "proving" | "live" | null;
619
+ version: string | null;
620
+ /** The run going now. */
621
+ runId: string | null;
622
+ lastResult: SessionWaitCheckResult | null;
623
+ /** The agent's reviews of it, newest first (the last 20). */
624
+ reviews: SessionWaitCheckReview[];
625
+ nextReviewAt: string | null;
626
+ }
627
+ /** One declared wait as the server renders it. */
628
+ export interface SessionWait {
629
+ object: "session_wait";
630
+ id: string;
631
+ kind: SessionWaitKind;
632
+ /** mail: { from, subject? }; time: { instant }. */
633
+ spec: Record<string, unknown>;
634
+ label: string | null;
635
+ /** ISO deadline (a time wait's instant); a standing wait's lifetime, or
636
+ * null when it lives until cancelled. */
637
+ until: string | null;
638
+ state: SessionWaitState;
639
+ /** What settled it: the mail's from, subject and Gmail message id; the
640
+ * instant; or the deadline that passed. Null while open or cancelled. */
641
+ satisfiedBy: Record<string, unknown> | null;
642
+ createdAt: string;
643
+ updatedAt: string;
644
+ /** Standing waits only: how often it fired, when last, and a schedule's
645
+ * next instant. */
646
+ fireCount?: number;
647
+ lastFiredAt?: string | null;
648
+ nextFireAt?: string | null;
649
+ /** A webhook wait: the URL a monitoring tool POSTs to, and how to use it. */
650
+ webhookUrl?: string | null;
651
+ instructions?: string;
652
+ /** A scripted check: where it stands, its run going now, how the latest
653
+ * one came out, and the agent's reviews of it. */
654
+ check?: SessionWaitCheck;
655
+ }
656
+ /** The 201 of a declared wait: the row plus the one sentence the worker
657
+ * acts on ("the platform wakes this session when it arrives"). */
658
+ export interface SessionWaitCreated extends SessionWait {
659
+ message: string;
660
+ }
661
+ export interface SessionWaitList {
662
+ object: "list";
663
+ waits: SessionWait[];
664
+ }
446
665
  /** Outcome of requesting one machine size up
447
666
  * (`POST /conversations/:id/machine/request-upsize` — task #110, the
448
667
  * auto-resize policy's agent door). The platform arbitrates: within the
@@ -576,6 +795,38 @@ export interface SessionChangeSet {
576
795
  * pages serve an empty array. */
577
796
  reviewDocuments?: SessionReviewDocument[];
578
797
  }
798
+ /** The one fact keeping a session branch's version of a file off main
799
+ * (`SessionFilePlane.why`): an open conflict on the session's branch (one
800
+ * conflicting path holds every file on it), an open merge card, the
801
+ * session's merge gate, a linked repository's folder — or none of those,
802
+ * the session simply has not landed it yet. */
803
+ export type SessionFileHold = "in_progress" | "conflict" | "awaiting_approval" | "merge_gated" | "linked_repo";
804
+ /** Where ONE path's current version lives (`GET /conversations/:id/changes/file`),
805
+ * decided when a link to it is opened: `main` when the main drive holds the
806
+ * session branch's version (landed, or never changed on the branch), else
807
+ * `branch` with `why` naming what keeps it off main. `change`, `why` and
808
+ * `approvalId` are null and `conflictPaths` empty on the main plane. */
809
+ export interface SessionFilePlane {
810
+ object: "session_file_plane";
811
+ conversationId: string;
812
+ path: string;
813
+ plane: "main" | "branch";
814
+ branch: string | null;
815
+ change: "add" | "modify" | null;
816
+ why: SessionFileHold | null;
817
+ /** The open merge card waiting on this session's branch, if any. */
818
+ approvalId: string | null;
819
+ /** `why: "conflict"`: the session's open conflicts, oldest first, capped
820
+ * — the path itself when it is one of them, else what holds it. */
821
+ conflictPaths: string[];
822
+ /** The session the link named: its title, its status, and whether the
823
+ * platform lands its drive work on main as it goes (a thread's worker). */
824
+ session: {
825
+ title: string | null;
826
+ status: string;
827
+ worker: boolean;
828
+ };
829
+ }
579
830
  /** One reviewer's published review document
580
831
  * (`SessionChangeSet.reviewDocuments`). `stale` compares the head this
581
832
  * document was published against with the served diff. */
@@ -889,6 +1140,9 @@ export interface SendConversationMessageResult {
889
1140
  * private channel pings members only; a DM pings only the pair) —
890
1141
  * present only when non-empty. Absent on older servers. */
891
1142
  unnotifiedMentions?: UnnotifiedMention[];
1143
+ /** This send made the sender a member of the public chat (posting
1144
+ * joins). Present only when it happened; absent on older servers. */
1145
+ joined?: true;
892
1146
  }
893
1147
  /** Response of the presence heartbeat (ADR-0037 §6): a fresh agent-liveness
894
1148
  * snapshot + the roster TTL. The attach roster itself is NOT returned —
@@ -909,10 +1163,37 @@ export interface ChannelSessionStatus {
909
1163
  unreadCount: number;
910
1164
  /** An agent ask is awaiting a human answer (bounded transcript scan). */
911
1165
  awaiting: boolean;
912
- /** The most recent agent-authored message contains an error part. */
1166
+ /** The most recent agent-authored message contains an error part and the
1167
+ * newest turn was not stopped (a stop is not a failure). */
913
1168
  lastTurnFailed: boolean;
1169
+ /** The newest turn ended by a stop: someone stopped it on purpose (a
1170
+ * person, Ivy, the session's thread agent, the platform enforcing one of
1171
+ * those). */
1172
+ lastTurnStopped: boolean;
914
1173
  lastAgentMessageAt: string | null;
915
1174
  }
1175
+ /** A chat worker's chip facts (owner 2026-09-28) — set only when the
1176
+ * session is one of this chat's workers. Never viewer-scoped. */
1177
+ export interface ChannelWorkerFacts {
1178
+ /** The chat message the worker was started under; null = no anchor. */
1179
+ originMessageId: string | null;
1180
+ /** The model stamped at birth; null = the runtime's own default. */
1181
+ model: string | null;
1182
+ /** The worker's session has ended. */
1183
+ ended: boolean;
1184
+ /** Parked on an open question to its thread agent (work unfinished). */
1185
+ parked: boolean;
1186
+ /** Its one-line status ("Reading the Fly logs"), when that text was set
1187
+ * (ISO) and how old it was when read (`ageSec`: a live turn whose words
1188
+ * are old has produced nothing since), while the turn the line describes
1189
+ * is running; null otherwise. Live updates ride the channel's
1190
+ * `session_status` frames. */
1191
+ statusLine: {
1192
+ text: string;
1193
+ at: string;
1194
+ ageSec: number;
1195
+ } | null;
1196
+ }
916
1197
  /** One session attached to a channel, as listed by
917
1198
  * `GET /conversations/:channelId/sessions` (newest attach first). */
918
1199
  export interface ChannelSessionRow {
@@ -930,22 +1211,14 @@ export interface ChannelSessionRow {
930
1211
  attachedAt: string;
931
1212
  /** Who attached it (provenance only); null when the user is gone. */
932
1213
  addedBy: string | null;
1214
+ /** Set ⇔ the session is one of this chat's workers. */
1215
+ worker: ChannelWorkerFacts | null;
933
1216
  status: ChannelSessionStatus;
934
1217
  }
935
1218
  /** Response of `GET /conversations/:channelId/sessions`, verbatim. */
936
1219
  export interface ChannelSessionsResponse {
937
1220
  sessions: ChannelSessionRow[];
938
1221
  }
939
- /** Result of posting into a channel AS a session
940
- * (`POST /conversations/:channelId/session-messages`, ADR-0057 Seam 4).
941
- * The post never triggers any turn — it is a report, not an address. */
942
- export interface SessionChannelMessagePosted {
943
- messageId: string;
944
- /** Where the post landed: the thread root it replied under (by default,
945
- * the message that last addressed the session from that channel), or
946
- * null for a room post. Absent on older servers. */
947
- threadRootId?: string | null;
948
- }
949
1222
  /** Result of `POST /session-messages` — one session's agent messaging
950
1223
  * ANOTHER session's conversation (`agentc session message @alias`). Unlike
951
1224
  * a channel post, this DOES wake the target's turn machinery: `turn` is
@@ -1029,9 +1302,12 @@ export interface StreamConversationOptions {
1029
1302
  lastEventId?: number;
1030
1303
  signal?: AbortSignal;
1031
1304
  }
1032
- /** Shared chat creation includes its own Ivy unless explicitly disabled. */
1305
+ /** Shared chat creation includes its own Ivy unless explicitly disabled.
1306
+ * A chat is named for its topic: `title` is required (the server refuses
1307
+ * a shared chat without one — a conversation with people and no name is
1308
+ * a direct message, which people start themselves in the dashboard). */
1033
1309
  export interface CreateChatInput {
1034
- title?: string;
1310
+ title: string;
1035
1311
  visibility: "shared";
1036
1312
  access?: "public" | "private";
1037
1313
  memberIds?: string[];
@@ -1046,26 +1322,163 @@ export interface ChannelIvyState {
1046
1322
  enabled: boolean;
1047
1323
  agentId: string | null;
1048
1324
  canManage: boolean;
1049
- nextReviewAt: string | null;
1050
- work: Array<{
1051
- id: string;
1052
- title: string;
1053
- status: string;
1054
- evidence: string;
1055
- nextAction: string;
1056
- waitingOn: string | null;
1057
- reviewAt: string | null;
1058
- }>;
1059
1325
  }
1060
- export interface ProjectIvyConnection {
1326
+ /** Where a GitHub grant lives: the whole workspace, one project, or one
1327
+ * private chat. */
1328
+ export type IvyConnectionScope = "workspace" | "project" | "chat";
1329
+ /** A GitHub App installation's repositories granted to one scope. `scope`
1330
+ * says where it lives, `projectId` or `chatId` which one, and `scopeName`
1331
+ * that project's name or chat's title (null for the workspace). */
1332
+ export interface IvyConnection {
1061
1333
  id: string;
1062
1334
  teamId: string;
1063
- projectId: string;
1335
+ scope: IvyConnectionScope;
1336
+ projectId: string | null;
1337
+ chatId: string | null;
1338
+ scopeName: string | null;
1064
1339
  provider: "github";
1065
1340
  installationId: string;
1066
1341
  accountLogin: string;
1067
1342
  repositories: string[];
1343
+ /** ADR-0065 capabilities on the connection: allowWrites = issues
1344
+ * (create, update, comment), allowCodeWrites = code (push branches, open
1345
+ * pull requests). Every connection reads its repositories. */
1068
1346
  allowWrites: boolean;
1347
+ allowCodeWrites: boolean;
1069
1348
  configuredBy: string | null;
1349
+ /** The name of the person who granted it (last shared or changed); null
1350
+ * when they are gone or unnamed. */
1351
+ configuredByName: string | null;
1070
1352
  updatedAt: string;
1071
1353
  }
1354
+ /** An inherited grant of a project the caller does not read: the fact that
1355
+ * a project they are not in grants something here, nothing more. */
1356
+ export interface HiddenIvyConnection {
1357
+ id: string;
1358
+ provider: "github";
1359
+ scope: "project";
1360
+ hidden: true;
1361
+ }
1362
+ /** One scope's listing: its own grants, the ones it inherits (a project
1363
+ * the workspace's; a chat the workspace's and each project it is in),
1364
+ * whether the caller may remove and add here (canManage), whether the
1365
+ * scope takes a new grant now at all (addable: a chat's only while it is
1366
+ * private), and who can grant here when the caller cannot. */
1367
+ export interface IvyConnectionsView {
1368
+ own: IvyConnection[];
1369
+ inherited: Array<IvyConnection | HiddenIvyConnection>;
1370
+ canManage: boolean;
1371
+ addable: boolean;
1372
+ granters: string[];
1373
+ /** The TEAM CONNECTIONS of this scope (owner 2026-10-01): the
1374
+ * workspace's own, and what a project or chat inherits from the
1375
+ * workspace. Absent on an older server. */
1376
+ workspaceConnections?: WorkspaceConnectionsScopeView;
1377
+ /** The listing is a PAGE (`limit`, `cursor` on the request): its rows are
1378
+ * one bounded read in scope order, and `nextCursor` opens the next page;
1379
+ * null at the end. */
1380
+ nextCursor: string | null;
1381
+ }
1382
+ /** One page of a scope listing: `cursor` is the previous page's
1383
+ * `nextCursor`; `limit` (1..200, 50 by default) its size. */
1384
+ export interface ListingPage {
1385
+ cursor?: string;
1386
+ limit?: number;
1387
+ }
1388
+ /** Where a team connection is shared, as the caller may read it: a
1389
+ * project they are not in and a chat they do not read keep their ids and
1390
+ * lose their names. */
1391
+ export type WorkspaceConnectionAudienceEntry = {
1392
+ scope: "workspace";
1393
+ } | {
1394
+ scope: "project";
1395
+ projectId: string;
1396
+ name: string | null;
1397
+ } | {
1398
+ scope: "chat";
1399
+ chatId: string;
1400
+ title: string | null;
1401
+ };
1402
+ /** A TEAM-OWNED connection of a tool (Linear first): a dedicated account's
1403
+ * pasted key or the provider's app installed as itself, with the admin's
1404
+ * label, the provider's capability keys it enables, and who it is shared
1405
+ * with. Never a credential. */
1406
+ export interface WorkspaceConnection {
1407
+ id: string;
1408
+ provider: string;
1409
+ providerName: string;
1410
+ kind: "api_key" | "oauth_app";
1411
+ label: string;
1412
+ accountLabel: string | null;
1413
+ capabilities: string[];
1414
+ capabilityLabels: string[];
1415
+ status: "active" | "revoked" | "reauth_required";
1416
+ projectShareable: boolean;
1417
+ addedByName: string | null;
1418
+ audience: WorkspaceConnectionAudienceEntry[];
1419
+ apiHosts: string[];
1420
+ updatedAt: string;
1421
+ }
1422
+ export interface WorkspaceConnectionsScopeView {
1423
+ own: WorkspaceConnection[];
1424
+ inherited: WorkspaceConnection[];
1425
+ shareable: WorkspaceConnection[];
1426
+ canShare: boolean;
1427
+ }
1428
+ /** One picked Google Drive item of a share: a file, or a folder with
1429
+ * everything inside it. */
1430
+ export interface DriveShareItem {
1431
+ id: string;
1432
+ kind: "file" | "folder";
1433
+ name: string;
1434
+ mimeType: string | null;
1435
+ }
1436
+ /** A person's own Drive folders and files shared read-only into one scope
1437
+ * (owner 2026-10-01), read by Ivy there through the sharer's own account.
1438
+ * `account` is that Google address on the caller's OWN shares only; every
1439
+ * other row carries null and reads as "<Name>'s Drive". `sharedBy.email`
1440
+ * (the sharer's sign-in address, not their Google one) rides only while
1441
+ * the sharer shares the workspace with the viewer, for the label rule's
1442
+ * fail-safe when they have no name. `standing` says whether it still
1443
+ * reaches the scope today (the sharer is still in it and their Google
1444
+ * account still works). */
1445
+ export interface DriveShare {
1446
+ id: string;
1447
+ scope: IvyConnectionScope;
1448
+ projectId: string | null;
1449
+ chatId: string | null;
1450
+ scopeName: string | null;
1451
+ provider: string;
1452
+ sharedBy: {
1453
+ id: string;
1454
+ name: string | null;
1455
+ email: string | null;
1456
+ };
1457
+ account: string | null;
1458
+ items: DriveShareItem[];
1459
+ standing: boolean;
1460
+ standingReason: "sharer_left_scope" | "google_disconnected" | null;
1461
+ createdAt: string;
1462
+ canRemove: boolean;
1463
+ }
1464
+ /** An inherited share of a project the caller does not read: the fact that
1465
+ * a project they are not in shares something here, nothing more. */
1466
+ export interface HiddenDriveShare {
1467
+ id: string;
1468
+ provider: string;
1469
+ scope: "project";
1470
+ hidden: true;
1471
+ }
1472
+ /** One scope's Drive shares: its own, the ones it inherits, whether the
1473
+ * caller may share here, and the Google account they would share from. */
1474
+ export interface DriveSharesView {
1475
+ own: DriveShare[];
1476
+ inherited: Array<DriveShare | HiddenDriveShare>;
1477
+ mayShare: boolean;
1478
+ google: {
1479
+ grantId: string;
1480
+ account: string | null;
1481
+ } | null;
1482
+ /** A page, as IvyConnectionsView: `nextCursor` opens the next one. */
1483
+ nextCursor: string | null;
1484
+ }