@agent-compose/sdk 0.2.2 → 0.2.4

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 (88) hide show
  1. package/README.md +145 -33
  2. package/dist/agent/agent-loop.d.ts +83 -5
  3. package/dist/agent/run-agent.d.ts +34 -9
  4. package/dist/client.d.ts +247 -99
  5. package/dist/index.d.ts +26 -11
  6. package/dist/index.js +1968 -746
  7. package/dist/processors/builtins.d.ts +35 -0
  8. package/dist/processors/index.d.ts +4 -0
  9. package/dist/processors/processor.d.ts +91 -0
  10. package/dist/processors/processor.test.d.ts +1 -0
  11. package/dist/processors/runner.d.ts +19 -0
  12. package/dist/request-context/index.d.ts +2 -0
  13. package/dist/request-context/request-context.d.ts +159 -0
  14. package/dist/request-context/request-context.test.d.ts +1 -0
  15. package/dist/runtimes/claude.d.ts +27 -50
  16. package/dist/runtimes/openai-desktop.js +1919 -742
  17. package/dist/runtimes/vercel.d.ts +34 -0
  18. package/dist/runtimes/vercel.js +474 -0
  19. package/dist/sandbox.d.ts +29 -25
  20. package/dist/step-invocation/__tests__/invoker.test.d.ts +1 -0
  21. package/dist/step-invocation/__tests__/protocol.test.d.ts +1 -0
  22. package/dist/step-invocation/__tests__/server.test.d.ts +1 -0
  23. package/dist/step-invocation/index.d.ts +25 -0
  24. package/dist/step-invocation/invoker.d.ts +65 -0
  25. package/dist/step-invocation/protocol.d.ts +44 -0
  26. package/dist/step-invocation/server.d.ts +63 -0
  27. package/dist/step-invocation/types.d.ts +72 -0
  28. package/dist/tools/coding.d.ts +49 -0
  29. package/dist/tools/coding.test.d.ts +1 -0
  30. package/dist/tools/index.d.ts +2 -0
  31. package/dist/types/events.d.ts +36 -0
  32. package/dist/types/execution-context.d.ts +22 -0
  33. package/dist/types/runtime.d.ts +32 -0
  34. package/dist/types/sandbox-environment.d.ts +5 -2
  35. package/dist/types/sandbox.d.ts +14 -12
  36. package/dist/types/workflow-metadata.d.ts +51 -0
  37. package/dist/types/workflow-plan.d.ts +19 -0
  38. package/dist/types/workflow.d.ts +57 -17
  39. package/dist/utils/bundler.d.ts +62 -3
  40. package/dist/workflow-steps/__tests__/observability.test.d.ts +1 -0
  41. package/dist/workflow-steps/index.d.ts +10 -0
  42. package/dist/workflow-steps/observability.d.ts +58 -0
  43. package/dist/workflow-steps/runner.d.ts +96 -0
  44. package/dist/workflow-steps/step.d.ts +25 -0
  45. package/dist/workflow-steps/types.d.ts +135 -0
  46. package/dist/workflow-steps/workflow-steps.test.d.ts +1 -0
  47. package/dist/workflow-steps/workflow.d.ts +50 -0
  48. package/dist/workflows/engine.d.ts +27 -13
  49. package/dist/workflows/invoke-child.d.ts +10 -0
  50. package/package.json +25 -15
  51. package/src/agent/agent-loop.ts +197 -26
  52. package/src/agent/run-agent.ts +40 -15
  53. package/src/client.ts +326 -76
  54. package/src/index.ts +124 -10
  55. package/src/processors/builtins.ts +72 -0
  56. package/src/processors/index.ts +15 -0
  57. package/src/processors/processor.ts +103 -0
  58. package/src/processors/runner.ts +42 -0
  59. package/src/request-context/index.ts +17 -0
  60. package/src/request-context/request-context.ts +302 -0
  61. package/src/runtimes/claude.ts +123 -254
  62. package/src/runtimes/vercel.ts +180 -0
  63. package/src/sandbox.ts +53 -21
  64. package/src/step-invocation/index.ts +33 -0
  65. package/src/step-invocation/invoker.ts +204 -0
  66. package/src/step-invocation/protocol.ts +57 -0
  67. package/src/step-invocation/server.ts +184 -0
  68. package/src/step-invocation/types.ts +70 -0
  69. package/src/tools/coding.ts +126 -0
  70. package/src/tools/index.ts +8 -0
  71. package/src/types/events.ts +40 -0
  72. package/src/types/execution-context.ts +30 -0
  73. package/src/types/runtime.ts +24 -0
  74. package/src/types/sandbox-environment.ts +7 -5
  75. package/src/types/sandbox.ts +16 -12
  76. package/src/types/workflow-metadata.ts +84 -0
  77. package/src/types/workflow-plan.ts +24 -0
  78. package/src/types/workflow.ts +139 -25
  79. package/src/utils/bundler.ts +213 -19
  80. package/src/utils/source-loader.ts +2 -2
  81. package/src/workflow-steps/index.ts +30 -0
  82. package/src/workflow-steps/observability.ts +103 -0
  83. package/src/workflow-steps/runner.ts +244 -0
  84. package/src/workflow-steps/step.ts +38 -0
  85. package/src/workflow-steps/types.ts +134 -0
  86. package/src/workflow-steps/workflow.ts +95 -0
  87. package/src/workflows/engine.ts +69 -40
  88. package/src/workflows/invoke-child.ts +29 -0
