@agent-compose/sdk 0.2.4 → 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.
@@ -24,7 +24,7 @@ import { createHash } from "node:crypto";
24
24
  import { parse as babelParse } from "@babel/parser";
25
25
  import type { File, ExportDefaultDeclaration, CallExpression, Expression, Statement } from "@babel/types";
26
26
  import { importSourceModule } from "./source-loader.js";
27
- import type { WorkflowMemoryConfig } from "../types/workflow.js";
27
+ import type { WorkflowMemoryConfig, SnapshotConfig } from "../types/workflow.js";
28
28
  import type { SandboxNetworkPolicy } from "../sandbox.js";
29
29
  import { isWorkflow } from "../workflow-steps/workflow.js";
30
30
  import type { Workflow } from "../workflow-steps/types.js";
@@ -65,15 +65,20 @@ export interface BundledWorkflow {
65
65
  manifest: WorkflowManifest;
66
66
  networkPolicy?: SandboxNetworkPolicy;
67
67
  placeholders?: Record<string, string>;
68
- /** Run UUID, workflow name, or `name@version` referencing the snapshot
69
- * this workflow's runner boots from, if declared via the `snapshot`
70
- * field on `defineWorkflow`. */
71
- snapshot?: string;
72
- /** Default capture-on-success flag from the workflow definition. */
73
- saveSnapshot?: boolean;
68
+ /** Snapshot config from the workflow definition — `bootFrom` (where to
69
+ * restore at run start), `save`, `retain`. */
70
+ snapshots?: SnapshotConfig;
74
71
  workflowPlan: WorkflowPlan;
75
72
  /** Workflow Memory extractor config. */
76
73
  memory?: WorkflowMemoryConfig;
74
+ /** Ordered post-hook workflow names declared on the workflow. */
75
+ postRunHooks?: readonly string[];
76
+ /** Compact JSON-Schema-shaped description of the workflow's input
77
+ * type. Extracted from the workflow's declared `input` zod schema
78
+ * at bundle time; undefined when the schema is `z.unknown()`. */
79
+ inputSchema?: import("../types/workflow-metadata.js").IOSchema;
80
+ /** Same for the workflow's output zod schema. */
81
+ outputSchema?: import("../types/workflow-metadata.js").IOSchema;
77
82
  }
78
83
 
