@agent-compose/sdk 0.2.5 → 0.3.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.
package/README.md CHANGED
@@ -64,10 +64,10 @@ export default defineWorkflow({
64
64
  ```
65
65
 
66
66
  `defineWorkflow` is a thin sugar — it returns the bare `run` function with
67
- `networkPolicy` / `placeholders` / `snapshot` / `saveSnapshot` attached as
68
- metadata that the bundler picks up at registration time. A plain
69
- `export default async (ctx, sandbox) => {...}` is also valid; you just lose
70
- the metadata channel.
67
+ `networkPolicy` / `placeholders` / `snapshots` / `memory` / `postRunHooks`
68
+ attached as metadata that the bundler picks up at registration time. A
69
+ plain `export default async (ctx, sandbox) => {...}` is also valid; you
70
+ just lose the metadata channel.
71
71
 
72
72
  ### What the workflow can do with `ctx`
73
73
 
@@ -183,7 +183,7 @@ await client.register({
183
183
  runtimes: bundled.runtimes, // [{ name, source }] — embedded so the runner has them locally
184
184
  schedule: "*/30 * * * *", // optional cron
185
185
  factorySlug: "default", // optional — defaults to "default"
186
- // snapshot, saveSnapshot, networkPolicy, placeholders — all optional
186
+ // snapshots, memory, postRunHooks, networkPolicy, placeholders — all optional
187
187
  });
188
188
  ```
189
189
 
@@ -216,9 +216,10 @@ async run() { return … } })` resolves to). `setMetadata()` writes to a
216
216
  separate `metadata` field — useful for "side-channel" facts (PR url, plan
217
217
  url) without polluting the structured return.
218
218
 
219
- `invoke` and `invokeAndWait` both accept `{ factorySlug, snapshot,
220
- saveSnapshot, parentRunId }` as the third argument. `factorySlug` defaults
221
- to `"default"`.
219
+ `invoke` and `invokeAndWait` both accept `{ factorySlug, snapshots,
220
+ memory, postRunHooks, networkPolicy, placeholders, parentRunId }` as the
221
+ third argument. Per-invocation `snapshots` merges field-by-field with
222
+ the registered default. `factorySlug` defaults to `"default"`.
222
223
 
223
224
  ### Auto parent/child tracing
224
225
 
@@ -348,26 +349,37 @@ CLI equivalent: `agentc usage`.
348
349
  ## Snapshots (replay-friendly sandboxes)
349
350
 
350
351
  Long-running workflows can capture the runner sandbox as a Vercel snapshot
351
- on success (`saveSnapshot: true`). Other workflows reference that snapshot
352
- via the `snapshot` field to boot into the same prepared VM (deps installed,
353
- repo cloned, etc.) instead of repeating setup.
352
+ on success (`snapshots: { saveLatest: true }`). Other workflows reference
353
+ that snapshot via `snapshots.bootFrom` to boot into the same prepared VM
354
+ (deps installed, repo cloned, etc.) instead of repeating setup.
354
355
 
355
356
  ```ts
356
357
  // Capture per-invocation:
357
- await client.invoke("my-workflow", input, { saveSnapshot: true });
358
+ await client.invoke("my-workflow", input, { snapshots: { saveLatest: true } });
358
359
 
359
- // Boot from a different snapshot per invocation (per-invocation override):
360
- await client.invoke("my-workflow", input, { snapshot: "<run-id-or-name>" });
360
+ // Boot from a captured snapshot — pick the id from `agentc snapshot
361
+ // list` or the dashboard snapshots page:
362
+ await client.invoke("my-workflow", input, {
363
+ snapshots: { bootFrom: { snapshotId: "snap_…" } },
364
+ });
365
+
366
+ // Set a default at registration time:
367
+ defineWorkflow({ run, snapshots: { saveLatest: true } });
361
368
 
362
- // Or set a default at registration time:
363
- defineWorkflow({ run, saveSnapshot: true });
369
+ // Retain every step's snapshot (not just the latest):
370
+ defineWorkflow({ run, snapshots: { saveLatest: true, retainSteps: true } });
364
371
 
365
372
  // Browse / clean up:
