@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
package/src/errors.ts CHANGED
@@ -1,10 +1,49 @@
1
+ /** Stable machine-readable denial codes on membership/capability-gated
2
+ * mutations (ADR-0045). Reads never carry a code — a visibility denial is
3
+ * a plain 404, indistinguishable from a missing id. */
4
+ export type AuthzErrorCode =
5
+ | "role_read_only"
6
+ | "owner_required"
7
+ | "capability_required"
8
+ | "membership_required"
9
+ | "last_owner"
10
+ | "approver_only";
11
+
1
12
  /** Thrown by AgentComposeClient when the server returns a non-2xx response. */
2
13
  export class AgentComposeError extends Error {
14
+ /** Parsed `Retry-After` response header, in milliseconds, when the server
15
+ * sent one (rate-limit 429s do). Pollers use it to honor the server's own
16
+ * estimate of when the limit window clears instead of guessing. */
17
+ readonly retryAfterMs?: number;
18
+ /** Stable error code on gated-mutation 403s (ADR-0045) — one of
19
+ * `AuthzErrorCode` when the denial is a membership/capability gate;
20
+ * undefined otherwise. */
21
+ readonly code?: string;
22
+ /** The missing capability on a `capability_required` denial
23
+ * (e.g. "write", "invoke", "see_runs"). */
24
+ readonly capability?: string;
25
+
3
26
  constructor(
4
27
  public readonly status: number,
5
28
  message: string,
29
+ opts?: { retryAfterMs?: number; code?: string; capability?: string },
6
30
  ) {
7
31
  super(message);
8
32
  this.name = "AgentComposeError";
33
+ if (opts?.retryAfterMs !== undefined) this.retryAfterMs = opts.retryAfterMs;
34
+ if (opts?.code !== undefined) this.code = opts.code;
35
+ if (opts?.capability !== undefined) this.capability = opts.capability;
9
36
  }
10
37
  }
38
+
39
+ /** Parse a `Retry-After` header into milliseconds. Accepts both RFC 9110
40
+ * forms — delta-seconds (`"120"`) and HTTP-date — and returns `undefined`
41
+ * for anything unparseable so callers fall back to their own backoff. */
42
+ export function parseRetryAfterMs(header: string | null | undefined): number | undefined {
43
+ if (!header) return undefined;
44
+ const seconds = Number(header);
45
+ if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);
46
+ const date = Date.parse(header);
47
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
48
+ return undefined;
49
+ }
package/src/index.ts CHANGED
@@ -16,7 +16,6 @@
16
16
  // Factory functions
17
17
  export { defineRuntime } from "./types/runtime.js";
18
18
  export { defineWorkflow } from "./types/workflow.js";
19
- export type { WorkflowDefinition } from "./types/workflow.js";
20
19
  export { defineSandboxEnvironment } from "./types/sandbox-environment.js";
21
20
  export type { SandboxEnvironmentDefinition } from "./types/sandbox-environment.js";
22
21
 
