@agent-compose/sdk 0.8.5 → 0.8.6

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 (97) 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 +4 -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 +105 -52
  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 +716 -203
  13. package/dist/request-context/request-context.d.ts +1 -1
  14. package/dist/runtimes/_cli-agent.d.ts +182 -68
  15. package/dist/runtimes/claude-code.d.ts +60 -1
  16. package/dist/runtimes/claude.d.ts +1 -1
  17. package/dist/runtimes/codex.d.ts +94 -6
  18. package/dist/runtimes/codex.mid-turn-hook.test.d.ts +10 -0
  19. package/dist/runtimes/openai-desktop.js +686 -199
  20. package/dist/runtimes/opencode.d.ts +48 -11
  21. package/dist/runtimes/opencode.test.d.ts +14 -0
  22. package/dist/sandbox/baked-clis.d.ts +75 -0
  23. package/dist/sandbox/exec-stream.d.ts +1 -2
  24. package/dist/sandbox/network-policy.d.ts +23 -5
  25. package/dist/sandbox.d.ts +4 -2
  26. package/dist/step-invocation/protocol.d.ts +3 -4
  27. package/dist/step-invocation/server.d.ts +2 -2
  28. package/dist/step-invocation/types.d.ts +1 -1
  29. package/dist/types/api-conversations.d.ts +442 -29
  30. package/dist/types/api-factory.d.ts +78 -8
  31. package/dist/types/api-projects.d.ts +480 -0
  32. package/dist/types/api-runs.d.ts +8 -0
  33. package/dist/types/api-scopes.d.ts +32 -3
  34. package/dist/types/conversation-stream.d.ts +5 -0
  35. package/dist/types/execution-context.d.ts +1 -1
  36. package/dist/types/protocol.d.ts +65 -2
  37. package/dist/types/runtime.d.ts +9 -2
  38. package/dist/types/workflow-metadata.d.ts +2 -4
  39. package/dist/types/workflow-plan.d.ts +1 -3
  40. package/dist/utils/bundler.d.ts +23 -0
  41. package/dist/workflow-steps/observability.d.ts +2 -3
  42. package/dist/workflow-steps/runner.d.ts +5 -8
  43. package/dist/workflow-steps/types.d.ts +8 -10
  44. package/dist/workflow-steps/workflow.d.ts +2 -1
  45. package/dist/workflows/engine.d.ts +3 -5
  46. package/dist/workflows/invoke-child.d.ts +2 -2
  47. package/package.json +2 -2
  48. package/src/agent/agent-context.ts +168 -125
  49. package/src/agent/agent-loop.ts +5 -4
  50. package/src/agent/perf-sampler.ts +54 -3
  51. package/src/agent/run-agent.ts +1 -1
  52. package/src/client.ts +191 -71
  53. package/src/directives.ts +3 -3
  54. package/src/display.ts +12 -0
  55. package/src/errors.ts +1 -0
  56. package/src/generated/agentc-commands.ts +571 -0
  57. package/src/index.ts +54 -21
  58. package/src/pause/pause-core.ts +2 -1
  59. package/src/request-context/request-context.ts +1 -1
  60. package/src/runtimes/_cli-agent.ts +306 -122
  61. package/src/runtimes/claude-code.ts +179 -9
  62. package/src/runtimes/claude.ts +1 -1
  63. package/src/runtimes/codex.ts +188 -19
  64. package/src/runtimes/opencode.ts +195 -26
  65. package/src/sandbox/baked-clis.ts +86 -0
  66. package/src/sandbox/exec-stream.ts +1 -2
  67. package/src/sandbox/network-policy.ts +51 -7
  68. package/src/sandbox/providers/e2b.ts +3 -3
  69. package/src/sandbox/providers/vercel.ts +6 -6
  70. package/src/sandbox.ts +8 -2
  71. package/src/step-invocation/invoker.ts +2 -6
  72. package/src/step-invocation/protocol.ts +3 -4
  73. package/src/step-invocation/server.ts +2 -2
  74. package/src/types/api-conversations.ts +366 -23
  75. package/src/types/api-factory.ts +74 -8
  76. package/src/types/api-projects.ts +443 -0
  77. package/src/types/api-runs.ts +5 -0
  78. package/src/types/api-scopes.ts +32 -3
  79. package/src/types/conversation-stream.ts +5 -0
  80. package/src/types/execution-context.ts +1 -1
  81. package/src/types/protocol.ts +67 -1
  82. package/src/types/runtime.ts +8 -2
  83. package/src/types/sandbox-environment.ts +1 -2
  84. package/src/types/workflow-metadata.ts +2 -4
  85. package/src/types/workflow-plan.ts +1 -3
  86. package/src/utils/bundler.ts +88 -19
  87. package/src/workflow-steps/observability.ts +2 -3
  88. package/src/workflow-steps/runner.ts +5 -8
  89. package/src/workflow-steps/types.ts +8 -10
  90. package/src/workflow-steps/workflow.ts +2 -1
  91. package/src/workflows/engine.ts +3 -5
  92. package/src/workflows/invoke-child.ts +2 -2
  93. package/dist/generated/verb-synopsis.d.ts +0 -34
  94. package/dist/pause/__tests__/errors.test.d.ts +0 -1
  95. package/dist/pause/__tests__/wrappers.test.d.ts +0 -1
  96. package/dist/step-invocation/__tests__/protocol.test.d.ts +0 -1
  97. package/src/generated/verb-synopsis.ts +0 -544