366
- const snaps = await client.listSnapshots({ workflow: "my-workflow", limit: 50 });
367
- await client.deleteSnapshot(snaps[0].runId);
373
+ const page = await client.listSnapshotsPage({ workflow: "my-workflow", limit: 50 });
374
+ const snaps = page.data;
375
+ await client.deleteRunSnapshot(snaps[0].runId, snaps[0].snapshotId);
368
376
  ```
369
377
 
370
- CLI equivalents: `agentc snapshot list` / `agentc snapshot delete <run-id>`.
378
+ CLI equivalents: `agentc snapshot list` / `agentc snapshot delete <run-id> <snapshot-id>`.
379
+
380
+ ### Per-invocation overrides merge field-by-field
381
+
382
+ The `snapshots` object on `invoke()` is **merged** with the registered template's `snapshots` config — you can override `bootFrom` alone without losing `saveLatest`, or vice versa. Same for `memory` (boolean) and `postRunHooks` (string[]).
371
383
 
372
384
  ---
373
385
 
package/dist/client.d.ts CHANGED
@@ -13,12 +13,20 @@
13
13
  import type { SandboxNetworkPolicy } from "./sandbox.js";
14
14
  import type { RunEvent } from "./types/events.js";
15
15
  import type { WorkflowPlan } from "./types/workflow-plan.js";
16
+ import type { SnapshotConfig, IOSchema } from "./types/workflow-metadata.js";
16
17
  import type { WorkflowManifest } from "./utils/bundler.js";
17
18
  export interface RegisterResult {
18
19
  id: string;
19
20
  name: string;
20
21
  version: string;
21
22
  runtimes?: RegisteredRuntime[];
23
+ /** Non-fatal advisories from the server. Surfaced at register time so the
24
+ * operator sees them while still in front of the terminal — currently
25
+ * covers "memory extraction is configured but its workflow is not
26
+ * registered in this factory". Empty/undefined when registration was
27
+ * cleanly resolved against everything the workflow declares it
28
+ * depends on. */
29
+ warnings?: string[];
22
30
  }
23
31
  export interface RegisteredRuntime {
24
32
  id: string;
@@ -44,25 +52,32 @@ export interface RegisterWorkflowInput {
44
52
  runtimes?: RuntimeSourceInput[];
45
53
  networkPolicy?: unknown;
46
54
  placeholders?: Record<string, string>;
47
- /** Reference to a snapshot the runner should boot from at run start. */
48
- snapshot?: string;
49
- /** If true, runs default to capturing a long-lived sandbox snapshot on success. */
50
- saveSnapshot?: boolean;
55
+ /** All snapshot config — `bootFrom` (where to restore at run start),
56
+ * `save`, `retain`. See `WorkflowMetadata.snapshots`. */
57
+ snapshots?: SnapshotConfig;
51
58
  /** Provider-neutral execution plan detected by the CLI bundler. */
52
59
  workflowPlan?: WorkflowPlan;
53
- /** Workflow Memory extraction config. Defaults to "default" when omitted. */
54
- memory?: "default" | false | {
55
- workflow: string;
56
- };
60
+ /** Run the built-in memory extractor after this workflow completes.
61
+ * Opt-in; defaults to false when omitted. */
62
+ memory?: boolean;
63
+ /** Ordered list of workflow names that run as post-hooks after this
64
+ * workflow completes. The memory extractor (when `memory: true`)
65
+ * runs as an additional hook alongside these. */
66
+ postRunHooks?: readonly string[];
67
+ /** Input schema extracted from the workflow's `input` zod schema. */
68
+ inputSchema?: IOSchema;
69
+ /** Output schema extracted from the workflow's `output` zod schema. */
70
+ outputSchema?: IOSchema;
57
71
  /** Factory slug. Defaults to `"default"`. */
58
72
  factorySlug?: string;
59
73
  }
60
74
  export type RunState = "running" | "success" | "failed" | "abandoned" | "canceled";
61
75
  export interface InvokeWorkflowOptions {
62
- /** Per-invocation snapshot override: run UUID, workflow name, or `name@version`. */
63
- snapshot?: string;
64
- /** Per-invocation snapshot capture override. */
65
- saveSnapshot?: boolean;
76
+ /** Per-invocation snapshot config override. `snapshots.bootFrom`
77
+ * replaces the template's boot source; `snapshots.saveLatest` and
78
+ * `snapshots.retainSteps` override capture mode. Anything omitted
79
+ * falls back to the template's registered default. */
80
+ snapshots?: SnapshotConfig;
66
81
  /** Per-invocation network policy override. Replaces the template-level
67
82
  * policy for this run only — registered metadata is not mutated. */
68
83
  networkPolicy?: SandboxNetworkPolicy;
@@ -71,6 +86,13 @@ export interface InvokeWorkflowOptions {
71
86
  * vars after brokering. Replaces the template-level placeholders for
72
87
  * this run only — registered metadata is not mutated. */
73
88
  placeholders?: Record<string, string>;
89
+ /** Per-invocation memory-extractor override — `false` skips the
90
+ * built-in memory hook for this run; omitting leaves the registered
91
+ * default in place. */
92
+ memory?: boolean;
93
+ /** Per-invocation post-hook override — replaces the registered
94
+ * `postRunHooks` array for this run only. */
95
+ postRunHooks?: readonly string[];
74
96
  /** Explicit parent run id. Pass `null` to suppress ambient RUN_ID auto-detection. */
75
97
  parentRunId?: string | null;
76
98
  /** Agent loop inside the parent run that caused this invoke, when applicable. */
@@ -86,8 +108,12 @@ export interface InvokeResult {
86
108
  id: string;
87
109
  }
88
110
  export interface ListSnapshotsOptions {
111
+ /** Factory slug. Defaults to `"default"`. */
112
+ factorySlug?: string;
89
113
  workflow?: string;
90
114
  limit?: number;
115
+ /** Cursor returned as `next_cursor` from `listSnapshotsPage`. */
116
+ before?: string;
91
117
  }
92
118
  export interface TemplateRow {
93
119
  name: string;
@@ -275,32 +301,55 @@ export interface CancelRunResponse {
275
301
  canceledAt: string;
276
302
  }
277
303
  export interface SnapshotListEntry {
304
+ snapshotId: string;
305
+ kind: "latest" | "step";
306
+ stepIndex: number | null;
278
307
  runId: string;
279
308
  workflow: string | null;
280
309
  version: string | null;
281
- vercelSnapshotId: string;
282
- endedAt: string | null;
310
+ createdAt: string | null;
311
+ /** Provider-reported on-disk size, or null when unavailable. */
312
+ sizeBytes: number | null;
313
+ }
314
+ export interface SnapshotListResponse {
315
+ object: "list";
316
+ data: SnapshotListEntry[];
317
+ has_more: boolean;
318
+ next_cursor: string | null;
319
+ }
320
+ export interface RunSnapshotEntry {
321
+ snapshotId: string;
322
+ /** `"latest"` = current/last pointer on the run.
323
+ * `"step"` = retained per-step snapshot. */
324
+ kind: "latest" | "step";
325
+ stepIndex: number | null;
326
+ createdAt: string | null;
327
+ /** Provider-reported on-disk size, or null when unavailable. */
328
+ sizeBytes: number | null;
329
+ }
330
+ export interface AgentComposeClientOptions {
331
+ /** Your team's API key — minted from the dashboard or `agentc keys create`.
332
+ * Required. Resolved from `process.env.AGENT_COMPOSE_API_KEY` when omitted. */
333
+ apiKey?: string;
334
+ /** Override the API host. Defaults to `process.env.AGENT_COMPOSE_URL` if
335
+ * set, otherwise the public host (`https://api.agentcompose.ai`). Most
336
+ * callers should leave this unset — the only consumers that need it are
337
+ * the CLI's local-dev path and tests that point at a fake server. */
338
+ baseUrl?: string;
283
339
  }