package/src/client.ts CHANGED
@@ -14,8 +14,10 @@
14
14
  import { ofetch } from "ofetch";
15
15
  import { AgentComposeError } from "./errors.js";
16
16
  import { parseSseStream } from "./sse.js";
17
- import type { RunEvent } from "./types/events.js";
18
17
  import type { SandboxNetworkPolicy } from "./sandbox.js";
18
+ import type { RunEvent } from "./types/events.js";
19
+ import type { WorkflowPlan } from "./types/workflow-plan.js";
20
+ import type { WorkflowManifest } from "./utils/bundler.js";
19
21
 
20
22
  /** UUID-v4-ish — matches the server-side predicate. Used to auto-detect
21
23
  * that `process.env.RUN_ID` was injected by the runner sandbox (rather
@@ -48,13 +50,237 @@ export interface RegisterResult {
48
50
  id: string;
49
51
  name: string;
50
52
  version: string;
51
- runtimes?: Array<{ name: string; version: string; id: string }>;
53
+ runtimes?: RegisteredRuntime[];
54
+ }
55
+
56
+ export interface RegisteredRuntime {
57
+ id: string;
58
+ name: string;
59
+ version: string;
60
+ }
61
+
62
+ export interface RuntimeSourceInput {
63
+ name: string;
64
+ source: string;
65
+ }
66
+
67
+ export interface RegisterWorkflowInput {
68
+ name: string;
69
+ source: string;
70
+ /** Structured attestation produced by `bundleWorkflow`. The server
71
+ * requires this on every registration; it proves the source was
72
+ * bundled by a `defineWorkflow`-aware toolchain. The server validates
73
+ * the manifest's shape and verifies `manifest.sourceHash` matches
74
+ * sha256(source) — the source bytes themselves are never parsed or
75
+ * executed on the server. */
76
+ manifest: WorkflowManifest;
77
+ version?: string;
78
+ schedule?: string;
79
+ runtimes?: RuntimeSourceInput[];
80
+ networkPolicy?: unknown;
81
+ placeholders?: Record<string, string>;
82
+ /** Reference to a snapshot the runner should boot from at run start. */
83
+ snapshot?: string;
84
+ /** If true, runs default to capturing a long-lived sandbox snapshot on success. */
85
+ saveSnapshot?: boolean;
86
+ /** Provider-neutral execution plan detected by the CLI bundler. */
87
+ workflowPlan?: WorkflowPlan;
88
+ /** Workflow Memory extraction config. Defaults to "default" when omitted. */
89
+ memory?: "default" | false | { workflow: string };
90
+ /** Factory slug. Defaults to `"default"`. */
91
+ factorySlug?: string;
92
+ }
93
+
94
+ export type RunState = "running" | "success" | "failed" | "abandoned" | "canceled";
95
+
96
+ export interface InvokeWorkflowOptions {
97
+ /** Per-invocation snapshot override: run UUID, workflow name, or `name@version`. */
98
+ snapshot?: string;
99
+ /** Per-invocation snapshot capture override. */
100
+ saveSnapshot?: boolean;
101
+ /** Per-invocation network policy override. Replaces the template-level
102
+ * policy for this run only — registered metadata is not mutated. */
103
+ networkPolicy?: SandboxNetworkPolicy;
104
+ /** Per-invocation placeholder override. Maps secret names referenced in
105
+ * `networkPolicy` ($VAR) to the values the runner should see for env
106
+ * vars after brokering. Replaces the template-level placeholders for
107
+ * this run only — registered metadata is not mutated. */
108
+ placeholders?: Record<string, string>;
109
+ /** Explicit parent run id. Pass `null` to suppress ambient RUN_ID auto-detection. */
110
+ parentRunId?: string | null;
111
+ /** Agent loop inside the parent run that caused this invoke, when applicable. */
112
+ agentId?: string | null;
113
+ /** Factory slug. Defaults to `"default"`. */
114
+ factorySlug?: string;
115
+ }
116
+
117
+ export interface InvokeAndWaitOptions extends InvokeWorkflowOptions {
118
+ timeoutMs?: number;
119
+ pollIntervalMs?: number;
120
+ }
121
+
122
+ export interface InvokeResult {
123
+ id: string;
124
+ }
125
+
126
+ export interface ListSnapshotsOptions {
127
+ workflow?: string;
128
+ limit?: number;
129
+ }
130
+
131
+ export interface TemplateRow {
132
+ name: string;
133
+ version: string;
134
+ factorySlug: string;
135
+ }
136
+
137
+ export interface ListTemplatesOptions {
138
+ factorySlug?: string;
139
+ }
140
+
141
+ export interface CreateFactoryInput {
142
+ slug: string;
143
+ name: string;
144
+ description?: string;
145
+ }
146
+
147
+ export interface UpdateFactoryInput {
148
+ name?: string;
149
+ description?: string;
150
+ }
151
+
152
+ export interface SecretOptions {
153
+ factorySlug?: string;
154
+ }
155
+
156
+ export interface SetSecretResult {
157
+ key: string;
158
+ }
159
+
160
+ export interface SecretListEntry {
161
+ key: string;
162
+ createdAt: string;
163
+ updatedAt: string;
164
+ }
165
+
166
+ export interface CreateApiKeyInput {
167
+ name?: string;
168
+ scopes?: string[];
169
+ expiresAt?: string;
170
+ factorySlug?: string;
171
+ }
172
+
173
+ export interface StreamRunLogsOptions {
174
+ lastEventId?: number;
175
+ signal?: AbortSignal;
176
+ }
177
+
178
+ export interface RunStatus<TOutput = unknown> {
179
+ id: string;
180
+ status: RunState;
181
+ output?: TOutput;
182
+ }
183
+
184
+ export interface RunDetail<TOutput = unknown> {
185
+ runId: string;
186
+ title: string;
187
+ metadata: Record<string, unknown>;
188
+ outcome: string;
189
+ startedAt: string;
190
+ endedAt: string | null;
191
+ durationMs: number | null;
192
+ failureReason: string | null;
193
+ input: unknown;
194
+ output: TOutput | null;
195
+ lifecycleEvents: Array<{ at: string; type: string; payload: unknown }>;
196
+ children?: RunDetail[];
52
197
  }