@@ -31,8 +30,6 @@ export type {
31
30
 
32
31
  // Workflow types and runtime utilities
33
32
  export type {
34
- WorkflowFn,
35
- WorkflowCtx,
36
33
  WorkflowRun,
37
34
  AgentBudget,
38
35
  WorkflowHooks,
@@ -46,7 +43,7 @@ export type { ConnectorRequestRules } from "./types/workflow-metadata.js";
46
43
 
47
44
  // Snapshot entry type re-exported for consumers (dashboard, CLI).
48
45
  export type { RunSnapshotEntry } from "./client.js";
49
- export type { WorkflowPlan, WorkflowStepPlan } from "./types/workflow-plan.js";
46
+ export type { WorkflowPlan, WorkflowStepPlan, WorkflowStepDeliverable } from "./types/workflow-plan.js";
50
47
  export type { BaseExecutionContext, InvokeChild } from "./types/execution-context.js";
51
48
 
52
49
  // Request context — per-run typed bag (tenant identity + freeform).
@@ -108,6 +105,8 @@ export type {
108
105
  export type {
109
106
  SandboxProvider,
110
107
  DesktopSandboxProvider,
108
+ SandboxPtyOpts,
109
+ SandboxPtyHandle,
111
110
  } from "./types/sandbox.js";
112
111
 
113
112
  // HTTP client
@@ -122,6 +121,8 @@ export type {
122
121
  TeamMember, Mention, CreateMentionsInput,
123
122
  EventSubjectType, EventRow, ReportEventInput, ListEventsOptions, ListEventsResult,
124
123
  RunLogLine, ListRunLogsOptions,
124
+ RunListEntry, ListRunsOptions, RunDetail,
125
+ FundingLane, RunFundingStamp, RunFundingUsageRow, RunFundingResponse,
125
126
  RegisteredRuntime, RunState, RunStatus, FactoryRow, SnapshotListEntry, SnapshotListResponse,
126
127
  ApiKey, ApiKeyCreated,
127
128
  UsageRollupRow, UsageResponse,
@@ -130,13 +131,73 @@ export type {
130
131
  SendAgentMessageOptions, SendAgentMessageResponse,
131
132
  AnswerSteerOptions,
132
133
  ResumePauseOptions, ResumePauseResponse, ResumePauseSuccess, ResumePausePending, ResumePauseActor,
134
+ ConversationMessagePart, ConversationRow, ConversationMessageRow,
135
+ ConversationsPage, ConversationDetail, ConversationThread,
136
+ ConversationPageContext, SendConversationMessageInput, SendConversationMessageResult,
137
+ ConversationTurnState, ConversationPresenceSnapshot, StreamConversationOptions, AgentListRow,
138
+ CreateCloudSessionInput, CloudSessionCreated,
139
+ // Cloud-session developer surface (ADR-0052)
140
+ SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked,
141
+ // Session branch proposals (ADR-0053)
142
+ SessionFileChange, SessionChangeSet,
143
+ SessionMergeReportDetail, SessionMergeReport, SessionDiscardReport,
144
+ // Channel-attached sessions (ADR-0057)
145
+ ChannelSessionStatus, ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
146
+ FactoryFileSearchRow, FactoryFolderSearchRow, SearchFactoryFilesOptions, FactoryFileSearchResult, PublicFileLinkState,
147
+ // Membership-scoped authorization (ADR-0045)
148
+ ConversationMemberRole, ConversationMember,
149
+ DocumentCapability, TemplateCapability, ScopeGrant, ArtifactScope,
150
+ RunContext, SetScopeGrantsInput, SetTemplateScopeInput, TemplateDetail,
151
+ // Projects (ADR-0045 projects extension)
152
+ Project, ProjectsPage, ProjectRole, ProjectMember,
153
+ ProjectObject, ProjectObjectsPage, ProjectSkippedFile, ProjectPreviewFile,
154
+ ProjectAddPreview, AddProjectObjectInput, AddProjectObjectResult,
155
+ RefreshProjectObjectResult,
156
+ // Break-glass compliance sessions (ADR-0051)
157
+ ComplianceScopeKind, ComplianceStatus, ComplianceSession, ComplianceAccess,
158
+ RequestComplianceSessionInput, ListComplianceSessionsOptions, ComplianceSessionsPage,
159
+ ListComplianceAccessesOptions, ComplianceAccessesPage,
133
160
  } from "./client.js";
134
161
 
135
162
  // SSE parser — exposed so tests and downstream callers can reuse it.
136
163
  export { parseSseStream } from "./sse.js";
137
164
 
165
+ // Cloud-session UI directives — the marker contract between the in-sandbox
166
+ // `agentc` CLI and the server's cloud executor.
167
+ export {
168
+ renderDirectiveMarker, parseDirectiveMarkers,
169
+ DIRECTIVE_MARKER_PREFIX, DIRECTIVE_MARKER_SUFFIX, MAX_DIRECTIVES_PER_OUTPUT,
170
+ REVISION_SELECTOR_RE,
171
+ ASK_DIRECTIVE_PROMPT_MAX, ASK_DIRECTIVE_MAX_OPTIONS,
172
+ ASK_DIRECTIVE_OPTION_ID_MAX, ASK_DIRECTIVE_OPTION_LABEL_MAX,
173
+ ASK_APPROVER_MIN, ASK_APPROVER_MAX,
174
+ } from "./directives.js";
175
+ export type { CloudDirective } from "./directives.js";
176
+
177
+ // Conversation stream — typed wire events + the frame normalizer, shared by
178
+ // `streamConversation` and clients that run their own SSE loop (the CLI's
179
+ // `agentc session` terminal driver reuses its bridge parser + normalizes
180
+ // through this, so every surface holds the same durable-vs-live invariant).
181
+ export { normalizeConversationStreamEvent } from "./types/conversation-stream.js";
182
+ export type {
183
+ ConversationStreamEvent, ConversationStreamAuthor,
184
+ ConversationPartEvent, ConversationPartPartialEvent, ConversationMessageDoneEvent,
185
+ ConversationErrorEvent, ConversationReactionEvent, ConversationMessageDeletedEvent,
186
+ ConversationReplayContinueEvent, ConversationTurnStateEvent, ConversationPresenceEvent,
187
+ ConversationSessionStatusEvent,
188
+ ConversationPresenceClient, ConversationAgentPresence,
189
+ } from "./types/conversation-stream.js";
190
+
191
+ // ACP client peer — the JSON-RPC client half of ADR-0020. Re-exported for the
192
+ // CLI's local-agent bridge daemon (`agentc bridge`), which is an ACP client
193
+ // over a locally spawned agent process and reuses the exact same peer +
194
+ // session/update normaliser as the sandbox CLI-agent runtimes.
195
+ export { AcpClientPeer, normalizeSessionUpdate, ACP_PROTOCOL_VERSION } from "./runtimes/_acp-client.js";
196
+ export type { AcpAvailableCommand, AcpClientPeerDeps, AcpSessionCaps } from "./runtimes/_acp-client.js";
197
+
138
198
  // Errors and utilities
139
199
  export { AgentComposeError } from "./errors.js";
200
+ export type { AuthzErrorCode } from "./errors.js";
140
201
  export { formatError } from "./utils/errors.js";
141
202
 
142
203
  // Bundling utilities
@@ -170,9 +231,11 @@ export type { GatewayModelId } from "ai";
170
231
  // CLI-agent runtimes — drive an external agentic CLI (codex / amp) inside the
171
232
  // sandbox and stream-parse its JSONL. No heavy npm deps (the CLI lives in the
172
233
  // sandbox image), so these are root-exported like claudeRuntime.
173
- export { createCodexRuntime } from "./runtimes/codex.js";
234
+ export { createCodexRuntime, codexSpec } from "./runtimes/codex.js";
174
235
  export type { CodexRuntimeConfig } from "./runtimes/codex.js";
175
236
  export { default as codexRuntime } from "./runtimes/codex.js";
237
+ // Reasoning-effort level a CLI runtime turn may carry (claude-code / codex).
238
+ export type { CliReasoningEffort } from "./runtimes/_cli-agent.js";
176
239
  export { createAmpRuntime } from "./runtimes/amp.js";
177
240
  export type { AmpRuntimeConfig } from "./runtimes/amp.js";
178
241
  export { default as ampRuntime } from "./runtimes/amp.js";
@@ -188,6 +251,14 @@ export { createDroidRuntime, droidSpec } from "./runtimes/droid.js";
188
251
  export type { DroidRuntimeConfig } from "./runtimes/droid.js";
189
252
  export { default as droidRuntime } from "./runtimes/droid.js";
190
253
 
254
+ // Claude Code as a CLI-agent runtime (in-sandbox `claude -p` / the Zed ACP
255
+ // adapter) — the cloud-hostable counterpart of `claudeRuntime` (claude.ts,
256
+ // which drives the Agent SDK in the calling process and so can never run a
257
+ // server-driven cloud-session turn).
258
+ export { createClaudeCodeRuntime, claudeCodeSpec, CLAUDE_CODE_ACP_ADAPTER, CLAUDE_CODE_THINKING_TOKENS } from "./runtimes/claude-code.js";
259
+ export type { ClaudeCodeRuntimeConfig } from "./runtimes/claude-code.js";
260
+ export { default as claudeCodeRuntime } from "./runtimes/claude-code.js";
261
+
191
262
  // Built-in coding tools for Vercel AI SDK runtime.
192
263
  export { bashTool, codingTools, editTool, readTool, writeTool } from "./tools/index.js";
193
264
  export type { CodingTool } from "./tools/index.js";
@@ -202,7 +273,10 @@ export { createSandbox, reconnectSandbox, killAllSandboxes, killSandboxById,
202
273
  parseSseExecStream, AGENT_COMPOSE_TAG,
203
274
  SANDBOX_VCPUS, DEFAULT_SANDBOX_SIZE, E2B_TEMPLATE_SIZES,
204
275
  isE2bSupportedSize, e2bMachineSpec, e2bBaseTemplate, e2bAgentEnvTemplate,
205
- isPlatformE2bTemplateAlias } from "./sandbox.js";
276
+ isPlatformE2bTemplateAlias,
277
+ // ADR-0038 "Computer" — the per-member persistent desktop machine template.
278
+ E2B_DEVBOX_TEMPLATE, E2B_DEVBOX_SPEC, E2B_DEVBOX_RECIPE_VERSION,
279
+ e2bDevboxTemplateRef } from "./sandbox.js";
206
280
  export { SandboxUnavailableError, SANDBOX_UNAVAILABLE_PREFIX } from "./sandbox-errors.js";
207
281
  export type {
208
282
  SandboxCreateOpts, SandboxNetworkPolicy, SandboxNetworkHeaderTransform,
@@ -210,7 +284,7 @@ export type {
210
284
  SandboxQuotaResult, OwnedSandboxResult, OwnedSandbox, SandboxSize,
211
285
  ParseSseExecStreamOptions, SandboxCommandRunOptions, SandboxCommandResult,
212
286
  } from "./sandbox.js";
213
- export type { SandboxResources } from "./types/workflow-metadata.js";
287
+ export type { SandboxResources, DriveMergePolicy } from "./types/workflow-metadata.js";
214
288
 
215
289
  // Workflow engine
216
290
  export { runWorkflow, WorkflowError, EngineError, classifyError, parseNameVersion } from "./workflows/engine.js";
@@ -231,6 +305,7 @@ export {
231
305
  export type {
232
306
  Step,
233
307
  StepContext,
308
+ StepDeliverable,
234
309
  StepRunResult,
235
310
  Workflow,
236
311
  DefineStepOpts,
@@ -274,7 +349,7 @@ export {
274
349
  } from "./pause/errors.js";
275
350
  export type { PauseErrorCode } from "./pause/errors.js";
276
351
  export type { PauseRequest } from "./pause/pause-core.js";
277
- export type { WaitForEventRequest } from "./pause/wrappers.js";
352
+ export type { WaitForEventRequest, DecisionRequest } from "./pause/wrappers.js";
278
353
  // ADR-0028 — the in-sandbox pause client. `agentc pause` and the gate-pause
279
354
  // processor both pause their OWN run through the server pause API and block
280
355
  // until a human answers (create-then-poll; the VM suspends in place while
@@ -298,7 +373,26 @@ export { agentLoop, parseAgentStatus, DEFAULT_CLAUDE_MODEL } from "./agent/agent
298
373
  export type { AgentLifecycleEvent, AgentLoopOpts, AgentLoopResult } from "./agent/agent-loop.js";
299
374
  export { agent } from "./agent/run-agent.js";
300
375
  export type { AgentOpts } from "./agent/run-agent.js";
301
- export { AGENT_COMPOSE_MANUAL, buildAgentContextDoc, writeAgentContext } from "./agent/agent-context.js";
302
- export type { AgentConnectorInfo } from "./agent/agent-context.js";
376
+ export { AGENT_COMPOSE_MANUAL, buildAgentContextDoc, writeAgentContext, buildAddedSessionBrief } from "./agent/agent-context.js";
377
+ export type { AgentConnectorInfo, AddedSessionBriefParams } from "./agent/agent-context.js";
303
378
  export { AgentMessageSchema, parseAgentResponse } from "./agent/protocol.js";
304
379
  export { importSourceModule, TMP_DIR, LATEST_VERSION } from "./utils/source-loader.js";
380
+
381
+ // The display palette (ADR-0040 §7) — the typed marker contract between
382
+ // `agentc display` and the session executors, plus the promoter that maps
383
+ // native-harness tool traffic onto the platform's rich-card part shapes.
384
+ export {
385
+ DISPLAY_MARKER_KEY, DISPLAY_MARKER_VERSION, PLAN_MAX_ENTRIES, PLAN_ENTRY_MAX_CHARS,
386
+ TABLE_MAX_COLUMNS, TABLE_MAX_ROWS, TABLE_CELL_MAX_CHARS,
387
+ CHART_MAX_SERIES, CHART_MAX_POINTS_PER_SERIES, CHART_LABEL_MAX_CHARS,
388
+ ASK_PROMPT_MAX_CHARS, ASK_MAX_OPTIONS, ASK_OPTION_ID_MAX_CHARS, ASK_OPTION_LABEL_MAX_CHARS,
389
+ serializeDisplayMarker, parseDisplayMarker, findDisplayMarker, clampPlanEntries,
390
+ clampTableData, clampChartSeries, clampChartAxisLabel, clampAskOptions,
391
+ detectAgentcInvocation, shellWords, createDisplayPromoter,
392
+ } from "./display.js";
393
+ export type {
394
+ DisplayMarker, DisplayPlanEntry, DisplayPlanStatus,
395
+ DisplayTableCell, DisplayTableColumn, DisplayTableData,
396
+ DisplayChartKind, DisplayChartPoint, DisplayChartSeries, DisplayAskOption,
397
+ AgentcInvocation, PromotedPart, DisplayPromoter, DisplayPromoterContext,
398
+ } from "./display.js";
@@ -1,21 +1,24 @@
1
1
  /**
2
- * The two opinionated pause wrappers (ADR-0006 §"SDK surface"), each a thin
3
- * closure over `ctx.pause`:
2
+ * The three opinionated pause wrappers (ADR-0006 §"SDK surface", ADR-0042),
3
+ * each a thin closure over `ctx.pause`:
4
4
  *
5
5
  * - `sleep` — a lightweight timed pause; resolves on its own TTL,
6
6
  * skips the snapshot, returns void.
7
7
  * - `waitForEvent` — pause until an event resumes by correlation key.
8
- *
9
- * A typed human/agent decision is NOT a wrapper — it's a plain `ctx.pause`
10
- * with a `schema` (and `payload.options` for the dashboard's answer UI). The
11
- * pause primitive carries the reason (the ask) and the resume value (the
12
- * resolution); there is no separate `requestDecision`.
8
+ * - `requestDecision` — a typed human/agent decision (wire `kind:
9
+ * "decision"`): the prompt/options/approvers ride
10
+ * `payload` for the dashboard's answer UI, and the
11
+ * resume value is the frozen `{ decision: string }`
12
+ * convention. `approvers` travel as RAW member refs
13
+ * (user ids or emails) — the SERVER resolves them
14
+ * against the team roster at pause-insert time and
15
+ * stamps the gate-bearing snapshots (ADR-0042 §2).
13
16
  *
14
17
  * Built from a `PauseFn` so the wrapper logic lives in one place and the step
15
18
  * runner just spreads them onto the context next to `pause`.
16
19
  */
17
20
 
18
- import type { z } from "zod";
21
+ import { z } from "zod";
19
22
  import type { StepPauseRequest } from "../step-invocation/types.js";
20
23
  import type { PauseRequest } from "./pause-core.js";
21
24
 
@@ -23,7 +26,7 @@ import type { PauseRequest } from "./pause-core.js";
23
26
  export type PauseFn = <T = unknown>(req: PauseRequest<T>) => Promise<T>;
24
27
 
25
28
  /** Internal: pause with an explicit wire `kind`. The wrappers stamp
26
- * `sleep`/`event` through this; the public `ctx.pause` is always
29
+ * `decision`/`sleep`/`event` through this; the public `ctx.pause` is always
27
30
  * `custom` and never exposes it. */
28
31
  export type KindedPauseFn = <T = unknown>(req: PauseRequest<T>, kind: StepPauseRequest["kind"]) => Promise<T>;
29
32
 
@@ -35,9 +38,26 @@ export interface WaitForEventRequest<T> {
35
38
  ttlMs?: number;
36
39
  }
37
40
 
41
+ export interface DecisionRequest {
42
+ prompt: string;
43
+ /** Choices the decision UI offers as buttons; free text is always open. */
44
+ options?: Array<string | { label: string; value: string }>;
45
+ /** Team member refs (user ids or emails, ≤ 10) — with any named, ONLY a
46
+ * named approver may resolve the decision, and each gets an Activity
47
+ * ping. Resolution is server-side at pause creation. */
48
+ approvers?: string[];
49
+ /** DecisionDraft artifact rendered alongside the prompt. */
50
+ draft?: Record<string, unknown>;
51
+ ttlMs?: number;
52
+ correlationKey?: string;
53
+ }
54
+
55
+ const DecisionResultSchema = z.object({ decision: z.string() });
56
+
38
57
  export interface PauseWrappers {
39
58
  sleep(durationMs: number): Promise<void>;
40
59
  waitForEvent<T = unknown>(req: WaitForEventRequest<T>): Promise<T>;
60
+ requestDecision(req: DecisionRequest): Promise<{ decision: string }>;
41
61
  }
42
62
 
43
63
  export function buildPauseWrappers(pause: KindedPauseFn): PauseWrappers {
@@ -61,5 +81,20 @@ export function buildPauseWrappers(pause: KindedPauseFn): PauseWrappers {
61
81
  ...(req.ttlMs !== undefined ? { ttlMs: req.ttlMs } : {}),
62
82
  }, "event");
63
83
  },
84
+
85
+ requestDecision(req: DecisionRequest): Promise<{ decision: string }> {
86
+ return pause<{ decision: string }>({
87
+ reason: req.prompt,
88
+ payload: {
89
+ prompt: req.prompt,
90
+ ...(req.options !== undefined ? { options: req.options } : {}),
91
+ ...(req.approvers !== undefined ? { approvers: req.approvers } : {}),
92
+ ...(req.draft !== undefined ? { draft: req.draft } : {}),
93
+ },
94
+ schema: DecisionResultSchema,
95
+ ...(req.ttlMs !== undefined ? { ttlMs: req.ttlMs } : {}),
96
+ ...(req.correlationKey !== undefined ? { correlationKey: req.correlationKey } : {}),
97
+ }, "decision");
98
+ },
64
99
  };
65
100
  }
@@ -60,6 +60,28 @@ const OPTION_KIND_FOR_VERDICT = {
60
60
  deny: ["reject_once", "reject_always"],
61
61
  } as const;
62
62
 
63
+ /** Session-lifecycle capabilities the agent advertised at `initialize`,
64
+ * surfaced (not discarded) so callers can pick a resume strategy
65
+ * (ADR-0037 §3). The in-sandbox CLI-agent runtimes ignore both — they
66
+ * resume via the CLI's own on-disk rollout (ADR-0020 Q4); the bridge
67
+ * daemon attempts `session/resume` only when `resume` is true. */
68
+ export interface AcpSessionCaps {
69
+ /** Agent supports `session/load` — full-history replay as
70
+ * `agent_message_chunk`s. Deliberately NOT used for resume: replaying
71
+ * history would re-emit the transcript as new parts and duplicate the
72
+ * conversation (ADR-0037 alternative D). */
73
+ loadSession: boolean;
74
+ /** Agent supports `session/resume` — context restore with NO replay. */
75
+ resume: boolean;
76
+ }
77
+
78
+ /** One entry of the agent's slash-command palette, as announced by the ACP
79
+ * `available_commands_update` session notification. */
80
+ export interface AcpAvailableCommand {
81
+ name: string;
82
+ description: string;
83
+ }
84
+
63
85
  /** Collaborators `AcpClientPeer` needs from `CliAgentRunner`, kept as a small
64
86
  * injected surface so the peer stays transport-agnostic and unit-testable. */
65
87
  export interface AcpClientPeerDeps {
@@ -80,6 +102,14 @@ export interface AcpClientPeerDeps {
80
102
  /** Turn-scoped abort signal from `sendMessage`. On abort the peer sends
81
103
  * `session/cancel` for the live session. */
82
104
  signal?: AbortSignal;
105
+ /** The agent announced (or changed) its slash-command palette — an ACP
106
+ * `available_commands_update` session NOTIFICATION, which can arrive at
107
+ * any time (typically right after session/new, outside any prompt), so
108
+ * it rides a callback rather than the per-turn message stream. Each call
109
+ * carries the ENTIRE palette (replace semantics). Optional: the
110
+ * in-sandbox runtimes ignore it; the bridge daemon reports it to the
111
+ * server for composer autocomplete. */
112
+ onAvailableCommands?: (commands: AcpAvailableCommand[]) => void;
83
113
  }
84
114
 
85
115
  // ── The single session/update → AgentMessage normaliser ───────────────────────
@@ -270,6 +300,17 @@ export class AcpClientPeer {
270
300
  // answers method-not-found.
271
301
  const handler: Client = {
272
302
  sessionUpdate: (params: acp.SessionNotification): void => {
303
+ // The slash-command palette is session state, not turn content —
304
+ // it can land OUTSIDE any prompt (right after session/new), when
305
+ // no turn queue is being drained, so it goes to its own callback
306
+ // instead of the AgentMessage stream (which drops it).
307
+ if (params.update.sessionUpdate === "available_commands_update") {
308
+ this.deps.onAvailableCommands?.(params.update.availableCommands.map((c) => ({
309
+ name: c.name,
310
+ description: c.description,
311
+ })));
312
+ return;
313
+ }
273
314
  // Push mapped messages onto the queue. Tolerate updates that race in
274
315
  // after cancel (the agent may flush a few before unwinding) — the
275
316
  // normaliser handles them the same way.
@@ -290,9 +331,12 @@ export class AcpClientPeer {
290
331
  * Negotiate the wire protocol at `initialize`. Pins `protocolVersion` to
291
332
  * integer 1 and advertises NO fs / terminal capabilities. Returns the
292
333
  * version the agent negotiated — `CliAgentRunner` compares it to
293
- * `ACP_PROTOCOL_VERSION` to decide ACP-path vs. legacy-JSONL fallback.
334
+ * `ACP_PROTOCOL_VERSION` to decide ACP-path vs. legacy-JSONL fallback —
335
+ * plus the agent's advertised session-lifecycle capabilities (`caps`),
336
+ * which the bridge daemon caches as `agent_sessions.resume_supported`
337
+ * (ADR-0037 §3). Absent capability blocks mean "not supported".
294
338
  */
295
- async initialize(): Promise<{ protocolVersion: number }> {
339
+ async initialize(): Promise<{ protocolVersion: number; caps: AcpSessionCaps }> {
296
340
  const res = await this.conn.initialize({
297
341
  protocolVersion: ACP_PROTOCOL_VERSION,
298
342
  clientCapabilities: {
@@ -300,7 +344,15 @@ export class AcpClientPeer {
300
344
  terminal: false,
301
345
  },
302
346
  });
303
- return { protocolVersion: res.protocolVersion };
347
+ return {
348
+ protocolVersion: res.protocolVersion,
349
+ caps: {
350
+ loadSession: res.agentCapabilities?.loadSession ?? false,
351
+ // Per the ACP schema, `resume` is a capability OBJECT (`{}` =
352
+ // supported), not a boolean — presence is the signal.
353
+ resume: res.agentCapabilities?.sessionCapabilities?.resume != null,
354
+ },
355
+ };
304
356
  }
305
357
 
306
358
  /**
@@ -316,6 +368,23 @@ export class AcpClientPeer {
316
368
  return res.sessionId;
317
369
  }
318
370
 
371
+ /**
372
+ * Resume an existing session by id — ACP `session/resume` (ADR-0037 §3).
373
+ * ADDITIVE and opt-in: the in-sandbox CLI-agent runtimes never call this
374
+ * (their resume path is the CLI's own persisted rollout, see
375
+ * `startSession`); the bridge daemon calls it only when `initialize`
376
+ * advertised `caps.resume`. Unlike `session/load` there is NO history
377
+ * replay — the agent restores its internal context and returns nothing,
378
+ * so nothing is re-emitted through the caller's part sink. Throws on an
379
+ * unknown/expired session id (JSON-RPC error from the agent) — the caller
380
+ * marks the durable row stale and falls back to `startSession` + full
381
+ * re-render. `this.sessionId` is only adopted on success.
382
+ */
383
+ async resumeSession(cwd: string, acpSessionId: string): Promise<void> {
384
+ await this.conn.resumeSession({ sessionId: acpSessionId, cwd, mcpServers: [] });
385
+ this.sessionId = acpSessionId;
386
+ }
387
+
319
388
  /**
320
389
  * Drive one `session/prompt` turn, yielding `AgentMessage`s as the agent
321
390
  * streams `session/update` notifications. Resolves the generator when the