@@ -32,6 +32,23 @@ export interface Project {
32
32
  role: ProjectRole;
33
33
  memberCount: number;
34
34
  objectCount: number;
35
+ /** The overview's words and dates (owner 2026-10-05): a short
36
+ * description, and the start and target dates as calendar days
37
+ * (`YYYY-MM-DD`); null until someone sets them. */
38
+ description: string | null;
39
+ startDate: string | null;
40
+ targetDate: string | null;
41
+ }
42
+
43
+ /** What `updateProject` may set: each field absent = untouched, null =
44
+ * cleared. `name` and the details need `write`; `visibility` needs a
45
+ * project owner or a team admin. */
46
+ export interface ProjectUpdate {
47
+ name?: string;
48
+ visibility?: "public" | "private";
49
+ description?: string | null;
50
+ startDate?: string | null;
51
+ targetDate?: string | null;
35
52
  }
36
53
 
37
54
  /** A page of projects, newest-activity first. Cursor-paginated: pass the
@@ -93,6 +110,16 @@ export interface ProjectObjectsPage {
93
110
  next_cursor: string | null;
94
111
  }
95
112
 
113
+ /** How many objects of each kind a project holds for the caller — the
114
+ * whole project's counts under the list's own visibility, never a
115
+ * page's. `GET /projects/:id/objects/counts`. */
116
+ export interface ProjectObjectCounts {
117
+ file: number;
118
+ conversation: number;
119
+ session: number;
120
+ folder: number;
121
+ }
122
+
96
123
  /** One file the caller could NOT contribute to a project when adding a
97
124
  * session — only files the caller OWNS are materialized. `path` is present
98
125
  * only when visible to the caller; paths concealed from the caller are
@@ -138,3 +165,419 @@ export interface RefreshProjectObjectResult {
138
165
  objects: ProjectObject[];
139
166
  skipped: ProjectSkippedFile[];
140
167
  }
168
+
169
+ // ── The project status view (owner 2026-09-27) ─────────────────────────────
170
+
171
+ /** A person on a project status row, identity merged server-side. `email`
172
+ * rides only while they share the workspace with the viewer: the
173
+ * dashboard's label rule (name, else email, else "Teammate") picks. */
174
+ export interface ProjectStatusPerson {
175
+ id: string;
176
+ name: string | null;
177
+ email: string | null;
178
+ image: string | null;
179
+ avatarUrl: string | null;
180
+ }
181
+
182
+ /** Whom a thread waits on (owner 2026-10-01: status "is multiplayer, could
183
+ * be needs Chris or Westan"; it names people). A plan task that waits on a
184
+ * person, as the thread agent wrote it (a teammate, or someone outside by
185
+ * name), or a worker's question owed an answer, which waits on the person
186
+ * who asked for the work (the thread's opening message), else the last
187
+ * person to speak on the thread. */
188
+ export interface ProjectStatusWaitingOn {
189
+ /** The teammate, when known. */
190
+ person: ProjectStatusPerson | null;
191
+ /** How the thread names them: the teammate's name, "Dana's IT team", or
192
+ * "an answer" when a worker's question has nobody to wait on yet. */
193
+ who: string;
194
+ /** What they will do or answer, in the people's terms. */
195
+ what: string | null;
196
+ kind: "task" | "question";
197
+ /** The chat message where the person said it, when the thread agent
198
+ * recorded one: the way to the ask. Null otherwise. */
199
+ messageId: string | null;
200
+ }
201
+
202
+ /** Whom a TASK waits on (owner 2026-10-05: a todo may wait on more than one
203
+ * person's answer or sign-off): every teammate it names, as people, and
204
+ * the words the thread agent (or the person who set it) wrote for whoever
205
+ * they are. */
206
+ export interface ThreadStatusWaitingOn {
207
+ /** The teammates, the one whose act comes first first; empty for a wait
208
+ * on someone the workspace cannot name ("Dana's IT team"). */
209
+ people: ProjectStatusPerson[];
210
+ /** How the thread names them. */
211
+ who: string;
212
+ /** What they will do or answer, in the people's terms; null when a person
213
+ * set the wait without saying for what. */
214
+ what: string | null;
215
+ /** The chat message where the person said it, when one was recorded. */
216
+ messageId: string | null;
217
+ }
218
+
219
+ /** Who a todo is assigned to (owner 2026-10-05, like an issue's assignee):
220
+ * Ivy, or one person. Null on the todo = nobody, after a person took the
221
+ * assignee off. */
222
+ export type ThreadStatusAssignee = { kind: "ivy" } | ({ kind: "person" } & ProjectStatusPerson);
223
+
224
+ /** One plan task of a thread, as the thread agent's plan view reads it. */
225
+ export interface ProjectStatusTask {
226
+ id: string;
227
+ objective: string;
228
+ status: "open" | "done";
229
+ dueAt: string | null;
230
+ overdue: boolean;
231
+ /** Open and waiting on a task that is not done. */
232
+ blocked: boolean;
233
+ criteria: { met: number; total: number };
234
+ /** Open and waiting on a person. */
235
+ waitingOn: ThreadStatusWaitingOn | null;
236
+ assignee: ThreadStatusAssignee | null;
237
+ }
238
+
239
+ /** What a thread's workers are doing, from platform rows: `waiting` a
240
+ * worker's question is owed an answer; `working` a worker has a live turn;
241
+ * `idle` workers exist and none is running; `done` the thread is closed;
242
+ * `none` no worker was started. */
243
+ export type ProjectStatusWorker = "working" | "waiting" | "idle" | "done" | "none";
244
+
245
+ /** One outcome thread born in one of the project's chats. */
246
+ export interface ProjectStatusThread {
247
+ id: string;
248
+ title: string;
249
+ state: "open" | "waiting" | "snoozed" | "closed";
250
+ note: string | null;
251
+ /** The chat the thread was born in, and whether it is public or private. */
252
+ conversation: { id: string; title: string | null; access: "public" | "private" };
253
+ /** The chat message the thread opened on, for a deep link; null if none. */
254
+ openingMessageId: string | null;
255
+ /** Who holds the next move: a person while the thread waits on people,
256
+ * else Ivy. */
257
+ owner: { kind: "ivy" } | ({ kind: "person" } & ProjectStatusPerson);
258
+ /** The people who spoke on the thread, most recent first. */
259
+ people: ProjectStatusPerson[];
260
+ /** The now line: what its running worker is doing (`worker`), else the
261
+ * thread agent's note, its own reading of where things stand (`note`);
262
+ * null when there is neither. `at` is when the text was set (ISO). */
263
+ now: { kind: "worker" | "note"; text: string; at: string | null } | null;
264
+ /** `running`: how many of its workers have a live turn. `line`: while
265
+ * `status` is working, the one-line status its running worker set most
266
+ * recently ("Reading the Fly logs") and when (ISO); null otherwise. */
267
+ worker: { status: ProjectStatusWorker; sessions: number; running: number; line: { text: string; at: string } | null };
268
+ /** Whom the thread waits on, named; empty when it waits on nobody. */
269
+ waitingOn: ProjectStatusWaitingOn[];
270
+ /** The next open task deadline, else the thread agent's check-in. */
271
+ next: { at: string; what: string; kind: "deadline" | "check_in" } | null;
272
+ progress: { done: number; total: number };
273
+ tasks: ProjectStatusTask[];
274
+ overdue: number;
275
+ blocked: number;
276
+ unmetCriteria: number;
277
+ lastMovementAt: string;
278
+ closedAt: string | null;
279
+ }
280
+
281
+ /** What moved: a task done, a thread opened or closed, a result or decision
282
+ * the thread agent raised to the people, a worker's declared result, or a
283
+ * person's answer (`by` names them). */
284
+ export type ProjectStatusActivityKind = "task_done" | "thread_closed" | "thread_opened" | "result" | "decision" | "answer";
285
+
286
+ export interface ProjectStatusActivity {
287
+ at: string;
288
+ kind: ProjectStatusActivityKind;
289
+ threadId: string;
290
+ threadTitle: string;
291
+ text: string;
292
+ by: ProjectStatusPerson | null;
293
+ }
294
+
295
+ /** How many live threads wait on one person (or one named outsider). */
296
+ export interface ProjectStatusWaitingCount {
297
+ person: ProjectStatusPerson | null;
298
+ who: string;
299
+ threads: number;
300
+ }
301
+
302
+ /** The header's counts: "3 in progress, waiting on Chris (2) and Westan (1),
303
+ * 4 done this week". */
304
+ export interface ProjectStatusCounts {
305
+ /** Live threads (open, waiting or snoozed). */
306
+ inProgress: number;
307
+ /** Threads closed in the last seven days. */
308
+ doneThisWeek: number;
309
+ waitingOn: ProjectStatusWaitingCount[];
310
+ }
311
+
312
+ /** GET /projects/:id/status. `threads` (the live ones) is cursor-paginated
313
+ * by last movement; `recentlyClosed` rides the first page only (empty on
314
+ * later pages). The project facts (`sources`, `progress`, `dueAt`,
315
+ * `nextCheckIn`, `activity`) span every thread the viewer may read, on
316
+ * every page. */
317
+ export interface ProjectStatus {
318
+ project: { id: string; name: string; visibility: "public" | "private"; role: ProjectRole };
319
+ /** The chats in the project, where its threads are born. */
320
+ sources: number;
321
+ progress: { done: number; total: number };
322
+ /** The latest deadline of an open task: the project's finish line. */
323
+ dueAt: string | null;
324
+ nextCheckIn: { at: string; threadId: string; threadTitle: string; conversationId: string; with: ProjectStatusPerson | null } | null;
325
+ counts: ProjectStatusCounts;
326
+ threads: ProjectStatusThread[];
327
+ /** Closed in the last seven days. */
328
+ recentlyClosed: ProjectStatusThread[];
329
+ activity: ProjectStatusActivity[];
330
+ generatedAt: string;
331
+ next_cursor: string | null;
332
+ }
333
+
334
+ // ── One thread's status (GET /threads/:id/status) and one chat's work
335
+ // (GET /conversations/:id/work) ───────────────────────────────────────────
336
+
337
+ /** A worker's state as the chat's chips say it: working (a live turn, or
338
+ * parked on a question), starting (nothing produced yet), done, failed,
339
+ * stopped. */
340
+ export type ThreadStatusWorkerState = "starting" | "working" | "done" | "failed" | "stopped";
341
+
342
+ /** The effort a worker was started with; null = the harness's own default. */
343
+ export type ThreadStatusWorkerEffort = "low" | "medium" | "high" | "xhigh" | "max";
344
+
345
+ export interface ThreadStatusWorker {
346
+ /** The worker's session (its page is /conversations/:id). */
347
+ conversationId: string;
348
+ title: string | null;
349
+ state: ThreadStatusWorkerState;
350
+ /** Its one-line status while working; null otherwise. */
351
+ line: { text: string; at: string } | null;
352
+ startedAt: string;
353
+ /** When a settled worker last spoke, or its session ended. */
354
+ finishedAt: string | null;
355
+ /** The result it declared, when it did. */
356
+ result: string | null;
357
+ /** The question it is parked on, while one is owed. */
358
+ asking: string | null;
359
+ /** The model stamped at birth; null = the runtime's own default. */
360
+ model: string | null;
361
+ effort: ThreadStatusWorkerEffort | null;
362
+ /** The harness it runs ("claude-code", "codex", ...); null when unknown. */
363
+ runtime: string | null;
364
+ /** Where it runs: a cloud sandbox or the person's machine; null when
365
+ * unknown. */
366
+ executor: "local" | "cloud" | null;
367
+ /** When its running turn started (ISO); null while no turn runs. */
368
+ workingSince: string | null;
369
+ /** Its machine has a live desktop to show (the Desktop pill's own
370
+ * verdict, for the session's current machine): the chat draws a door to
371
+ * it on the step the worker is on. False for a local session, a machine
372
+ * with no GUI, or a session no longer on a machine. */
373
+ desktopCapable: boolean;
374
+ }
375
+
376
+ /** A message that directed a step: who wrote it and the opening of what
377
+ * they said, where it sits (its chat and, for a reply, the root of its
378
+ * chat thread; null for the chat's top level) and when. */
379
+ export interface ThreadStatusDirector {
380
+ messageId: string;
381
+ conversationId: string;
382
+ threadRootId: string | null;
383
+ /** The person who wrote it, as the status wire names people; null for a
384
+ * message Ivy wrote. */
385
+ author: ProjectStatusPerson | null;
386
+ /** The opening of what they said, in plain words (links read as their
387
+ * labels), capped; null when the message shows no words. */
388
+ excerpt: string | null;
389
+ /** When it was said (ISO). */
390
+ at: string;
391
+ }
392
+
393
+ /** Something a worker produced, filed under the step it was on: a desktop
394
+ * screenshot or a capture pushed with a need (`path`, a drive file), or
395
+ * its declared result (`text`, its words). */
396
+ export interface ThreadStatusOutput {
397
+ id: string;
398
+ kind: "screenshot" | "capture" | "result";
399
+ /** When it landed (ISO). */
400
+ at: string;
401
+ /** The drive path of a picture; null for words. */
402
+ path: string | null;
403
+ /** The words: a result's own, a picture's summary. */
404
+ text: string;
405
+ }
406
+
407
+ /** What a person did on a todo in the chat's todo view (owner 2026-10-05):
408
+ * a comment, or an edit of its assignee, details, status or whom it waits
409
+ * on. The todo's Activity, with the platform's own history. */
410
+ export interface ThreadStatusTodoActivity {
411
+ id: string;
412
+ /** When (ISO). */
413
+ at: string;
414
+ kind: "comment" | "assignee" | "description" | "status" | "waiting_on";
415
+ /** Who did it; null for a person the workspace cannot name. */
416
+ by: ProjectStatusPerson | null;
417
+ /** A comment's words, or the details written; null otherwise. */
418
+ text: string | null;
419
+ /** The assignee set (`kind` assignee); null for nobody. */
420
+ assignee: ThreadStatusAssignee | null;
421
+ /** The status set (`kind` status). */
422
+ status: "open" | "in_progress" | "waiting" | "done" | null;
423
+ /** The people set (`kind` waiting_on). */
424
+ waitingOn: ProjectStatusPerson[];
425
+ }
426
+
427
+ /** One step of the thread's plan. */
428
+ export interface ThreadStatusTodo {
429
+ id: string;
430
+ objective: string;
431
+ status: "open" | "done";
432
+ dueAt: string | null;
433
+ overdue: boolean;
434
+ /** Open and waiting on a step that is not done. */
435
+ blocked: boolean;
436
+ /** The steps it waits on, by their objective. */
437
+ blockedOn: string[];
438
+ /** Open and waiting on people. */
439
+ waitingOn: ThreadStatusWaitingOn | null;
440
+ /** Who it is assigned to: Ivy, one person, or nobody (owner 2026-10-05).
441
+ * Stored on the server; the plan's default is the first person it waits
442
+ * on, else Ivy, and a person's choice stands over the plan's. */
443
+ assignee: ThreadStatusAssignee | null;
444
+ /** The details a person wrote on it, plain text with line breaks; null
445
+ * when none. */
446
+ description: string | null;
447
+ /** What people did on it (edits and comments), oldest first, bounded. */
448
+ activity: ThreadStatusTodoActivity[];
449
+ /** Its done criteria, as counts. */
450
+ criteria: { met: number; total: number };
451
+ /** The thread's worker on this step (the thread agent's stamp), or null. */
452
+ workerConversationId: string | null;
453
+ /** Done, and completed with this note: what it produced, in one line. */
454
+ result: string | null;
455
+ /** When a worker was first put on the step (ISO): its own clock in the
456
+ * chat, never a worker's turn. Null until then. */
457
+ startedAt: string | null;
458
+ /** Done at this moment (ISO): when its check-off lands in the chat. Null
459
+ * while open. */
460
+ completedAt: string | null;
461
+ /** The messages that directed the step (the request or follow-up that
462
+ * added it, then every steer, from another chat thread too), oldest
463
+ * first. Empty = the job's request alone. The step's status shows in the
464
+ * chat thread of each. */
465
+ directedBy: ThreadStatusDirector[];
466
+ /** What the worker produced for the step, newest first (bounded): its
467
+ * screenshots and captures, and its declared result. */
468
+ outputs: ThreadStatusOutput[];
469
+ }
470
+
471
+ /** One of the last things that happened on the thread: a worker's stream
472
+ * event (its type), or the thread's record ("said" for a person's words,
473
+ * "raised" for what the agent put to the people, "worker_result", ...). */
474
+ export interface ThreadStatusEvent {
475
+ at: string;
476
+ kind: string;
477
+ text: string;
478
+ by: ProjectStatusPerson | null;
479
+ }
480
+
481
+ /** One thread's work, as the card under the message that started it reads
482
+ * it: the thread, the root it opened on, the note, its workers and its
483
+ * steps. */
484
+ export interface ChatWorkThread {
485
+ thread: ProjectStatusThread;
486
+ /** The message the thread opened on, in this chat: the job's request —
487
+ * who asked for it, their words, where it sits (the job's started mark
488
+ * shows there) and when. Null when the thread opened on no message here
489
+ * (the chat shows no card for it, but its workers still count in the
490
+ * chat's working status), or the viewer cannot see it. */
491
+ request: ThreadStatusDirector | null;
492
+ /** The job's latest place: the newest agent-authored message after the
493
+ * request in the request's own chat thread (the thread under a room
494
+ * request, or the reply thread the request sits in), else the room's
495
+ * newest after it for a room request answered inline. "Jump to latest"
496
+ * lands here. Null while nothing has answered; the request is then its
497
+ * place. */
498
+ latestMessageId: string | null;
499
+ /** The chat thread that latest message sits in (null for the room's top
500
+ * level); null with no latest message. */
501
+ latestThreadRootId: string | null;
502
+ /** The thread agent's rolling note: where things stand, in its words. */
503
+ summary: string | null;
504
+ /** When the thread was opened (ISO): the job's start, the beginning of
505
+ * its total time. */
506
+ openedAt: string;
507
+ /** Working first, then starting, then what settled (newest first). */
508
+ workers: ThreadStatusWorker[];
509
+ /** The plan's steps, in plan order (oldest first). */
510
+ todos: ThreadStatusTodo[];
511
+ }
512
+
513
+ export interface ThreadStatus extends ChatWorkThread {
514
+ events: ThreadStatusEvent[];
515
+ generatedAt: string;
516
+ }
517
+
518
+ /** GET /conversations/:id/work: the chat's threads, most recently moved
519
+ * first and bounded (the server's CHAT_WORK_THREADS), each with its work.
520
+ * 404 for a chat the viewer cannot read. */
521
+ export interface ChatWork {
522
+ threads: ChatWorkThread[];
523
+ /** The project the chat is filed in, as the viewer may see it; null for
524
+ * a chat in none. A todo's Properties name it. */
525
+ project: { id: string; name: string } | null;
526
+ generatedAt: string;
527
+ }
528
+
529
+ /** PATCH /conversations/:id/todos/:todoId (owner 2026-10-05: "change the
530
+ * assignee", "write details in here myself"): what a person who can read
531
+ * the chat may change on a todo. Each field is optional; one call may
532
+ * carry several. Answers the chat's whole work read (ChatWork), so the
533
+ * surfaces redraw from one truth. */
534
+ export interface ChatTodoPatch {
535
+ /** The one assignee: Ivy, a person who can read the chat, or null for
536
+ * nobody. */
537
+ assignee?: { kind: "ivy" } | { kind: "person"; userId: string } | null;
538
+ /** The details, plain text with line breaks; null or "" clears. */
539
+ description?: string | null;
540
+ /** Where it stands, in the plan's own states: open (not started), in
541
+ * progress, waiting (on the people given, or the ones it already waits
542
+ * on), done. A done todo stays done. */
543
+ status?: "open" | "in_progress" | "waiting" | "done";
544
+ /** Whom it waits on: people who can read the chat (several allowed), and
545
+ * for what; [] or null clears the wait. */
546
+ waitingOn?: { userIds: string[]; what?: string | null } | null;
547
+ }
548
+
549
+ /** POST /conversations/:id/todos/:todoId/comments: a comment on the todo,
550
+ * which lands in its Activity and reaches the thread's agent. Answers the
551
+ * chat's whole work read. */
552
+ export interface ChatTodoComment {
553
+ text: string;
554
+ }
555
+
556
+ // ── The personal status (GET /me/status) ───────────────────────────────────
557
+
558
+ export interface MeStatusCounts {
559
+ waitingOnYou: number;
560
+ inProgress: number;
561
+ /** The viewer's projects with a relevant live thread. */
562
+ projects: number;
563
+ doneThisWeek: number;
564
+ }
565
+
566
+ /** GET /me/status, for the current workspace: the threads relevant to the
567
+ * viewer (they posted, were mentioned, asked, have a to-do, or are in the
568
+ * chat), never the whole workspace. `threads` is cursor-paginated by last
569
+ * movement; `waitingOnYou`, `recentlyClosed` and `activity` ride the first
570
+ * page. The client groups by `chatProjects` (the viewer's project for each
571
+ * chat; a chat absent there is in none of theirs). */
572
+ export interface MeStatus {
573
+ counts: MeStatusCounts;
574
+ /** Live threads a task of which waits on the viewer, or whose worker's
575
+ * question is owed and the viewer asked for the work (else spoke last). */
576
+ waitingOnYou: ProjectStatusThread[];
577
+ threads: ProjectStatusThread[];
578
+ recentlyClosed: ProjectStatusThread[];
579
+ chatProjects: Array<{ conversationId: string; project: { id: string; name: string } }>;
580
+ activity: Array<ProjectStatusActivity & { conversationId: string | null }>;
581
+ generatedAt: string;
582
+ next_cursor: string | null;
583
+ }
@@ -97,6 +97,11 @@ export interface InvokeInlineAndWaitOptions extends InvokeInlineOptions {
97
97
 
98
98
  export interface InvokeResult {
99
99
  id: string;
100
+ /** The run is PARKED for its funding plan's reset: the person whose plan
101
+ * pays for it has every plan out, so nothing runs until `until`, when it
102
+ * starts by itself; `text` is the one line to pass on. Absent for a run
103
+ * that boots now. */
104
+ hold?: { until: string; text: string };
100
105
  }
101
106
 
102
107
  export interface StreamRunLogsOptions {
@@ -36,9 +36,10 @@ export type DocumentCapability = "read" | "write";
36
36
  export type TemplateCapability = "read" | "write" | "invoke" | "see_runs";
37
37
 
38
38
  /** One grant on an artifact scope. `team`/`user` are the editable tiers
39
- * carried on a PUT (full-replace); `session`/`project` are DERIVED,
40
- * read-only arms that appear only on READ payloads (the routes that manage
41
- * them own their mutation — a scope PUT rejects them, 400 `invalid_principal`).
39
+ * carried on a PUT (full-replace); `session`/`project`/`conversation`/
40
+ * `worker` are DERIVED, read-only arms that appear only on READ payloads
41
+ * (the routes that manage them own their mutation — a scope PUT rejects
42
+ * them, 400 `invalid_principal`).
42
43
  *
43
44
  * Session and project grants carry FIXED capabilities `['read','write']`:
44
45
  * the fs-gateway is concealment-only (no read/write dimension at the mount),
@@ -58,6 +59,30 @@ export type ScopeGrant =
58
59
  * being a project member (share implies unshare). Null only for
59
60
  * transitional/legacy rows. */
60
61
  projectObjectId: string | null;
62
+ /** The people in the project: the row's reach. */
63
+ memberCount: number;
64
+ capabilities: string[];
65
+ }
66
+ /** A chat's grant (scope-at-birth, documents): reach derives LIVE from the
67
+ * chat's tier and member rows. `memberCount` is null for a public channel,
68
+ * whose audience is the whole team rather than its member rows. */
69
+ | {
70
+ principal: "conversation";
71
+ conversationId: string;
72
+ conversationTitle: string | null;
73
+ memberCount: number | null;
74
+ capabilities: string[];
75
+ }
76
+ /** The grant a chat's WORKER session holds on what it wrote: not a group
77
+ * of people. The readers of the chat it works for (`chatId`) reach the
78
+ * file through it while the worker's project boundary holds. `title` is
79
+ * the work's (the session's) title. */
80
+ | {
81
+ principal: "worker";
82
+ conversationId: string;
83
+ title: string | null;
84
+ chatId: string;
85
+ chatTitle: string | null;
61
86
  capabilities: string[];
62
87
  };
63
88
 
@@ -71,6 +96,10 @@ export interface ArtifactScope {
71
96
  /** Templates only — the owning conversation binding (a grant source:
72
97
  * members of that conversation reach the template via their role). */
73
98
  conversationId?: string | null;
99
+ /** Documents only — born of a shared chat's work (Ivy's publish, a
100
+ * worker's output for a chat). Such a document is shared by the people
101
+ * who can edit it, whoever its owner is; so is one with no owner. */
102
+ bornOfChatWork?: boolean;
74
103
  grants: ScopeGrant[];
75
104
  }
76
105
 
@@ -130,6 +130,11 @@ export interface ConversationTurnStateEvent {
130
130
  pendingCount: number;
131
131
  at: number;
132
132
  partial: true;
133
+ /** The running turn is WRITING words that will be sent — the typing
134
+ * indicator's one signal (a running turn that thinks, calls tools,
135
+ * reacts or notes is not writing). Absent when the emitter cannot know;
136
+ * consumers inherit their previous frame's value, and `idle` resets it. */
137
+ writing?: boolean;
133
138
  /** When the open turn started (ms) — carried by the connect-time
134
139
  * snapshot frame only; absent on live transition frames. */
135
140
  startedAt?: number | null;
@@ -1,4 +1,4 @@
1
- /** Shared execution context capabilities for workflow functions and steps. */
1
+ /** Shared execution context capabilities for workflow steps. */
2
2
 
3
3
  import type { InvokeAndWaitOptions, RunStatus } from "./api-runs.js";
4
4
  import type { RequestContext } from "../request-context/request-context.js";
@@ -74,6 +74,22 @@ export interface AgentMessageError extends AgentMessageBase {
74
74
  text: string;
75
75
  }
76
76
 
77
+ /** One model's share of a harness's turn-end report (claude-code
78
+ * `result.modelUsage[<model>]`): the same four token classes as the turn
79
+ * totals, plus what the harness reports beside them. `costUsd` is the
80
+ * harness's OWN estimate at its price table — never a bill. Fields the
81
+ * harness did not report are absent, never zeroed. */
82
+ export interface AgentMessageModelUsage {
83
+ inputTokens: number;
84
+ outputTokens: number;
85
+ cacheReadTokens: number;
86
+ cacheCreationTokens: number;
87
+ /** Thinking tokens, already counted inside `outputTokens`. */
88
+ thinkingTokens?: number;
89
+ webSearchRequests?: number;
90
+ costUsd?: number;
91
+ }
92
+
77
93
  export interface AgentMessageUsage extends AgentMessageBase {
78
94
  type: "usage";
79
95
  inputTokens: number;
@@ -82,6 +98,54 @@ export interface AgentMessageUsage extends AgentMessageBase {
82
98
  cacheCreationTokens: number;
83
99
  durationMs: number;
84
100
  numTurns: number;
101
+ /** Reasoning tokens, already counted inside `outputTokens` (codex
102
+ * `turn.completed.usage.reasoning_output_tokens`). Absent when the
103
+ * harness reports no such class. */
104
+ reasoningOutputTokens?: number;
105
+ /** Per-model totals the harness reported beside the turn totals
106
+ * (claude-code `result.modelUsage`): every model the query pipeline
107
+ * called — main loop, subagents, compaction. As the CLI reports them:
108
+ * CUMULATIVE for the guest session (a streaming-input or resumed
109
+ * session carries its earlier turns), so a per-turn share is the
110
+ * difference from the previous report of the same session. Absent when
111
+ * the harness reports none. */
112
+ byModel?: Record<string, AgentMessageModelUsage>;
113
+ /** The harness's own cost estimate for the same scope as `byModel`
114
+ * (claude-code `result.total_cost_usd`): list-price arithmetic, an
115
+ * estimate and never a billing statement. */
116
+ costUsd?: number;
117
+ }
118
+
119
+ /** The harness's reading of the account's PLAN LIMITS (claude-code
120
+ * `rate_limit_event`, emitted whenever its rate-limit information changes
121
+ * — subscription-funded sessions only; the headers it reads exist for
122
+ * claude.ai plans). `status` is the verdict for the request just made:
123
+ * `rejected` means the plan's wall, with `resetsAt` the authoritative
124
+ * reset. `window` names which window the verdict speaks for (the CLI's
125
+ * `rateLimitType`: five_hour, seven_day, seven_day_opus, …). `utilization`
126
+ * is carried only once a window crosses a warning threshold (the CLI omits
127
+ * it while plainly allowed), as the CLI reports it — a 0..1 fraction of
128
+ * the window. Everything the event did not carry is absent; nothing is
129
+ * invented. Additive kind: existing producers never emit it. */
130
+ export interface AgentMessagePlanLimits extends AgentMessageBase {
131
+ type: "plan_limits";
132
+ status: "allowed" | "allowed_warning" | "rejected";
133
+ window?: string;
134
+ /** ISO time the named window resets. */
135
+ resetsAt?: string;
136
+ utilization?: number;
137
+ /** The warning threshold the window crossed (as the CLI reports it). */
138
+ surpassedThreshold?: number;
139
+ /** The plan's extra-usage (overage) lane, when the event spoke of it. */
140
+ overage?: {
141
+ status?: "allowed" | "allowed_warning" | "rejected";
142
+ resetsAt?: string;
143
+ disabledReason?: string;
144
+ inUse?: boolean;
145
+ };
146
+ /** Which spend limit blocked the request when not the member's own cap. */
147
+ limitScope?: string;
148
+ errorCode?: string;
85
149
  }
86
150
 
87
151
  /** LIVE-ONLY incremental usage off the harness's raw provider stream — the
@@ -114,7 +178,8 @@ export interface AgentMessagePlan extends AgentMessageBase {
114
178
  type: "plan";
115
179
  entries: {
116
180
  content: string;
117
- priority: "high" | "medium" | "low";
181
+ /** ACP names one; codex's `--json` plan names none. */
182
+ priority?: "high" | "medium" | "low";
118
183
  status: "pending" | "in_progress" | "completed";
119
184
  }[];
120
185
  }
@@ -280,6 +345,7 @@ export type AgentMessage =
280
345
  | AgentMessageError
281
346
  | AgentMessageUsage
282
347
  | AgentMessageUsageDelta
348
+ | AgentMessagePlanLimits
283
349
  | AgentMessagePlan
284
350
  | AgentMessageTaskNotification
285
351
  | AgentMessageTaskProgress
@@ -256,7 +256,13 @@ export interface ModelExecutionContract {
256
256
  * phase, a spec/transport without stream input, ACP path).
257
257
  * Calls are serialized per turn; never throws.
258
258
  */
259
- injectUserMessage?(text: string): Promise<"delivered" | "pending" | "closed" | "unsupported">;
259
+ injectUserMessage?(
260
+ text: string,
261
+ /** Who the message speaks for when it is not the session user
262
+ * (MidTurnEnvelopeOptions): a relayed person, named, or the thread
263
+ * agent that owns this worker. */
264
+ opts?: { relayedFrom?: string | null; fromOwnerAgent?: boolean },
265
+ ): Promise<"delivered" | "pending" | "closed" | "unsupported">;
260
266
  /**
261
267
  * Request an in-band STEP INTERRUPT of the currently running turn — the
262
268
  * ESC equivalent. Where `injectUserMessage` queues content for the turn
@@ -316,7 +322,7 @@ export interface ModelExecutionContract {
316
322
  * The sandbox provider (e.g. "vercel", "e2b") is an infrastructure concern
317
323
  * configured via SANDBOX_PROVIDER — not part of the runtime definition.
318
324
  * For non-sandbox agents (API calls, etc.) make the call directly in the workflow;
319
- * spawnAgent is a sandbox concept.
325
+ * `agent()` is a sandbox concept.
320
326
  */
321
327
  export interface AgentRuntime<S extends SandboxProvider = SandboxProvider> {
322
328
  create(sandbox: S, opts: RuntimeOptions): ModelExecutionContract;
@@ -79,8 +79,7 @@ export function defineSandboxEnvironment(
79
79
  snapshots: env.snapshots ?? { saveLatest: true },
80
80
  // Mark this as an environment build so the server skips the /factory mount
81
81
  // for its runs — an env build builds a platform image and never touches the
82
- // shared drive; baking a live Archil mount into its snapshot breaks the
83
- // re-mount of every workflow that later boots from it (#13).
82
+ // shared drive, so no live drive mount bakes into its snapshot (#13).
84
83
  environmentBuild: true,
85
84
  })
86
85
  .step({