53
198
 
54
- export interface RunStatus {
199
+ export interface TimelineEvent {
200
+ kind: string;
201
+ at: string;
202
+ seq: number;
203
+ type: string;
204
+ payload: Record<string, unknown>;
205
+ }
206
+
207
+ export type EventSubjectType = "run" | "agent" | "workflow" | "factory";
208
+
209
+ export interface EventRow {
55
210
  id: string;
56
- status: "running" | "success" | "failed" | "abandoned" | "canceled";
57
- output?: Record<string, unknown>;
211
+ teamId: string;
212
+ subjectType: EventSubjectType | string;
213
+ subjectId: string;
214
+ runId: string | null;
215
+ agentId: string | null;
216
+ workflowId: string | null;
217
+ factoryId: string | null;
218
+ name: string;
219
+ body: unknown;
220
+ summary: string | null;
221
+ confidence: string | null;
222
+ timestamp: string;
223
+ attributes: Record<string, unknown>;
224
+ propagate: boolean;
225
+ idempotencyKey: string | null;
226
+ createdAt: string;
227
+ }
228
+
229
+ export interface ReportEventInput {
230
+ name: string;
231
+ body: unknown;
232
+ summary?: string;
233
+ confidence?: number;
234
+ timestamp?: string | Date;
235
+ attributes?: Record<string, unknown>;
236
+ propagate?: boolean;
237
+ idempotencyKey?: string;
238
+ }
239
+
240
+ export interface ListEventsOptions {
241
+ factorySlug?: string;
242
+ limit?: number;
243
+ /** Case-insensitive substring match. Server uses `ILIKE %name%`, so
244
+ * `"mem"` matches `memory.fact`, `memory.usage`, etc. Pass the
245
+ * full event name for an effectively-exact filter (any string is a
246
+ * substring of itself). */
247
+ name?: string;
248
+ /** Date-range lower bound: only include events at or after this
249
+ * timestamp. Used by the dashboard's range toggle (1h / 24h / 7d
250
+ * / 30d). Same semantics as `from` on the runs list endpoint. */
251
+ from?: string;
252
+ /** Timestamp cursor for "load older" pagination. Pass the
253
+ * `timestamp` of the last row from the previous page; the server
254
+ * returns rows strictly older than that. Distinct from `from`:
255
+ * `from` filters a date range, `before` walks the page boundary.
256
+ * Both can be supplied together for a paginated range-query. */
257
+ before?: string;
258
+ }
259
+
260
+ export interface ListEventsResult {
261
+ events: EventRow[];
262
+ has_more: boolean;
263
+ }
264
+
265
+ /** One captured stdout or stderr line from a run's sandbox subprocess.
266
+ * `id` is per-run monotonically increasing — pass the highest `id`
267
+ * you've seen as `afterId` to paginate forward. */
268
+ export interface RunLogLine {
269
+ id: number;
270
+ at: string;
271
+ stream: "stdout" | "stderr";
272
+ line: string;
273
+ }
274
+
275
+ export interface ListRunLogsOptions {
276
+ /** Return only lines with `id > afterId` (forward pagination). */
277
+ afterId?: number;
278
+ /** Max lines (server clamps to [1, 1000], defaults to 200). */
279
+ limit?: number;
280
+ /** Stream direction. `"asc"` (default) returns the oldest lines first
281
+ * — use with `afterId` to paginate forward. `"desc"` returns the newest
282
+ * lines first — use to fetch the last N lines of a finished run. */
283
+ direction?: "asc" | "desc";
58
284
  }
59
285
 
60
286
  /** Row shape returned by `GET /api-keys`. */
@@ -133,27 +359,7 @@ export class AgentComposeClient {
133
359
 
134
360
  /** Register (or update) a workflow template inside a factory. Defaults to
135
361
  * the team's `default` factory when `factorySlug` is omitted. */
136
- register(payload: {
137
- name: string;
138
- source: string;
139
- version?: string;
140
- schedule?: string;
141
- runtimes?: Array<{ name: string; source: string }>;
142
- networkPolicy?: unknown;
143
- placeholders?: Record<string, string>;
144
- /** Reference to a snapshot the runner should boot from at run start.
145
- * A run UUID, workflow name, or `name@version`. The referenced
146
- * workflow must have been registered with `--build` (or any prior
147
- * successful run with `saveSnapshot: true`). Per-invocation
148
- * `invoke({ snapshot })` overrides this default. */
149
- snapshot?: string;
150
- /** If true, runs default to capturing a long-lived sandbox snapshot on
151
- * success. Individual invocations can override via
152
- * `invoke(..., { saveSnapshot })`. */
153
- saveSnapshot?: boolean;
154
- /** Factory slug. Defaults to `"default"`. */
155
- factorySlug?: string;
156
- }): Promise<RegisterResult> {
362
+ register(payload: RegisterWorkflowInput): Promise<RegisterResult> {
157
363
  const { factorySlug = DEFAULT_FACTORY, ...body } = payload;
158
364
  return this.fetch(templatePath(factorySlug), { method: "POST", body });
159
365
  }
@@ -183,15 +389,8 @@ export class AgentComposeClient {
183
389
  invoke(
184
390
  name: string,
185
391
  input?: Record<string, unknown>,
186
- opts?: {
187
- snapshot?: string;
188
- saveSnapshot?: boolean;
189
- parentRunId?: string | null;
190
- factorySlug?: string;
191
- networkPolicy?: SandboxNetworkPolicy;
192
- placeholders?: Record<string, string>;
193
- },
194
- ): Promise<{ id: string }> {
392
+ opts?: InvokeWorkflowOptions,
393
+ ): Promise<InvokeResult> {
195
394
  const parentRunId = opts?.parentRunId === undefined
196
395
  ? detectAmbientParentRunId()
197
396
  : opts.parentRunId;
@@ -200,11 +399,12 @@ export class AgentComposeClient {
200
399
  method: "POST",
201
400
  body: {
202
401
  input,
203
- ...(opts?.snapshot !== undefined ? { snapshot: opts.snapshot } : {}),
204
- ...(opts?.saveSnapshot !== undefined ? { saveSnapshot: opts.saveSnapshot } : {}),
205
- ...(parentRunId ? { parentRunId } : {}),
402
+ ...(opts?.snapshot !== undefined ? { snapshot: opts.snapshot } : {}),
403
+ ...(opts?.saveSnapshot !== undefined ? { saveSnapshot: opts.saveSnapshot } : {}),
206
404
  ...(opts?.networkPolicy !== undefined ? { networkPolicy: opts.networkPolicy } : {}),
207
405
  ...(opts?.placeholders !== undefined ? { placeholders: opts.placeholders } : {}),
406
+ ...(parentRunId ? { parentRunId } : {}),
407
+ ...(opts?.agentId ? { agentId: opts.agentId } : {}),
208
408
  },
209
409
  });
210
410
  }
@@ -216,33 +416,20 @@ export class AgentComposeClient {
216
416
  * Defaults: `timeoutMs = 30min`, `pollIntervalMs = 1000ms`. Tune down for
217
417
  * tests, tune up for long-running workflows. The parent-child auto-
218
418
  * detection from `invoke()` applies here too. */
219
- async invokeAndWait(
419
+ async invokeAndWait<TOutput = unknown>(
220
420
  name: string,
221
421
  input?: Record<string, unknown>,
222
- opts?: {
223
- snapshot?: string;
224
- saveSnapshot?: boolean;
225
- parentRunId?: string | null;
226
- factorySlug?: string;
227
- networkPolicy?: SandboxNetworkPolicy;
228
- placeholders?: Record<string, string>;
229
- timeoutMs?: number;
230
- pollIntervalMs?: number;
231
- },
232
- ): Promise<RunStatus> {
422
+ opts?: InvokeAndWaitOptions,
423
+ ): Promise<RunStatus<TOutput>> {
233
424
  const timeoutMs = opts?.timeoutMs ?? 30 * 60 * 1000;
234
425
  const pollMs = opts?.pollIntervalMs ?? 1000;
235
- const { id: runId } = await this.invoke(name, input, {
236
- ...(opts?.snapshot !== undefined ? { snapshot: opts.snapshot } : {}),
237
- ...(opts?.saveSnapshot !== undefined ? { saveSnapshot: opts.saveSnapshot } : {}),
238
- ...(opts?.parentRunId !== undefined ? { parentRunId: opts.parentRunId } : {}),
239
- ...(opts?.factorySlug !== undefined ? { factorySlug: opts.factorySlug } : {}),
240
- ...(opts?.networkPolicy !== undefined ? { networkPolicy: opts.networkPolicy } : {}),
241
- ...(opts?.placeholders !== undefined ? { placeholders: opts.placeholders } : {}),
242
- });
426
+ // InvokeAndWaitOptions extends InvokeWorkflowOptions, so we can forward
427
+ // `opts` directly — invoke() picks only the fields it sends, so the
428
+ // extra `timeoutMs` / `pollIntervalMs` never leak into the request body.
429
+ const { id: runId } = await this.invoke(name, input, opts);
243
430
  const deadline = Date.now() + timeoutMs;
244
431
  while (Date.now() < deadline) {
245
- const status = await this.getStatus(runId);
432
+ const status = await this.getStatus<TOutput>(runId);
246
433
  if (status.status === "success" || status.status === "failed" || status.status === "abandoned") {
247
434
  return status;
248
435
  }
@@ -255,7 +442,7 @@ export class AgentComposeClient {
255
442
  }
256
443
 
257
444
  /** List runs this account has captured snapshots for. */
258
- async listSnapshots(opts?: { workflow?: string; limit?: number }): Promise<SnapshotListEntry[]> {
445
+ async listSnapshots(opts?: ListSnapshotsOptions): Promise<SnapshotListEntry[]> {
259
446
  const q = new URLSearchParams();
260
447
  if (opts?.workflow) q.set("workflow", opts.workflow);
261
448
  if (opts?.limit != null) q.set("limit", String(opts.limit));
@@ -271,20 +458,88 @@ export class AgentComposeClient {
271
458
  }
272
459
 
273
460
  /** Poll run status. */
274
- getStatus(runId: string): Promise<RunStatus> {
461
+ getStatus<TOutput = unknown>(runId: string): Promise<RunStatus<TOutput>> {
275
462
  return this.fetch(`/api/v1/workflows/${runId}/status`);
276
463
  }
277
464
 
465
+ /** Full run detail, including input/output and lifecycle events. */
466
+ getRun<TOutput = unknown>(runId: string): Promise<RunDetail<TOutput>> {
467
+ return this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}`);
468
+ }
469
+
470
+ /** Ordered lifecycle timeline for one run. */
471
+ async getRunTimeline(runId: string, opts?: { limit?: number; offset?: number }): Promise<TimelineEvent[]> {
472
+ const q = new URLSearchParams();
473
+ if (opts?.limit !== undefined) q.set("limit", String(opts.limit));
474
+ if (opts?.offset !== undefined) q.set("offset", String(opts.offset));
475
+ const body = await this.fetch<{ events: TimelineEvent[] }>(
476
+ `/api/v1/workflows/${encodeURIComponent(runId)}/timeline${q.toString() ? `?${q}` : ""}`,
477
+ );
478
+ return body.events;
479
+ }
480
+
481
+ /** Report a durable event against a run. Events are late-binding facts
482
+ * like quality.accepted, defect.regression, or intervention.override. */
483
+ async reportEvent(runId: string, input: ReportEventInput): Promise<EventRow> {
484
+ const body = {
485
+ ...input,
486
+ ...(input.timestamp instanceof Date ? { timestamp: input.timestamp.toISOString() } : {}),
487
+ };
488
+ const response = await this.fetch<{ event: EventRow }>(`/api/v1/workflows/${encodeURIComponent(runId)}/events`, {
489
+ method: "POST",
490
+ body,
491
+ });
492
+ return response.event;
493
+ }
494
+
495
+ async listRunEvents(runId: string): Promise<EventRow[]> {
496
+ const body = await this.fetch<{ events: EventRow[] }>(`/api/v1/workflows/${encodeURIComponent(runId)}/events`);
497
+ return body.events;
498
+ }
499
+
500
+ /** List events ingested into a factory, newest first. Supports
501
+ * case-insensitive substring filter (`name`) and timestamp-cursor
502
+ * pagination (`before`). Returns `{ events, has_more }` — the
503
+ * `has_more` flag is the signal to render a "load more" affordance
504
+ * or fire a follow-up request with `before = events.at(-1).timestamp`. */
505
+ async listFactoryEvents(opts?: ListEventsOptions): Promise<ListEventsResult> {
506
+ const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
507
+ const q = new URLSearchParams();
508
+ if (opts?.limit != null) q.set("limit", String(opts.limit));
509
+ if (opts?.name) q.set("name", opts.name);
510
+ if (opts?.from) q.set("from", opts.from);
511
+ if (opts?.before) q.set("before", opts.before);
512
+ return this.fetch<ListEventsResult>(
513
+ `/api/v1/factories/${encodeURIComponent(factorySlug)}/events${q.toString() ? `?${q}` : ""}`,
514
+ );
515
+ }
516
+
517
+ /** Fetch captured stdout / stderr lines for a run, ordered by insertion
518
+ * sequence. Pass `afterId` (the highest `id` from a prior page) for
519
+ * forward pagination — useful for tail-style polling without
520
+ * re-fetching the whole stream. `limit` defaults to 200 server-side and
521
+ * is clamped to [1, 1000]. */
522
+ async listRunLogs(runId: string, opts?: ListRunLogsOptions): Promise<RunLogLine[]> {
523
+ const q = new URLSearchParams();
524
+ if (opts?.afterId !== undefined) q.set("afterId", String(opts.afterId));
525
+ if (opts?.limit !== undefined) q.set("limit", String(opts.limit));
526
+ if (opts?.direction !== undefined) q.set("direction", opts.direction);
527
+ const body = await this.fetch<{ logs: RunLogLine[] }>(
528
+ `/api/v1/workflows/${encodeURIComponent(runId)}/logs${q.toString() ? `?${q}` : ""}`,
529
+ );
530
+ return body.logs;
531
+ }
532
+
278
533
  /** List registered workflow templates. When `factorySlug` is supplied,
279
534
  * scopes to that factory; otherwise returns every template the team can
280
535
  * see across every factory in one round trip — each row carries its
281
536
  * `factorySlug` so callers can route per-template actions to the right
282
537
  * factory. */
283
- async listTemplates(opts?: { factorySlug?: string }): Promise<Array<{ name: string; version: string; factorySlug: string }>> {
538
+ async listTemplates(opts?: ListTemplatesOptions): Promise<TemplateRow[]> {
284
539
  const path = opts?.factorySlug
285
540
  ? templatePath(opts.factorySlug)
286
541
  : "/api/v1/templates";
287
- const body = await this.fetch<{ templates: Array<{ name: string; version: string; factorySlug: string }> }>(path);
542
+ const body = await this.fetch<{ templates: TemplateRow[] }>(path);
288
543
  return body.templates;
289
544
  }
290
545
 
@@ -299,7 +554,7 @@ export class AgentComposeClient {
299
554
 
300
555
  /** Create a factory. `slug` must be lowercase kebab-case and unique
301
556
  * within the team. */
302
- createFactory(payload: { slug: string; name: string; description?: string }): Promise<FactoryRow> {
557
+ createFactory(payload: CreateFactoryInput): Promise<FactoryRow> {
303
558
  return this.fetch<FactoryRow>("/api/v1/factories", { method: "POST", body: payload });
304
559
  }
305
560
 
@@ -309,7 +564,7 @@ export class AgentComposeClient {
309
564
  }
310
565
 
311
566
  /** Rename or describe a factory. */
312
- updateFactory(slug: string, updates: { name?: string; description?: string }): Promise<FactoryRow> {
567
+ updateFactory(slug: string, updates: UpdateFactoryInput): Promise<FactoryRow> {
313
568
  return this.fetch<FactoryRow>(`/api/v1/factories/${encodeURIComponent(slug)}`, { method: "PATCH", body: updates });
314
569
  }
315
570
 
@@ -324,7 +579,7 @@ export class AgentComposeClient {
324
579
  // `factorySlug` defaults to `"default"`.
325
580
 
326
581
  /** Create or update a workflow secret. Value is stored in GCP Secret Manager. */
327
- setSecret(workflowName: string, key: string, value: string, opts?: { factorySlug?: string }): Promise<{ key: string }> {
582
+ setSecret(workflowName: string, key: string, value: string, opts?: SecretOptions): Promise<SetSecretResult> {
328
583
  const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
329
584
  return this.fetch(templatePath(factorySlug, workflowName, "secrets"), {
330
585
  method: "POST",
@@ -333,7 +588,7 @@ export class AgentComposeClient {
333
588
  }
334
589
 
335
590
  /** List secret keys registered for a workflow (metadata only — values are never returned). */
336
- async listSecrets(workflowName: string, opts?: { factorySlug?: string }): Promise<Array<{ key: string; createdAt: string; updatedAt: string }>> {
591
+ async listSecrets(workflowName: string, opts?: SecretOptions): Promise<SecretListEntry[]> {
337
592
  const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
338
593
  const body = await this.fetch<{ secrets: Array<{ secretKey: string; createdAt: string; updatedAt: string }> }>(
339
594
  templatePath(factorySlug, workflowName, "secrets"),
@@ -342,7 +597,7 @@ export class AgentComposeClient {
342
597
  }
343
598
 
344
599
  /** Delete a workflow secret. */
345
- deleteSecret(workflowName: string, key: string, opts?: { factorySlug?: string }): Promise<void> {
600
+ deleteSecret(workflowName: string, key: string, opts?: SecretOptions): Promise<void> {
346
601
  const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
347
602
  return this.fetch(templatePath(factorySlug, workflowName, "secrets", key), { method: "DELETE" });
348
603
  }
@@ -355,12 +610,7 @@ export class AgentComposeClient {
355
610
  *
356
611
  * When `factorySlug` is set, the new key is restricted to that factory.
357
612
  * Factory-scoped keys can only mint other keys bound to the same factory. */
358
- createApiKey(input: {
359
- name?: string;
360
- scopes?: string[];
361
- expiresAt?: string;
362
- factorySlug?: string;
363
- }): Promise<ApiKeyCreated> {
613
+ createApiKey(input: CreateApiKeyInput): Promise<ApiKeyCreated> {
364
614
  return this.fetch<ApiKeyCreated>("/api-keys", { method: "POST", body: input });
365
615
  }
366
616
 
@@ -398,7 +648,7 @@ export class AgentComposeClient {
398
648
  * `lastEventId` enables resume — pass the highest `seq` you've already
399
649
  * processed to receive only events you missed.
400
650
  *
401
- * `signal` can be used to abort the stream from the caller side.
651
+ * `event` can be used to abort the stream from the caller side.
402
652
  *
403
653
  * Uses raw `fetch` (not ofetch) because SSE requires access to the
404
654
  * response's `ReadableStream`, which ofetch consumes when parsing. Auth
@@ -406,7 +656,7 @@ export class AgentComposeClient {
406
656
  * non-2xx responses throw the same `AgentComposeError`. */
407
657
  async *streamRunLogs(
408
658
  runId: string,
409
- opts?: { lastEventId?: number; signal?: AbortSignal },
659
+ opts?: StreamRunLogsOptions,
410
660
  ): AsyncGenerator<RunEvent> {
411
661
  const headers: Record<string, string> = { Authorization: `Bearer ${this.apiKey}` };
412
662
  if (opts?.lastEventId && opts.lastEventId > 0) {