284
340
  export declare class AgentComposeClient {
285
341
  private readonly fetch;
286
342
  private readonly baseUrl;
287
343
  private readonly apiKey;
288
- constructor(baseUrl: string, apiKey: string);
344
+ constructor(options?: AgentComposeClientOptions);
289
345
  /** Register (or update) a workflow template inside a factory. Defaults to
290
346
  * the team's `default` factory when `factorySlug` is omitted. */
291
347
  register(payload: RegisterWorkflowInput): Promise<RegisterResult>;
292
348
  /** Invoke a workflow. Returns run ID immediately — workflow runs asynchronously.
293
349
  *
294
- * `snapshot`: per-invocation override of the template-level `snapshot`
295
- * field. Same forms — a run UUID, a workflow name, or `name@version`.
296
- * Use this when one template needs to boot from many different
297
- * snapshots (e.g. a benchmark workflow running against a different
298
- * starting state per invocation). Does not change the template's
299
- * registered default; only affects this run.
300
- *
301
- * `saveSnapshot`: `true` overrides the workflow default; `false` opts
302
- * out. When the run captures a snapshot, its id is stamped on the row
303
- * and can be referenced as the `snapshot` field on other workflows.
350
+ * `snapshots`: per-invocation snapshot config override. `bootFrom`
351
+ * accepts `{ snapshotId }`; `saveLatest` / `retainSteps` control
352
+ * capture. Merged field-by-field with the registered default.
304
353
  *
305
354
  * `parentRunId`: links the new run to another of the same account's
306
355
  * currently-running runs. When omitted, the client auto-detects from
@@ -320,10 +369,18 @@ export declare class AgentComposeClient {
320
369
  * tests, tune up for long-running workflows. The parent-child auto-
321
370
  * detection from `invoke()` applies here too. */
322
371
  invokeAndWait<TOutput = unknown>(name: string, input?: Record<string, unknown>, opts?: InvokeAndWaitOptions): Promise<RunStatus<TOutput>>;
323
- /** List runs this account has captured snapshots for. */
372
+ /** List captured snapshots in a factory. */
324
373
  listSnapshots(opts?: ListSnapshotsOptions): Promise<SnapshotListEntry[]>;
374
+ /** List one page of captured snapshots in a factory. */
375
+ listSnapshotsPage(opts?: ListSnapshotsOptions): Promise<SnapshotListResponse>;
325
376
  /** Delete the snapshot captured by a specific run. Frees Vercel storage. */
326
377
  deleteSnapshot(runId: string): Promise<void>;
378
+ /** List all snapshots captured for a single run — the latest pointer
379
+ * plus any retained per-step rows (`snapshots: { retainSteps: true }`). */
380
+ listRunSnapshots(runId: string): Promise<RunSnapshotEntry[]>;
381
+ /** Delete one snapshot from a run by id. Walks both the latest pointer
382
+ * and the retention rows; provider-side delete is best-effort. */
383
+ deleteRunSnapshot(runId: string, snapshotId: string): Promise<void>;
327
384
  /** Poll run status. */
328
385
  getStatus<TOutput = unknown>(runId: string): Promise<RunStatus<TOutput>>;
329
386
  /** Full run detail, including input/output and lifecycle events. */
package/dist/index.d.ts CHANGED
@@ -16,7 +16,8 @@ export type { WorkflowDefinition } from "./types/workflow.js";
16
16
  export { defineSandboxEnvironment } from "./types/sandbox-environment.js";
17
17
  export type { SandboxEnvironmentDefinition } from "./types/sandbox-environment.js";
18
18
  export type { AgentRuntime, McpServerConfig, ModelExecutionContract, ToolCallGateResult, RuntimeOptions, } from "./types/runtime.js";
19
- export type { WorkflowFn, WorkflowCtx, WorkflowRun, AgentBudget, WorkflowHooks, } from "./types/workflow.js";
19
+ export type { WorkflowFn, WorkflowCtx, WorkflowRun, AgentBudget, WorkflowHooks, SnapshotConfig, BootSnapshot, IOSchema, OutputSchema, WorkflowMemoryConfig, } from "./types/workflow.js";
20
+ export type { RunSnapshotEntry } from "./client.js";
20
21
  export type { WorkflowPlan, WorkflowStepPlan } from "./types/workflow-plan.js";
21
22
  export type { BaseExecutionContext, InvokeChild } from "./types/execution-context.js";
22
23
  export { RequestContext, ReservedKeyError, NonSerialisableValueError, AC_RESERVED_PREFIX, AC_TEAM_ID, AC_RUN_ID, AC_WORKFLOW_ID, AC_FACTORY_ID, AC_API_KEY_SCOPES, AC_PARENT_RUN_ID, AC_ABORT_SIGNAL, } from "./request-context/index.js";
@@ -26,7 +27,7 @@ export type { Processor, ProcessorContext, ProcessorVerdict, ToolCall, } from ".
26
27
  export type { AgentMessage, AgentMessageInit, AgentMessageText, AgentMessageThinking, AgentMessageToolUse, AgentMessageToolResult, AgentMessageDone, AgentMessageError, AgentMessageUsage, AgentStatus, } from "./types/protocol.js";
27
28
  export type { SandboxProvider, DesktopSandboxProvider, } from "./types/sandbox.js";
28
29
  export { AgentComposeClient } from "./client.js";
29
- export type { RegisterResult, RegisterWorkflowInput, RuntimeSourceInput, InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, ListSnapshotsOptions, TemplateRow, ListTemplatesOptions, CreateFactoryInput, UpdateFactoryInput, SecretOptions, SetSecretResult, SecretListEntry, CreateApiKeyInput, StreamRunLogsOptions, EventSubjectType, EventRow, ReportEventInput, ListEventsOptions, ListEventsResult, RunLogLine, ListRunLogsOptions, RegisteredRuntime, RunState, RunStatus, FactoryRow, SnapshotListEntry, ApiKey, ApiKeyCreated, UsageRollupRow, UsageResponse, CancelRunResponse, } from "./client.js";
30
+ export type { RegisterResult, RegisterWorkflowInput, RuntimeSourceInput, InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, ListSnapshotsOptions, TemplateRow, ListTemplatesOptions, CreateFactoryInput, UpdateFactoryInput, SecretOptions, SetSecretResult, SecretListEntry, CreateApiKeyInput, StreamRunLogsOptions, EventSubjectType, EventRow, ReportEventInput, ListEventsOptions, ListEventsResult, RunLogLine, ListRunLogsOptions, RegisteredRuntime, RunState, RunStatus, FactoryRow, SnapshotListEntry, SnapshotListResponse, ApiKey, ApiKeyCreated, UsageRollupRow, UsageResponse, CancelRunResponse, } from "./client.js";
30
31
  export { parseSseStream } from "./sse.js";
31
32
  export { AgentComposeError } from "./errors.js";
32
33
  export { formatError } from "./utils/errors.js";
package/dist/index.js CHANGED
@@ -481,14 +481,18 @@ function extractMetadata(source) {
481
481
  out.networkPolicy = freezeMetadataValue(source.networkPolicy);
482
482
  if (source.placeholders !== undefined)
483
483
  out.placeholders = Object.freeze({ ...source.placeholders });
484
- if (source.snapshot !== undefined)
485
- out.snapshot = source.snapshot;
486
- if (source.saveSnapshot !== undefined)
487
- out.saveSnapshot = source.saveSnapshot;
484
+ if (source.inputSchema !== undefined)
485
+ out.inputSchema = freezeMetadataValue(source.inputSchema);
486
+ if (source.outputSchema !== undefined)
487
+ out.outputSchema = freezeMetadataValue(source.outputSchema);
488
+ if (source.snapshots !== undefined)
489
+ out.snapshots = Object.freeze({ ...source.snapshots });
488
490
  if (source.processors !== undefined)
489
491
  out.processors = Object.freeze([...source.processors]);
490
492
  if (source.memory !== undefined)
491
- out.memory = typeof source.memory === "object" && source.memory !== null ? Object.freeze({ ...source.memory }) : source.memory;
493
+ out.memory = source.memory;
494
+ if (source.postRunHooks !== undefined)
495
+ out.postRunHooks = Object.freeze([...source.postRunHooks]);
492
496
  return Object.freeze(out);
493
497
  }
494
498
  function freezeMetadataValue(value) {
@@ -583,7 +587,8 @@ function defineSandboxEnvironment(env) {
583
587
  throw new Error(`defineSandboxEnvironment(${env.name}): 'setup' must be a function`);
584
588
  }
585
589
  return defineWorkflow({
586
- saveSnapshot: env.saveSnapshot ?? true,
590
+ snapshots: env.snapshots ?? { saveLatest: true },
591
+ memory: env.memory ?? false,
587
592
  run: async (_ctx, sandbox) => env.setup(sandbox)
588
593
  });
589
594
  }
@@ -695,12 +700,18 @@ function templatePath(factorySlug, ...rest) {
695
700
  const tail = rest.length > 0 ? "/" + rest.map(encodeURIComponent).join("/") : "";
696
701
  return `/api/v1/factories/${encodeURIComponent(factorySlug)}/templates${tail}`;
697
702
  }
703
+ var PUBLIC_API_HOST = "https://api.agentcompose.ai";
698
704
 
699
705
  class AgentComposeClient {
700
706
  fetch;
701
707
  baseUrl;
702
708
  apiKey;
703
- constructor(baseUrl, apiKey) {
709
+ constructor(options = {}) {
710
+ const apiKey = options.apiKey ?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_API_KEY : undefined) ?? "";
711
+ if (!apiKey) {
712
+ throw new Error("AgentComposeClient: `apiKey` is required. Pass it as `{ apiKey }` " + "or set the AGENT_COMPOSE_API_KEY env var.");
713
+ }
714
+ const baseUrl = options.baseUrl ?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_URL : undefined) ?? PUBLIC_API_HOST;
704
715
  this.baseUrl = baseUrl.replace(/\/$/, "");
705
716
  this.apiKey = apiKey;
706
717
  this.fetch = ofetch.create({
@@ -723,10 +734,11 @@ class AgentComposeClient {
723
734
  method: "POST",
724
735
  body: {
725
736
  input,
726
- ...opts?.snapshot !== undefined ? { snapshot: opts.snapshot } : {},
727
- ...opts?.saveSnapshot !== undefined ? { saveSnapshot: opts.saveSnapshot } : {},
737
+ ...opts?.snapshots !== undefined ? { snapshots: opts.snapshots } : {},
728
738
  ...opts?.networkPolicy !== undefined ? { networkPolicy: opts.networkPolicy } : {},
729
739
  ...opts?.placeholders !== undefined ? { placeholders: opts.placeholders } : {},
740
+ ...opts?.memory !== undefined ? { memory: opts.memory } : {},
741
+ ...opts?.postRunHooks !== undefined ? { postRunHooks: opts.postRunHooks } : {},
730
742
  ...parentRunId ? { parentRunId } : {},
731
743
  ...opts?.agentId ? { agentId: opts.agentId } : {}
732
744
  }
@@ -747,16 +759,29 @@ class AgentComposeClient {
747
759
  throw new AgentComposeError(504, `invokeAndWait: run ${runId} did not settle within ${timeoutMs}ms`);
748
760
  }
749
761
  async listSnapshots(opts) {
762
+ const body = await this.listSnapshotsPage(opts);
763
+ return body.data;
764
+ }
765
+ async listSnapshotsPage(opts) {
766
+ const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
750
767
  const q2 = new URLSearchParams;
751
768
  if (opts?.workflow)
752
769
  q2.set("workflow", opts.workflow);
753
770
  if (opts?.limit != null)
754
771
  q2.set("limit", String(opts.limit));
755
- const body = await this.fetch(`/api/v1/snapshots${q2.toString() ? `?${q2}` : ""}`);
756
- return body.data;
772
+ if (opts?.before)
773
+ q2.set("before", opts.before);
774
+ return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/snapshots${q2.toString() ? `?${q2}` : ""}`);
757
775
  }
758
776
  deleteSnapshot(runId) {
759
- return this.fetch(`/api/v1/workflows/${runId}/snapshot`, { method: "DELETE" });
777
+ return this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}/snapshot`, { method: "DELETE" });
778
+ }
779
+ async listRunSnapshots(runId) {
780
+ const body = await this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}/snapshots`);
781
+ return body.data;
782
+ }
783
+ deleteRunSnapshot(runId, snapshotId) {
784
+ return this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}/snapshots/${encodeURIComponent(snapshotId)}`, { method: "DELETE" });
760
785
  }
761
786
  getStatus(runId) {
762
787
  return this.fetch(`/api/v1/workflows/${runId}/status`);
@@ -1017,17 +1042,59 @@ async function bundleWorkflow(workflowPath, overrides) {
1017
1042
  sourceHash: sha256(source),
1018
1043
  bundlerVersion: BUNDLER_VERSION
1019
1044
  };
1045
+ const inputSchema = extractIOSchema(workflow.input);
1046
+ const outputSchema = extractIOSchema(workflow.output);
1020
1047
  return {
1021
1048
  source,
1022
1049
  manifest,
1023
1050
  workflowPlan: plan,
1024
1051
  networkPolicy: overrides?.networkPolicy ?? metadata.networkPolicy,
1025
1052
  placeholders: overrides?.placeholders ?? metadata.placeholders,
1026
- ...metadata.snapshot !== undefined ? { snapshot: metadata.snapshot } : {},
1027
- ...metadata.saveSnapshot !== undefined ? { saveSnapshot: metadata.saveSnapshot } : {},
1028
- ...metadata.memory !== undefined ? { memory: metadata.memory } : {}
1053
+ ...inputSchema !== undefined ? { inputSchema } : {},
1054
+ ...outputSchema !== undefined ? { outputSchema } : {},
1055
+ ...metadata.snapshots !== undefined ? { snapshots: metadata.snapshots } : {},
1056
+ ...metadata.memory !== undefined ? { memory: metadata.memory } : {},
1057
+ ...metadata.postRunHooks !== undefined ? { postRunHooks: metadata.postRunHooks } : {}
1029
1058
  };
1030
1059
  }
1060
+ function extractIOSchema(zodSchema) {
1061
+ let json;
1062
+ try {
1063
+ const z3 = __require("zod");
1064
+ if (!z3.toJSONSchema)
1065
+ return;
1066
+ json = z3.toJSONSchema(zodSchema);
1067
+ } catch {
1068
+ return;
1069
+ }
1070
+ if (!json || typeof json !== "object")
1071
+ return;
1072
+ if (Object.keys(json).filter((k) => k !== "$schema").length === 0)
1073
+ return;
1074
+ const out = {};
1075
+ if (json.type !== undefined)
1076
+ out.type = json.type;
1077
+ if (json.description !== undefined)
1078
+ out.description = String(json.description);
1079
+ if (json.properties && typeof json.properties === "object") {
1080
+ const props = {};
1081
+ for (const [key, raw] of Object.entries(json.properties)) {
1082
+ if (raw && typeof raw === "object") {
1083
+ const r = raw;
1084
+ props[key] = {};
1085
+ if (r.type !== undefined)
1086
+ props[key].type = r.type;
1087
+ if (r.description !== undefined)
1088
+ props[key].description = String(r.description);
1089
+ }
1090
+ }
1091
+ if (Object.keys(props).length > 0)
1092
+ out.properties = props;
1093
+ }
1094
+ if (Array.isArray(json.required))
1095
+ out.required = json.required.map(String);
1096
+ return out;
1097
+ }
1031
1098
  // src/utils/schemas.ts
1032
1099
  import { z as z3 } from "zod";
1033
1100
  var AgentStatusSchema = z3.object({
@@ -1544,7 +1611,11 @@ function makeVercelSandboxProvider(sb, globalEnvs) {
1544
1611
  },
1545
1612
  async snapshot() {
1546
1613
  const res = await sb.snapshot({ expiration: 0 });
1547
- return { snapshotId: res.snapshotId };
1614
+ const sizeBytes = typeof res.sizeBytes === "number" ? res.sizeBytes : undefined;
1615
+ return {
1616
+ snapshotId: res.snapshotId,
1617
+ ...sizeBytes !== undefined ? { sizeBytes } : {}
1618
+ };
1548
1619
  }
1549
1620
  };
1550
1621
  }
@@ -2049,7 +2120,7 @@ function buildInvokeChild(runId, opts = {}) {
2049
2120
  if (!baseUrl || !apiKey) {
2050
2121
  throw new Error("ctx.invokeChild requires AGENT_COMPOSE_URL and AGENT_COMPOSE_API_KEY");
2051
2122
  }
2052
- childClient = new AgentComposeClient(baseUrl, apiKey);
2123
+ childClient = new AgentComposeClient({ apiKey, baseUrl });
2053
2124
  return childClient;
2054
2125
  };
2055
2126
  return (name, input, childOpts) => getChildClient().invokeAndWait(name, input, {
@@ -481,14 +481,18 @@ function extractMetadata(source) {
481
481
  out.networkPolicy = freezeMetadataValue(source.networkPolicy);
482
482
  if (source.placeholders !== undefined)
483
483
  out.placeholders = Object.freeze({ ...source.placeholders });
484
- if (source.snapshot !== undefined)
485
- out.snapshot = source.snapshot;
486
- if (source.saveSnapshot !== undefined)
487
- out.saveSnapshot = source.saveSnapshot;
484
+ if (source.inputSchema !== undefined)
485
+ out.inputSchema = freezeMetadataValue(source.inputSchema);
486
+ if (source.outputSchema !== undefined)
487
+ out.outputSchema = freezeMetadataValue(source.outputSchema);
488
+ if (source.snapshots !== undefined)
489
+ out.snapshots = Object.freeze({ ...source.snapshots });
488
490
  if (source.processors !== undefined)
489
491
  out.processors = Object.freeze([...source.processors]);
490
492
  if (source.memory !== undefined)
491
- out.memory = typeof source.memory === "object" && source.memory !== null ? Object.freeze({ ...source.memory }) : source.memory;
493
+ out.memory = source.memory;
494
+ if (source.postRunHooks !== undefined)
495
+ out.postRunHooks = Object.freeze([...source.postRunHooks]);
492
496
  return Object.freeze(out);
493
497
  }
494
498
  function freezeMetadataValue(value) {
@@ -583,7 +587,8 @@ function defineSandboxEnvironment(env) {
583
587
  throw new Error(`defineSandboxEnvironment(${env.name}): 'setup' must be a function`);
584
588
  }
585
589
  return defineWorkflow({
586
- saveSnapshot: env.saveSnapshot ?? true,
590
+ snapshots: env.snapshots ?? { saveLatest: true },
591
+ memory: env.memory ?? false,
587
592
  run: async (_ctx, sandbox) => env.setup(sandbox)
588
593
  });
589
594
  }
@@ -695,12 +700,18 @@ function templatePath(factorySlug, ...rest) {
695
700
  const tail = rest.length > 0 ? "/" + rest.map(encodeURIComponent).join("/") : "";
696
701
  return `/api/v1/factories/${encodeURIComponent(factorySlug)}/templates${tail}`;
697
702
  }
703
+ var PUBLIC_API_HOST = "https://api.agentcompose.ai";
698
704
 
699
705
  class AgentComposeClient {
700
706
  fetch;
701
707
  baseUrl;
702
708
  apiKey;
703
- constructor(baseUrl, apiKey) {
709
+ constructor(options = {}) {
710
+ const apiKey = options.apiKey ?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_API_KEY : undefined) ?? "";
711
+ if (!apiKey) {
712
+ throw new Error("AgentComposeClient: `apiKey` is required. Pass it as `{ apiKey }` " + "or set the AGENT_COMPOSE_API_KEY env var.");
713
+ }
714
+ const baseUrl = options.baseUrl ?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_URL : undefined) ?? PUBLIC_API_HOST;
704
715
  this.baseUrl = baseUrl.replace(/\/$/, "");
705
716
  this.apiKey = apiKey;
706
717
  this.fetch = ofetch.create({
@@ -723,10 +734,11 @@ class AgentComposeClient {
723
734
  method: "POST",
724
735
  body: {
725
736
  input,
726
- ...opts?.snapshot !== undefined ? { snapshot: opts.snapshot } : {},
727
- ...opts?.saveSnapshot !== undefined ? { saveSnapshot: opts.saveSnapshot } : {},
737
+ ...opts?.snapshots !== undefined ? { snapshots: opts.snapshots } : {},
728
738
  ...opts?.networkPolicy !== undefined ? { networkPolicy: opts.networkPolicy } : {},
729
739
  ...opts?.placeholders !== undefined ? { placeholders: opts.placeholders } : {},
740
+ ...opts?.memory !== undefined ? { memory: opts.memory } : {},
741
+ ...opts?.postRunHooks !== undefined ? { postRunHooks: opts.postRunHooks } : {},
730
742
  ...parentRunId ? { parentRunId } : {},
731
743
  ...opts?.agentId ? { agentId: opts.agentId } : {}
732
744
  }
@@ -747,16 +759,29 @@ class AgentComposeClient {
747
759
  throw new AgentComposeError(504, `invokeAndWait: run ${runId} did not settle within ${timeoutMs}ms`);
748
760
  }
749
761
  async listSnapshots(opts) {
762
+ const body = await this.listSnapshotsPage(opts);
763
+ return body.data;
764
+ }
765
+ async listSnapshotsPage(opts) {
766
+ const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
750
767
  const q2 = new URLSearchParams;
751
768
  if (opts?.workflow)
752
769
  q2.set("workflow", opts.workflow);
753
770
  if (opts?.limit != null)
754
771
  q2.set("limit", String(opts.limit));
755
- const body = await this.fetch(`/api/v1/snapshots${q2.toString() ? `?${q2}` : ""}`);
756
- return body.data;
772
+ if (opts?.before)
773
+ q2.set("before", opts.before);
774
+ return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/snapshots${q2.toString() ? `?${q2}` : ""}`);
757
775
  }
758
776
  deleteSnapshot(runId) {
759
- return this.fetch(`/api/v1/workflows/${runId}/snapshot`, { method: "DELETE" });
777
+ return this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}/snapshot`, { method: "DELETE" });
778
+ }
779
+ async listRunSnapshots(runId) {
780
+ const body = await this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}/snapshots`);
781
+ return body.data;
782
+ }
783
+ deleteRunSnapshot(runId, snapshotId) {
784
+ return this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}/snapshots/${encodeURIComponent(snapshotId)}`, { method: "DELETE" });
760
785
  }
761
786
  getStatus(runId) {
762
787
  return this.fetch(`/api/v1/workflows/${runId}/status`);
@@ -1017,17 +1042,59 @@ async function bundleWorkflow(workflowPath, overrides) {
1017
1042
  sourceHash: sha256(source),
1018
1043
  bundlerVersion: BUNDLER_VERSION
1019
1044
  };
1045
+ const inputSchema = extractIOSchema(workflow.input);
1046
+ const outputSchema = extractIOSchema(workflow.output);
1020
1047
  return {
1021
1048
  source,
1022
1049
  manifest,
1023
1050
  workflowPlan: plan,
1024
1051
  networkPolicy: overrides?.networkPolicy ?? metadata.networkPolicy,
1025
1052
  placeholders: overrides?.placeholders ?? metadata.placeholders,
1026
- ...metadata.snapshot !== undefined ? { snapshot: metadata.snapshot } : {},
1027
- ...metadata.saveSnapshot !== undefined ? { saveSnapshot: metadata.saveSnapshot } : {},
1028
- ...metadata.memory !== undefined ? { memory: metadata.memory } : {}
1053
+ ...inputSchema !== undefined ? { inputSchema } : {},
1054
+ ...outputSchema !== undefined ? { outputSchema } : {},
1055
+ ...metadata.snapshots !== undefined ? { snapshots: metadata.snapshots } : {},
1056
+ ...metadata.memory !== undefined ? { memory: metadata.memory } : {},
1057
+ ...metadata.postRunHooks !== undefined ? { postRunHooks: metadata.postRunHooks } : {}
1029
1058
  };
1030
1059
  }
1060
+ function extractIOSchema(zodSchema) {
1061
+ let json;
1062
+ try {
1063
+ const z3 = __require("zod");
1064
+ if (!z3.toJSONSchema)
1065
+ return;
1066
+ json = z3.toJSONSchema(zodSchema);
1067
+ } catch {
1068
+ return;
1069
+ }
1070
+ if (!json || typeof json !== "object")
1071
+ return;
1072
+ if (Object.keys(json).filter((k) => k !== "$schema").length === 0)
1073
+ return;
1074
+ const out = {};
1075
+ if (json.type !== undefined)
1076
+ out.type = json.type;
1077
+ if (json.description !== undefined)
1078
+ out.description = String(json.description);
1079
+ if (json.properties && typeof json.properties === "object") {
1080
+ const props = {};
1081
+ for (const [key, raw] of Object.entries(json.properties)) {
1082
+ if (raw && typeof raw === "object") {
1083
+ const r = raw;
1084
+ props[key] = {};
1085
+ if (r.type !== undefined)
1086
+ props[key].type = r.type;
1087
+ if (r.description !== undefined)
1088
+ props[key].description = String(r.description);
1089
+ }
1090
+ }
1091
+ if (Object.keys(props).length > 0)
1092
+ out.properties = props;
1093
+ }
1094
+ if (Array.isArray(json.required))
1095
+ out.required = json.required.map(String);
1096
+ return out;
1097
+ }
1031
1098
  // src/utils/schemas.ts
1032
1099
  import { z as z3 } from "zod";
1033
1100
  var AgentStatusSchema = z3.object({
@@ -1544,7 +1611,11 @@ function makeVercelSandboxProvider(sb, globalEnvs) {
1544
1611
  },
1545
1612
  async snapshot() {
1546
1613
  const res = await sb.snapshot({ expiration: 0 });
1547
- return { snapshotId: res.snapshotId };
1614
+ const sizeBytes = typeof res.sizeBytes === "number" ? res.sizeBytes : undefined;
1615
+ return {
1616
+ snapshotId: res.snapshotId,
1617
+ ...sizeBytes !== undefined ? { sizeBytes } : {}
1618
+ };
1548
1619
  }
1549
1620
  };
1550
1621
  }
@@ -2049,7 +2120,7 @@ function buildInvokeChild(runId, opts = {}) {
2049
2120
  if (!baseUrl || !apiKey) {
2050
2121
  throw new Error("ctx.invokeChild requires AGENT_COMPOSE_URL and AGENT_COMPOSE_API_KEY");
2051
2122
  }
2052
- childClient = new AgentComposeClient(baseUrl, apiKey);
2123
+ childClient = new AgentComposeClient({ apiKey, baseUrl });
2053
2124
  return childClient;
2054
2125
  };
2055
2126
  return (name, input, childOpts) => getChildClient().invokeAndWait(name, input, {