79
84
  async function bundle(path: string, label: string): Promise<string> {
@@ -120,11 +125,19 @@ async function extractFromBundle<T>(
120
125
  /**
121
126
  * Bun bundles `import { defineWorkflow }` to a local identifier — usually
122
127
  * the exact name, or a numeric suffix like `defineWorkflow2` to disambiguate
123
- * scopes. Match exactly that pattern, NOT `startsWith("defineWorkflow")` —
128
+ * scopes. Match exactly those patterns, NOT `startsWith("defineWorkflow")` —
124
129
  * the latter would also accept `defineWorkflowAttacker`. The runtime brand
125
130
  * check is the second gate, but the syntactic check should be tight.
131
+ *
132
+ * `defineSandboxEnvironment` is sugar over `defineWorkflow` (see
133
+ * `src/types/sandbox-environment.ts`) and is explicitly registerable via
134
+ * `agentc register setup.ts --build` to capture a snapshot. The AST gate
135
+ * accepts it for the same reason it accepts `defineWorkflow`: the bundled
136
+ * default export is a CallExpression to a known SDK identifier, and the
137
+ * runtime brand check downstream catches anything that lies about being a
138
+ * workflow.
126
139
  */
127
- const DEFINE_WORKFLOW_CALLEE = /^defineWorkflow\d*$/;
140
+ const DEFINE_WORKFLOW_CALLEE = /^(?:defineWorkflow|defineSandboxEnvironment)\d*$/;
128
141
 
129
142
  function isDefineWorkflowCall(node: CallExpression): boolean {
130
143
  if (node.callee.type === "Identifier") {
@@ -262,14 +275,76 @@ export async function bundleWorkflow(
262
275
  bundlerVersion: BUNDLER_VERSION,
263
276
  };
264
277
 
278
+ // Input + output schemas: the workflow's `input` / `output` zod
279
+ // schemas → compact JSON-Schema-shaped descriptions carried in
280
+ // template metadata. Used by the dashboard's "Registered" panel to
281
+ // render schema tables and (eventually) client-side validation in
282
+ // the playground. Run-form workflows stamp `z.unknown()` for both
283
+ // — `extractIOSchema` filters those down to undefined so the
284
+ // dashboard renders the "no declared schema" empty state instead
285
+ // of a meaningless empty table.
286
+ const inputSchema = extractIOSchema(workflow.input);
287
+ const outputSchema = extractIOSchema(workflow.output);
288
+
265
289
  return {
266
290
  source,
267
291
  manifest,
268
292
  workflowPlan: plan,
269
293
  networkPolicy: overrides?.networkPolicy ?? metadata.networkPolicy,
270
294
  placeholders: overrides?.placeholders ?? metadata.placeholders,
271
- ...(metadata.snapshot !== undefined ? { snapshot: metadata.snapshot } : {}),
272
- ...(metadata.saveSnapshot !== undefined ? { saveSnapshot: metadata.saveSnapshot } : {}),
273
- ...(metadata.memory !== undefined ? { memory: metadata.memory } : {}),
295
+ ...(inputSchema !== undefined ? { inputSchema } : {}),
296
+ ...(outputSchema !== undefined ? { outputSchema } : {}),
297
+ ...(metadata.snapshots !== undefined ? { snapshots: metadata.snapshots } : {}),
298
+ ...(metadata.memory !== undefined ? { memory: metadata.memory } : {}),
299
+ ...(metadata.postRunHooks !== undefined ? { postRunHooks: metadata.postRunHooks } : {}),
274
300
  };
275
301
  }
302
+
303
+ /** Convert a workflow's `input` or `output` zod schema into a compact,
304
+ * serialisable description for template metadata. Uses Zod 4's
305
+ * `toJSONSchema` and trims the result to fields the dashboard
306
+ * renders — full draft 2020-12 schemas have refs / unions / nested
307
+ * allOf chains that are noisier than the dashboard needs.
308
+ *
309
+ * Returns undefined when:
310
+ * - The schema can't be serialised (corrupt / unknown variant)
311
+ * - The schema is effectively `unknown` (the run-form sugar default —
312
+ * no meaningful contract to render). */
313
+ function extractIOSchema(
314
+ zodSchema: { _zod?: unknown } | unknown,
315
+ ): import("../types/workflow-metadata.js").IOSchema | undefined {
316
+ // Late require to keep this module tree-shakeable for callers that
317
+ // don't bundle. `toJSONSchema` lives on the top-level Zod export in v4.
318
+ let json: Record<string, unknown> | undefined;
319
+ try {
320
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
321
+ const z = require("zod") as { toJSONSchema?: (s: unknown) => Record<string, unknown> };
322
+ if (!z.toJSONSchema) return undefined;
323
+ json = z.toJSONSchema(zodSchema);
324
+ } catch {
325
+ return undefined;
326
+ }
327
+ if (!json || typeof json !== "object") return undefined;
328
+
329
+ // `z.unknown()` serialises to `{}` (no type, no properties). Skip that
330
+ // — there's nothing useful to show.
331
+ if (Object.keys(json).filter((k) => k !== "$schema").length === 0) return undefined;
332
+
333
+ const out: import("../types/workflow-metadata.js").OutputSchema = {};
334
+ if (json.type !== undefined) out.type = json.type as string | string[];
335
+ if (json.description !== undefined) out.description = String(json.description);
336
+ if (json.properties && typeof json.properties === "object") {
337
+ const props: Record<string, { type?: string | string[]; description?: string }> = {};
338
+ for (const [key, raw] of Object.entries(json.properties as Record<string, unknown>)) {
339
+ if (raw && typeof raw === "object") {
340
+ const r = raw as Record<string, unknown>;
341
+ props[key] = {};
342
+ if (r.type !== undefined) props[key].type = r.type as string | string[];
343
+ if (r.description !== undefined) props[key].description = String(r.description);
344
+ }
345
+ }
346
+ if (Object.keys(props).length > 0) out.properties = props;
347
+ }
348
+ if (Array.isArray(json.required)) out.required = json.required.map(String);
349
+ return out;
350
+ }
@@ -23,6 +23,7 @@ import type { z } from "zod";
23
23
  import type { Step, Workflow } from "./types.js";
24
24
  import { WORKFLOW_BRAND } from "./types.js";
25
25
  import { extractMetadata } from "../types/workflow-metadata.js";
26
+ import type { SnapshotConfig, WorkflowMemoryConfig } from "../types/workflow-metadata.js";
26
27
  import type { SandboxNetworkPolicy } from "../sandbox.js";
27
28
  import type { Processor } from "../processors/processor.js";
28
29
 
@@ -46,8 +47,9 @@ export interface StepWorkflowDefinition<TInput, TOutput> {
46
47
  output: z.ZodType<TOutput>;
47
48
  networkPolicy?: SandboxNetworkPolicy;
48
49
  placeholders?: Record<string, string>;
49
- snapshot?: string;
50
- saveSnapshot?: boolean;
50
+ snapshots?: SnapshotConfig;
51
+ memory?: WorkflowMemoryConfig;
52
+ postRunHooks?: readonly string[];
51
53
  processors?: readonly Processor[];
52
54
  }
53
55
 
@@ -18,7 +18,7 @@ export function buildInvokeChild(
18
18
  if (!baseUrl || !apiKey) {
19
19
  throw new Error("ctx.invokeChild requires AGENT_COMPOSE_URL and AGENT_COMPOSE_API_KEY");
20
20
  }
21
- childClient = new AgentComposeClient(baseUrl, apiKey);
21
+ childClient = new AgentComposeClient({ apiKey, baseUrl });
22
22
  return childClient;
23
23
  };
24
24
  return (name, input, childOpts) => getChildClient().invokeAndWait(name, input, {