@agent-compose/sdk 0.7.0 → 0.8.1
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 +66 -39
- package/dist/agent/__tests__/runtime-json-schema.test.d.ts +10 -0
- package/dist/agent/agent-context.d.ts +21 -1
- package/dist/agent/agent-loop.d.ts +32 -1
- package/dist/agent/run-agent.d.ts +4 -0
- package/dist/client.d.ts +382 -534
- package/dist/directives.d.ts +112 -0
- package/dist/display.d.ts +258 -0
- package/dist/errors.d.ts +24 -1
- package/dist/index.d.ts +26 -14
- package/dist/index.js +3774 -1679
- package/dist/pause/wrappers.d.ts +31 -9
- package/dist/runtimes/_acp-client.d.ts +46 -1
- package/dist/runtimes/_cli-agent.d.ts +51 -4
- package/dist/runtimes/_jsonl-guard.d.ts +103 -0
- package/dist/runtimes/amp.d.ts +2 -2
- package/dist/runtimes/claude-code.d.ts +61 -0
- package/dist/runtimes/claude-code.test.d.ts +14 -0
- package/dist/runtimes/claude.d.ts +16 -0
- package/dist/runtimes/claude.test.d.ts +8 -0
- package/dist/runtimes/codex.d.ts +12 -3
- package/dist/runtimes/cursor.d.ts +2 -2
- package/dist/runtimes/droid.d.ts +2 -2
- package/dist/runtimes/jsonl-guard.test.d.ts +19 -0
- package/dist/runtimes/openai-desktop.js +3718 -1680
- package/dist/runtimes/opencode.d.ts +2 -2
- package/dist/runtimes/vercel.js +12 -1
- package/dist/sandbox/devbox.d.ts +42 -0
- package/dist/sandbox/exec-stream.d.ts +14 -0
- package/dist/sandbox/network-policy.d.ts +100 -0
- package/dist/sandbox/provider-def.d.ts +79 -0
- package/dist/sandbox/providers/desktop.d.ts +10 -0
- package/dist/sandbox/providers/e2b.d.ts +17 -0
- package/dist/sandbox/providers/local.d.ts +11 -0
- package/dist/sandbox/providers/vercel.d.ts +18 -0
- package/dist/sandbox/registry.d.ts +45 -0
- package/dist/sandbox/sizes.d.ts +68 -0
- package/dist/sandbox.d.ts +24 -299
- package/dist/step-invocation/__tests__/foreground-recovery.test.d.ts +1 -0
- package/dist/step-invocation/invoker.d.ts +10 -0
- package/dist/step-invocation/protocol.d.ts +5 -0
- package/dist/types/api-compliance.d.ts +71 -0
- package/dist/types/api-conversations.d.ts +523 -0
- package/dist/types/api-factory.d.ts +334 -0
- package/dist/types/api-projects.d.ts +131 -0
- package/dist/types/api-runs.d.ts +422 -0
- package/dist/types/api-scopes.d.ts +102 -0
- package/dist/types/conversation-stream.d.ts +191 -0
- package/dist/types/execution-context.d.ts +12 -2
- package/dist/types/protocol.d.ts +38 -1
- package/dist/types/sandbox-environment.d.ts +8 -5
- package/dist/types/sandbox.d.ts +74 -4
- package/dist/types/workflow-metadata.d.ts +41 -8
- package/dist/types/workflow-plan.d.ts +10 -0
- package/dist/types/workflow.d.ts +18 -205
- package/dist/utils/bundler.d.ts +68 -1
- package/dist/workflow-steps/index.d.ts +1 -1
- package/dist/workflow-steps/observability.d.ts +8 -1
- package/dist/workflow-steps/runner.d.ts +3 -3
- package/dist/workflow-steps/step.d.ts +15 -1
- package/dist/workflow-steps/types.d.ts +19 -5
- package/dist/workflow-steps/workflow.d.ts +29 -1
- package/dist/workflows/engine.d.ts +3 -2
- package/dist/workflows/invoke-child.d.ts +20 -2
- package/dist/workflows/invoke-child.test.d.ts +9 -0
- package/package.json +2 -2
- package/src/agent/agent-context.ts +186 -3
- package/src/agent/agent-loop.ts +40 -2
- package/src/agent/run-agent.ts +5 -0
- package/src/client.ts +1048 -625
- package/src/directives.ts +184 -0
- package/src/display.ts +834 -0
- package/src/errors.ts +39 -0
- package/src/index.ts +114 -12
- package/src/pause/wrappers.ts +44 -9
- package/src/runtimes/_acp-client.ts +72 -3
- package/src/runtimes/_cli-agent.ts +161 -36
- package/src/runtimes/_jsonl-guard.ts +219 -0
- package/src/runtimes/claude-code.ts +256 -0
- package/src/runtimes/claude.ts +32 -2
- package/src/runtimes/codex.ts +63 -3
- package/src/runtimes/openai-desktop.ts +59 -14
- package/src/sandbox/devbox.ts +48 -0
- package/src/sandbox/exec-stream.ts +48 -0
- package/src/sandbox/network-policy.ts +181 -0
- package/src/sandbox/provider-def.ts +94 -0
- package/src/sandbox/providers/desktop.ts +57 -0
- package/src/sandbox/providers/e2b.ts +354 -0
- package/src/sandbox/providers/local.ts +106 -0
- package/src/sandbox/providers/vercel.ts +331 -0
- package/src/sandbox/registry.ts +198 -0
- package/src/sandbox/sizes.ts +95 -0
- package/src/sandbox.ts +59 -1275
- package/src/step-invocation/invoker.ts +151 -28
- package/src/step-invocation/protocol.ts +8 -0
- package/src/types/api-compliance.ts +79 -0
- package/src/types/api-conversations.ts +547 -0
- package/src/types/api-factory.ts +368 -0
- package/src/types/api-projects.ts +140 -0
- package/src/types/api-runs.ts +459 -0
- package/src/types/api-scopes.ts +102 -0
- package/src/types/conversation-stream.ts +231 -0
- package/src/types/execution-context.ts +10 -2
- package/src/types/protocol.ts +41 -0
- package/src/types/sandbox-environment.ts +28 -9
- package/src/types/sandbox.ts +73 -4
- package/src/types/workflow-metadata.ts +44 -8
- package/src/types/workflow-plan.ts +11 -0
- package/src/types/workflow.ts +25 -292
- package/src/utils/bundler.ts +245 -8
- package/src/utils/errors.ts +16 -1
- package/src/workflow-steps/index.ts +1 -0
- package/src/workflow-steps/observability.ts +19 -8
- package/src/workflow-steps/runner.ts +4 -4
- package/src/workflow-steps/step.ts +49 -1
- package/src/workflow-steps/types.ts +20 -5
- package/src/workflow-steps/workflow.ts +29 -1
- package/src/workflows/engine.ts +3 -2
- package/src/workflows/invoke-child.ts +49 -13
package/src/utils/bundler.ts
CHANGED
|
@@ -18,14 +18,28 @@
|
|
|
18
18
|
* parses or imports user source — it only validates the structured manifest
|
|
19
19
|
* this function returns alongside the bundled bytes, and cross-checks the
|
|
20
20
|
* manifest's `sourceHash` against the source it received.
|
|
21
|
+
*
|
|
22
|
+
* Module resolution carries two more layers, because most workflow source is
|
|
23
|
+
* now written by an AGENT and the bundler's error is the only feedback it
|
|
24
|
+
* gets (the Workflow Studio's agent authored `import … from "agentc/sdk"`
|
|
25
|
+
* and prod answered with bun's `Maybe you need to "bun install"?` — advice
|
|
26
|
+
* nobody could act on inside a sandbox with no package.json):
|
|
27
|
+
*
|
|
28
|
+
* - TOLERATE (`SDK_SPECIFIER_ALIASES` + `sdkAliasPlugin`) — near-miss
|
|
29
|
+
* spellings of `@agent-compose/sdk` resolve to the real package, so a
|
|
30
|
+
* draft already written with the wrong one builds unedited.
|
|
31
|
+
* - SELF-CORRECT (`explainBundleFailure`) — anything that still fails to
|
|
32
|
+
* resolve produces an error NAMING the real package, which an agent
|
|
33
|
+
* reading its own tool error can fix on the next turn.
|
|
21
34
|
*/
|
|
22
35
|
|
|
23
36
|
import { createHash } from "node:crypto";
|
|
37
|
+
import { dirname } from "node:path";
|
|
24
38
|
import { parse as babelParse } from "@babel/parser";
|
|
25
39
|
import type { File, ExportDefaultDeclaration, CallExpression, Expression, Statement } from "@babel/types";
|
|
26
40
|
import { importSourceModule } from "./source-loader.js";
|
|
27
41
|
import type { SnapshotConfig } from "../types/workflow.js";
|
|
28
|
-
import type { IOSchema, OutputSchema, ConnectorRequirements, ConnectorOperationTag, InvokePolicy, SandboxResources } from "../types/workflow-metadata.js";
|
|
42
|
+
import type { IOSchema, OutputSchema, ConnectorRequirements, ConnectorOperationTag, InvokePolicy, SandboxResources, DriveMergePolicy } from "../types/workflow-metadata.js";
|
|
29
43
|
import type { SandboxNetworkPolicy } from "../sandbox.js";
|
|
30
44
|
import { isWorkflow } from "../workflow-steps/workflow.js";
|
|
31
45
|
import type { Workflow } from "../workflow-steps/types.js";
|
|
@@ -101,6 +115,9 @@ export interface BundledWorkflow {
|
|
|
101
115
|
/** Sandbox resources declared via `defineWorkflow({ resources })` — machine
|
|
102
116
|
* SKU (`size`) and `provider` (`vercel` | `e2b`). */
|
|
103
117
|
resources?: SandboxResources;
|
|
118
|
+
/** Run-branch merge policy declared via `defineWorkflow({ mergePolicy })`.
|
|
119
|
+
* Absent ⇒ `"auto"` (today's behaviour). */
|
|
120
|
+
mergePolicy?: DriveMergePolicy;
|
|
104
121
|
workflowPlan: WorkflowPlan;
|
|
105
122
|
/** Compact JSON-Schema-shaped description of the workflow's input
|
|
106
123
|
* type. Extracted from the workflow's declared `input` zod schema
|
|
@@ -118,11 +135,191 @@ export interface BundledWorkflow {
|
|
|
118
135
|
/** Set by `defineSandboxEnvironment` — marks an environment build so the
|
|
119
136
|
* server skips the /factory mount for its runs (#13). */
|
|
120
137
|
environmentBuild?: boolean;
|
|
138
|
+
/** Drive requirement declared via `defineWorkflow({ factoryDrive })`.
|
|
139
|
+
* Absent ⇒ `"required"` (a failed /factory mount fails the run);
|
|
140
|
+
* `"none"` = explicit no-drive opt-out. */
|
|
141
|
+
factoryDrive?: "required" | "none";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The one true import specifier for the platform SDK. */
|
|
145
|
+
export const SDK_PACKAGE = "@agent-compose/sdk";
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Import specifiers that can only have MEANT `@agent-compose/sdk`, rewritten
|
|
149
|
+
* to it at bundle time so a draft written with the wrong spelling builds
|
|
150
|
+
* unedited.
|
|
151
|
+
*
|
|
152
|
+
* Every entry carries an `sdk` segment or suffix, so none of them can be a
|
|
153
|
+
* real third-party package a workflow might legitimately depend on: `a/b`
|
|
154
|
+
* forms are subpaths of packages that don't exist, and the `*-sdk` forms
|
|
155
|
+
* name this platform explicitly. Bare `agentc` / `agent-compose` are
|
|
156
|
+
* deliberately NOT aliased — those are plausible npm package names, and
|
|
157
|
+
* silently redirecting a real dependency is worse than a clear error.
|
|
158
|
+
*
|
|
159
|
+
* Exported for the unit test that pins the table.
|
|
160
|
+
*/
|
|
161
|
+
export const SDK_SPECIFIER_ALIASES: readonly string[] = [
|
|
162
|
+
"agentc/sdk",
|
|
163
|
+
"@agentc/sdk",
|
|
164
|
+
"agentc-sdk",
|
|
165
|
+
"agent-compose/sdk",
|
|
166
|
+
"agentcompose/sdk",
|
|
167
|
+
"@agentcompose/sdk",
|
|
168
|
+
"agent-compose-sdk",
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const SDK_ALIAS_SET = new Set(SDK_SPECIFIER_ALIASES);
|
|
172
|
+
|
|
173
|
+
/** `@agent-compose/sdk` when `specifier` is a known near-miss for it, else
|
|
174
|
+
* null. The single authority: the plugin's regex filter is only a fast
|
|
175
|
+
* pre-filter, and this decides. */
|
|
176
|
+
export function resolveSdkAlias(specifier: string): string | null {
|
|
177
|
+
return SDK_ALIAS_SET.has(specifier) ? SDK_PACKAGE : null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Minimal shape of the Bun surface this module drives. Accessed via
|
|
181
|
+
* globalThis so the SDK keeps no compile-time dependency on @types/bun. */
|
|
182
|
+
interface BunBuildLog { message: string; name?: string; specifier?: string }
|
|
183
|
+
interface BunSurface {
|
|
184
|
+
build(opts: {
|
|
185
|
+
entrypoints: string[]; format: string; target: string;
|
|
186
|
+
plugins?: { name: string; setup(build: BunPluginBuild): void }[];
|
|
187
|
+
}): Promise<{ success: boolean; outputs: { text(): Promise<string> }[]; logs: BunBuildLog[] }>;
|
|
188
|
+
resolveSync(specifier: string, parent: string): string;
|
|
189
|
+
}
|
|
190
|
+
interface BunPluginBuild {
|
|
191
|
+
onResolve(
|
|
192
|
+
constraints: { filter: RegExp; namespace?: string },
|
|
193
|
+
callback: (args: { path: string; importer?: string; resolveDir?: string }) => { path: string } | undefined,
|
|
194
|
+
): void;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Anchored regex over the alias table — the plugin filter. Specifiers are
|
|
198
|
+
* literal package names, but escape anyway so a future entry with a `.`
|
|
199
|
+
* or `+` can't widen the filter. */
|
|
200
|
+
function aliasFilter(): RegExp {
|
|
201
|
+
const alternation = SDK_SPECIFIER_ALIASES
|
|
202
|
+
.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
203
|
+
.join("|");
|
|
204
|
+
return new RegExp(`^(?:${alternation})$`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The TOLERATE layer: a Bun resolve plugin that maps every known near-miss
|
|
209
|
+
* specifier onto the real SDK. Resolution goes through Bun's own resolver
|
|
210
|
+
* from the importing file's directory, so the alias lands on exactly the
|
|
211
|
+
* `@agent-compose/sdk` the build would have used had the source spelled it
|
|
212
|
+
* correctly. When the real SDK can't be resolved either, the plugin declines
|
|
213
|
+
* and Bun's failure flows into `explainBundleFailure` below.
|
|
214
|
+
*/
|
|
215
|
+
function sdkAliasPlugin(bun: BunSurface): { name: string; setup(build: BunPluginBuild): void } {
|
|
216
|
+
return {
|
|
217
|
+
name: "agent-compose-sdk-alias",
|
|
218
|
+
setup(build) {
|
|
219
|
+
build.onResolve({ filter: aliasFilter() }, (args) => {
|
|
220
|
+
const target = resolveSdkAlias(args.path);
|
|
221
|
+
if (!target) return undefined;
|
|
222
|
+
// `importer` is the importing FILE, `resolveDir` already a directory.
|
|
223
|
+
const from = args.importer ? dirname(args.importer) : args.resolveDir;
|
|
224
|
+
try {
|
|
225
|
+
return { path: bun.resolveSync(target, from && from.length > 0 ? from : process.cwd()) };
|
|
226
|
+
} catch {
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** The unresolved specifier a Bun diagnostic is about, or null when it isn't
|
|
235
|
+
* a resolve failure. `ResolveMessage` carries it as a field; parsing the
|
|
236
|
+
* message text is the fallback. */
|
|
237
|
+
function readUnresolvedSpecifier(diag: unknown): string | null {
|
|
238
|
+
if (!diag || typeof diag !== "object") return null;
|
|
239
|
+
const d = diag as { specifier?: unknown; message?: unknown };
|
|
240
|
+
if (typeof d.specifier === "string" && d.specifier.length > 0) return d.specifier;
|
|
241
|
+
const message = typeof d.message === "string" ? d.message : "";
|
|
242
|
+
const m = /Could not resolve:?\s*"([^"]+)"/.exec(message);
|
|
243
|
+
return m ? m[1] : null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Flatten a bundler failure into the diagnostics it actually carries.
|
|
247
|
+
* Bun reports through two channels and both land here: a THROWN
|
|
248
|
+
* `AggregateError("Bundle failed")` whose real messages hide in `.errors`,
|
|
249
|
+
* and a returned `logs` array on `success: false`. */
|
|
250
|
+
function flattenDiagnostics(failure: unknown): unknown[] {
|
|
251
|
+
const out: unknown[] = [];
|
|
252
|
+
const seen = new Set<unknown>();
|
|
253
|
+
const walk = (node: unknown, depth: number): void => {
|
|
254
|
+
if (!node || depth > 4 || seen.has(node)) return;
|
|
255
|
+
seen.add(node);
|
|
256
|
+
if (Array.isArray(node)) {
|
|
257
|
+
for (const sub of node) walk(sub, depth + 1);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
out.push(node);
|
|
261
|
+
if (typeof node === "object") {
|
|
262
|
+
const n = node as { errors?: unknown; cause?: unknown };
|
|
263
|
+
if (Array.isArray(n.errors)) for (const sub of n.errors) walk(sub, depth + 1);
|
|
264
|
+
if (n.cause) walk(n.cause, depth + 1);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
walk(failure, 0);
|
|
268
|
+
return out;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function diagnosticText(diag: unknown): string {
|
|
272
|
+
if (diag instanceof Error) return diag.message || String(diag);
|
|
273
|
+
if (diag && typeof diag === "object" && typeof (diag as { message?: unknown }).message === "string") {
|
|
274
|
+
return (diag as { message: string }).message;
|
|
275
|
+
}
|
|
276
|
+
return String(diag);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The SELF-CORRECTING layer. Turns a Bun bundle failure into a message that
|
|
281
|
+
* names the real fix instead of leaking Bun's internals.
|
|
282
|
+
*
|
|
283
|
+
* An unresolved import is almost always one thing: workflow source naming
|
|
284
|
+
* the platform SDK by a specifier that isn't its package name. Bun answers
|
|
285
|
+
* that with `Could not resolve: "agentc/sdk". Maybe you need to "bun
|
|
286
|
+
* install"?` — advice the author cannot act on (there is no package.json to
|
|
287
|
+
* install into; the drive holds one file). The rewritten message says the
|
|
288
|
+
* package name, so an agent reading its own tool error can fix the import on
|
|
289
|
+
* the next turn.
|
|
290
|
+
*
|
|
291
|
+
* Non-resolve failures (syntax errors, transform failures) keep their
|
|
292
|
+
* verbatim diagnostics — those are already actionable.
|
|
293
|
+
*
|
|
294
|
+
* Exported for the unit test; `bundleWorkflow` is the supported entrypoint.
|
|
295
|
+
*/
|
|
296
|
+
export function explainBundleFailure(err: unknown, label: string): string {
|
|
297
|
+
const diagnostics = flattenDiagnostics(err);
|
|
298
|
+
const unresolved: string[] = [];
|
|
299
|
+
for (const diag of diagnostics) {
|
|
300
|
+
const specifier = readUnresolvedSpecifier(diag);
|
|
301
|
+
if (specifier && !unresolved.includes(specifier)) unresolved.push(specifier);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (unresolved.length > 0) {
|
|
305
|
+
const quoted = unresolved.map((s) => `"${s}"`).join(", ");
|
|
306
|
+
return (
|
|
307
|
+
`Could not resolve ${quoted} — workflow source imports the platform SDK as "${SDK_PACKAGE}". ` +
|
|
308
|
+
`Fix the import specifier in ${label}; anything that is genuinely a third-party ` +
|
|
309
|
+
`package must be installed where the source is bundled.`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const detail = diagnostics
|
|
314
|
+
.map(diagnosticText)
|
|
315
|
+
.filter((t) => t.length > 0 && t !== "Bundle failed")
|
|
316
|
+
.join("\n");
|
|
317
|
+
return `Failed to bundle ${label}:\n${detail || "the bundler reported no diagnostics"}`;
|
|
121
318
|
}
|
|
122
319
|
|
|
123
320
|
async function bundle(path: string, label: string): Promise<string> {
|
|
124
321
|
// Use globalThis to access Bun without a compile-time dependency on @types/bun.
|
|
125
|
-
const bun = globalThis as unknown as { Bun?:
|
|
322
|
+
const bun = globalThis as unknown as { Bun?: BunSurface };
|
|
126
323
|
if (!bun.Bun?.build) throw new Error("bundleWorkflow requires the Bun runtime (Bun.build)");
|
|
127
324
|
// target: "node" — the bundle runs inside the Vercel sandbox under Node 22+.
|
|
128
325
|
//
|
|
@@ -138,9 +335,24 @@ async function bundle(path: string, label: string): Promise<string> {
|
|
|
138
335
|
// Pure-ESM workflows (e.g. only importing `@agent-compose/sdk`) bundled
|
|
139
336
|
// identically under either target — that's why this bug stayed hidden
|
|
140
337
|
// until the first workflow that pulled CJS deps got dispatched.
|
|
141
|
-
|
|
338
|
+
//
|
|
339
|
+
// The alias plugin rewrites known near-miss SDK specifiers before Bun's
|
|
340
|
+
// resolver sees them; whatever still fails to resolve comes back through
|
|
341
|
+
// `explainBundleFailure` naming the real package instead of Bun's
|
|
342
|
+
// "Maybe you need to `bun install`?".
|
|
343
|
+
let result: Awaited<ReturnType<BunSurface["build"]>>;
|
|
344
|
+
try {
|
|
345
|
+
result = await bun.Bun.build({
|
|
346
|
+
entrypoints: [path], format: "esm", target: "node",
|
|
347
|
+
plugins: [sdkAliasPlugin(bun.Bun)],
|
|
348
|
+
});
|
|
349
|
+
} catch (err) {
|
|
350
|
+
// Modern Bun.build THROWS AggregateError("Bundle failed") on a resolve
|
|
351
|
+
// or transform error rather than returning `success: false`.
|
|
352
|
+
throw new WorkflowSourceValidationError(explainBundleFailure(err, label));
|
|
353
|
+
}
|
|
142
354
|
if (!result.success) {
|
|
143
|
-
throw new
|
|
355
|
+
throw new WorkflowSourceValidationError(explainBundleFailure(result.logs, label));
|
|
144
356
|
}
|
|
145
357
|
return result.outputs[0].text();
|
|
146
358
|
}
|
|
@@ -173,7 +385,7 @@ async function extractFromBundle<T>(
|
|
|
173
385
|
* the latter would also accept `defineWorkflowAttacker`. The runtime brand
|
|
174
386
|
* check is the second gate, but the syntactic check should be tight.
|
|
175
387
|
*
|
|
176
|
-
* `defineSandboxEnvironment` is sugar over
|
|
388
|
+
* `defineSandboxEnvironment` is sugar over the step builder (see
|
|
177
389
|
* `src/types/sandbox-environment.ts`) and is explicitly registerable via
|
|
178
390
|
* `agentc register setup.ts --build` to capture a snapshot. The AST gate
|
|
179
391
|
* accepts it for the same reason it accepts `defineWorkflow`: the bundled
|
|
@@ -284,6 +496,28 @@ function sha256(text: string): string {
|
|
|
284
496
|
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
285
497
|
}
|
|
286
498
|
|
|
499
|
+
/**
|
|
500
|
+
* The registration-time step plan read off an (already evaluated) workflow
|
|
501
|
+
* object. Narrative fields (`summary`, `deliverables`) ride along additively;
|
|
502
|
+
* conditional spreads keep absent fields ABSENT — the plan travels as JSON,
|
|
503
|
+
* so no `undefined` keys. Exported for tests.
|
|
504
|
+
*/
|
|
505
|
+
export function extractWorkflowPlan(workflow: Workflow<unknown, unknown>): WorkflowPlan {
|
|
506
|
+
return workflowPlan(workflow.steps.map((step, index) => ({
|
|
507
|
+
index,
|
|
508
|
+
name: step.name,
|
|
509
|
+
...(step.summary !== undefined ? { summary: step.summary } : {}),
|
|
510
|
+
...(step.deliverables?.length
|
|
511
|
+
? {
|
|
512
|
+
deliverables: step.deliverables.map((d) => ({
|
|
513
|
+
path: d.path,
|
|
514
|
+
...(d.description !== undefined ? { description: d.description } : {}),
|
|
515
|
+
})),
|
|
516
|
+
}
|
|
517
|
+
: {}),
|
|
518
|
+
})));
|
|
519
|
+
}
|
|
520
|
+
|
|
287
521
|
/** Bundle a workflow from source. */
|
|
288
522
|
export async function bundleWorkflow(
|
|
289
523
|
workflowPath: string,
|
|
@@ -312,7 +546,7 @@ export async function bundleWorkflow(
|
|
|
312
546
|
}
|
|
313
547
|
|
|
314
548
|
const { metadata } = workflow;
|
|
315
|
-
const plan
|
|
549
|
+
const plan = extractWorkflowPlan(workflow);
|
|
316
550
|
const manifest: WorkflowManifest = {
|
|
317
551
|
definitionBrand: true,
|
|
318
552
|
sourceHash: sha256(source),
|
|
@@ -341,10 +575,12 @@ export async function bundleWorkflow(
|
|
|
341
575
|
...(outputSchema !== undefined ? { outputSchema } : {}),
|
|
342
576
|
...(metadata.snapshots !== undefined ? { snapshots: metadata.snapshots } : {}),
|
|
343
577
|
...(metadata.resources !== undefined ? { resources: metadata.resources } : {}),
|
|
578
|
+
...(metadata.mergePolicy !== undefined ? { mergePolicy: metadata.mergePolicy } : {}),
|
|
344
579
|
...(metadata.connectors !== undefined ? { connectors: metadata.connectors } : {}),
|
|
345
580
|
...(metadata.connectorOperation !== undefined ? { connectorOperation: metadata.connectorOperation } : {}),
|
|
346
581
|
...(metadata.invokePolicy !== undefined ? { invokePolicy: metadata.invokePolicy } : {}),
|
|
347
582
|
...(metadata.environmentBuild !== undefined ? { environmentBuild: metadata.environmentBuild } : {}),
|
|
583
|
+
...(metadata.factoryDrive !== undefined ? { factoryDrive: metadata.factoryDrive } : {}),
|
|
348
584
|
};
|
|
349
585
|
}
|
|
350
586
|
|
|
@@ -356,8 +592,9 @@ export async function bundleWorkflow(
|
|
|
356
592
|
*
|
|
357
593
|
* Returns undefined when:
|
|
358
594
|
* - The schema can't be serialised (corrupt / unknown variant)
|
|
359
|
-
* - The schema is effectively `unknown` (the
|
|
360
|
-
*
|
|
595
|
+
* - The schema is effectively `unknown` (e.g. the
|
|
596
|
+
* `defineSandboxEnvironment` sugar's schemas — no meaningful
|
|
597
|
+
* contract to render). */
|
|
361
598
|
function extractIOSchema(
|
|
362
599
|
zodSchema: { _zod?: unknown } | unknown,
|
|
363
600
|
): IOSchema | undefined {
|
package/src/utils/errors.ts
CHANGED
|
@@ -8,7 +8,22 @@
|
|
|
8
8
|
* so the root cause is always visible. Cycle-guarded against self-referential
|
|
9
9
|
* `cause` links. */
|
|
10
10
|
export function formatError(err: unknown): string {
|
|
11
|
-
if (!(err instanceof Error))
|
|
11
|
+
if (!(err instanceof Error)) {
|
|
12
|
+
// Parsed-JSON error payloads (a CLI runtime's `{"error":{"message":…}}`,
|
|
13
|
+
// a provider body) are plain objects, and `String({...})` is the literal
|
|
14
|
+
// "[object Object]" — the exact string that reached session transcripts.
|
|
15
|
+
// Prefer the conventional `.message`; otherwise show the JSON itself.
|
|
16
|
+
if (typeof err === "object" && err !== null) {
|
|
17
|
+
const msg = (err as { message?: unknown }).message;
|
|
18
|
+
if (typeof msg === "string" && msg.length > 0) return msg;
|
|
19
|
+
try {
|
|
20
|
+
return JSON.stringify(err) ?? String(err);
|
|
21
|
+
} catch {
|
|
22
|
+
return String(err);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return String(err);
|
|
26
|
+
}
|
|
12
27
|
const parts: string[] = [err.message];
|
|
13
28
|
const seen = new Set<unknown>([err]);
|
|
14
29
|
let cause: unknown = (err as { cause?: unknown }).cause;
|
|
@@ -56,7 +56,12 @@ export interface SubStepEvent {
|
|
|
56
56
|
* empty snapshot (and the wire payload omits the field entirely). */
|
|
57
57
|
export interface StepObservability {
|
|
58
58
|
metadata?: Record<string, unknown>;
|
|
59
|
-
events
|
|
59
|
+
/** Residual events that failed live delivery, each carrying its ORIGINAL
|
|
60
|
+
* emit-time `seq`. The server keys its idempotency on that seq — the SAME
|
|
61
|
+
* key the live route used — so a live write whose ack was lost collapses
|
|
62
|
+
* on redelivery instead of duplicating. Array position is NOT the seq:
|
|
63
|
+
* acked events are stripped, so indices shift. */
|
|
64
|
+
events?: Array<AgentLifecycleEvent & { seq: number }>;
|
|
60
65
|
subSteps?: SubStepEvent[];
|
|
61
66
|
}
|
|
62
67
|
|
|
@@ -163,11 +168,12 @@ export class StepObservabilityCollector {
|
|
|
163
168
|
|
|
164
169
|
readonly agentEvents: AgentEventSink = {
|
|
165
170
|
emit: (event: AgentLifecycleEvent) => {
|
|
166
|
-
// `seq` is the event's index in `events`, captured BEFORE
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
+
// `seq` is the event's emit-time index in `events`, captured BEFORE
|
|
172
|
+
// push. It is THE idempotency handle on both delivery paths: the
|
|
173
|
+
// live POST carries it per event, and `snapshot()` stamps it onto
|
|
174
|
+
// every residual event so the server's batch backstop builds the
|
|
175
|
+
// byte-identical key. The live route's `acceptedSeqs` response uses
|
|
176
|
+
// this seq; we strip acked events in `snapshot()`.
|
|
171
177
|
const seq = this.events.length;
|
|
172
178
|
this.events.push(event);
|
|
173
179
|
if (this.liveEmitter) {
|
|
@@ -206,8 +212,13 @@ export class StepObservabilityCollector {
|
|
|
206
212
|
|
|
207
213
|
const hasMetadata = Object.keys(this.metadata).length > 0;
|
|
208
214
|
const hasSubSteps = this.subSteps.length > 0;
|
|
209
|
-
// Filter out ack'd events — the live route already wrote them
|
|
210
|
-
|
|
215
|
+
// Filter out ack'd events — the live route already wrote them — and
|
|
216
|
+
// stamp each survivor with its ORIGINAL emit-time seq. Filtering shifts
|
|
217
|
+
// array positions, so the seq must travel explicitly for the server's
|
|
218
|
+
// backstop to reproduce the live path's idempotency key.
|
|
219
|
+
const remainingEvents = this.events
|
|
220
|
+
.map((event, seq) => ({ ...event, seq }))
|
|
221
|
+
.filter(({ seq }) => !this.ackedSeqs.has(seq));
|
|
211
222
|
const hasEvents = remainingEvents.length > 0;
|
|
212
223
|
|
|
213
224
|
if (!hasMetadata && !hasEvents && !hasSubSteps) return undefined;
|
|
@@ -26,7 +26,7 @@ import { z } from "zod";
|
|
|
26
26
|
import type { Workflow, StepContext, StepRunResult } from "./types.js";
|
|
27
27
|
import type { RequestContext } from "../request-context/request-context.js";
|
|
28
28
|
import type { SandboxProvider } from "../types/sandbox.js";
|
|
29
|
-
import type { WorkflowRun,
|
|
29
|
+
import type { WorkflowRun, InvokeChild } from "../types/execution-context.js";
|
|
30
30
|
import { StepObservabilityCollector, type StepObservability } from "./observability.js";
|
|
31
31
|
import type { LiveAgentEventEmitter } from "./run-callback.js";
|
|
32
32
|
import { scopedMemoize } from "../pause/checkpoint.js";
|
|
@@ -89,7 +89,7 @@ export interface RunWorkflowStepsOpts<TInput, TOutput> {
|
|
|
89
89
|
/** Fire before a step runs (after cache check / before input validation). */
|
|
90
90
|
onStepStarted?(stepIndex: number, stepName: string): void | Promise<void>;
|
|
91
91
|
/** Child workflow invocation implementation. Defaults to a clear unsupported error. */
|
|
92
|
-
invokeChild?:
|
|
92
|
+
invokeChild?: InvokeChild;
|
|
93
93
|
/** Optional live-stream emitter for agent lifecycle events. The runner
|
|
94
94
|
* passes a fetch-based emitter wired to the per-run callback token so
|
|
95
95
|
* the dashboard sees events as the agent loop produces them; tests
|
|
@@ -111,7 +111,7 @@ export interface RunWorkflowSingleStepOpts {
|
|
|
111
111
|
requestContext: RequestContext;
|
|
112
112
|
sandbox?: SandboxProvider;
|
|
113
113
|
abortSignal?: AbortSignal;
|
|
114
|
-
invokeChild?:
|
|
114
|
+
invokeChild?: InvokeChild;
|
|
115
115
|
/** Optional live-stream emitter — see `RunWorkflowStepsOpts.liveAgentEventEmitter`. */
|
|
116
116
|
liveAgentEventEmitter?: LiveAgentEventEmitter;
|
|
117
117
|
}
|
|
@@ -189,7 +189,7 @@ export async function runWorkflowSteps<TInput, TOutput>(
|
|
|
189
189
|
): Promise<RunWorkflowStepsResult<TOutput>> {
|
|
190
190
|
const { workflow, run, requestContext } = opts;
|
|
191
191
|
const abortSignal = opts.abortSignal ?? new AbortController().signal;
|
|
192
|
-
const invokeChild:
|
|
192
|
+
const invokeChild: InvokeChild = opts.invokeChild ?? (() => {
|
|
193
193
|
throw new Error("StepContext.invokeChild is not configured for this workflow engine");
|
|
194
194
|
});
|
|
195
195
|
|
|
@@ -7,32 +7,80 @@
|
|
|
7
7
|
* - `output` Zod schema; validated against `run`'s return value
|
|
8
8
|
* - `run` step body
|
|
9
9
|
*
|
|
10
|
+
* Optional narrative fields (rendered on the dashboard workflow graph):
|
|
11
|
+
* - `summary` one plain sentence of what the step actually does
|
|
12
|
+
* - `deliverables` files the step promises to produce
|
|
13
|
+
*
|
|
10
14
|
* Validation is required (not optional) because the durability story rides
|
|
11
15
|
* on every step boundary being a recordable, replayable JSON value. A step
|
|
12
16
|
* without a schema is invisible to the engine's persistence layer.
|
|
13
17
|
*
|
|
18
|
+
* Narrative caps are enforced HERE, at construction — the bundler evaluates
|
|
19
|
+
* the module at registration, so this is the "length-capped at registration"
|
|
20
|
+
* point and the error names the step in the author's own environment. The
|
|
21
|
+
* server's manifest zod re-checks the same caps (untrusted input).
|
|
22
|
+
*
|
|
14
23
|
* Type inference flows: `defineStep` infers TInput/TOutput from the schemas
|
|
15
24
|
* so `run(ctx)` is fully typed via `ctx.input` at the call site.
|
|
16
25
|
*/
|
|
17
26
|
|
|
18
27
|
import type { z } from "zod";
|
|
19
|
-
import type { Step, StepContext } from "./types.js";
|
|
28
|
+
import type { Step, StepContext, StepDeliverable } from "./types.js";
|
|
20
29
|
|
|
21
30
|
export interface DefineStepOpts<TInput, TOutput> {
|
|
22
31
|
name: string;
|
|
23
32
|
input: z.ZodType<TInput>;
|
|
24
33
|
output: z.ZodType<TOutput>;
|
|
25
34
|
run(ctx: StepContext<TInput>): TOutput | Promise<TOutput>;
|
|
35
|
+
/** One plain sentence of what the step actually does. 1-200 chars, no
|
|
36
|
+
* control characters (so no newlines). */
|
|
37
|
+
summary?: string;
|
|
38
|
+
/** Files the step promises to produce. At most 8. */
|
|
39
|
+
deliverables?: StepDeliverable[];
|
|
26
40
|
}
|
|
27
41
|
|
|
42
|
+
// C0 controls + DEL — narrative text is one-line prose, never structural.
|
|
43
|
+
// eslint-disable-next-line no-control-regex
|
|
44
|
+
const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
|
|
45
|
+
|
|
28
46
|
export function defineStep<TInput, TOutput>(
|
|
29
47
|
opts: DefineStepOpts<TInput, TOutput>,
|
|
30
48
|
): Step<TInput, TOutput> {
|
|
31
49
|
if (!opts.name) throw new Error("defineStep: 'name' is required");
|
|
50
|
+
if (opts.summary !== undefined) {
|
|
51
|
+
if (opts.summary.length === 0 || opts.summary.length > 200 || CONTROL_CHARS.test(opts.summary)) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`defineStep("${opts.name}"): 'summary' must be one plain sentence, 1-200 characters, no control characters`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (opts.deliverables !== undefined) {
|
|
58
|
+
if (opts.deliverables.length > 8) {
|
|
59
|
+
throw new Error(`defineStep("${opts.name}"): at most 8 deliverables`);
|
|
60
|
+
}
|
|
61
|
+
for (const d of opts.deliverables) {
|
|
62
|
+
if (!d.path || d.path.length > 200 || CONTROL_CHARS.test(d.path)) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`defineStep("${opts.name}"): each deliverable needs a 'path' of 1-200 characters, no control characters`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (d.description !== undefined && (d.description.length === 0 || d.description.length > 200 || CONTROL_CHARS.test(d.description))) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`defineStep("${opts.name}"): deliverable descriptions must be 1-200 characters, no control characters`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Conditional spreads keep absent fields ABSENT (no `undefined` keys —
|
|
75
|
+
// matters for the canonical-hash discipline used elsewhere).
|
|
32
76
|
return {
|
|
33
77
|
name: opts.name,
|
|
34
78
|
input: opts.input,
|
|
35
79
|
output: opts.output,
|
|
36
80
|
run: opts.run,
|
|
81
|
+
...(opts.summary !== undefined ? { summary: opts.summary } : {}),
|
|
82
|
+
...(opts.deliverables !== undefined
|
|
83
|
+
? { deliverables: Object.freeze(opts.deliverables.map((d) => ({ ...d }))) }
|
|
84
|
+
: {}),
|
|
37
85
|
};
|
|
38
86
|
}
|
|
@@ -15,6 +15,7 @@ import type { z } from "zod";
|
|
|
15
15
|
import type { BaseExecutionContext } from "../types/execution-context.js";
|
|
16
16
|
import type { AgentEventSink } from "../types/workflow.js";
|
|
17
17
|
import type { WorkflowMetadata } from "../types/workflow-metadata.js";
|
|
18
|
+
import type { StepObservability } from "./observability.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Per-step execution context. Threaded into every step's `execute(...)` so
|
|
@@ -62,6 +63,15 @@ export interface StepContext<TInput = unknown> extends BaseExecutionContext {
|
|
|
62
63
|
agentEvents: AgentEventSink;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
/** One artifact a step promises to produce. `path` is workspace/drive-relative
|
|
67
|
+
* (e.g. "out/report.html"); the dashboard derives the format tag from the
|
|
68
|
+
* extension client-side — no `format` field here. */
|
|
69
|
+
export interface StepDeliverable {
|
|
70
|
+
path: string;
|
|
71
|
+
/** Optional one-line description of the artifact. */
|
|
72
|
+
description?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
65
75
|
/**
|
|
66
76
|
* Step definition — a single typed unit of work in a workflow chain.
|
|
67
77
|
*
|
|
@@ -80,6 +90,11 @@ export interface Step<TInput, TOutput> {
|
|
|
80
90
|
readonly output: z.ZodType<TOutput>;
|
|
81
91
|
/** Step body. Receives a `StepContext<TInput>` and returns the typed output. */
|
|
82
92
|
run(ctx: StepContext<TInput>): TOutput | Promise<TOutput>;
|
|
93
|
+
/** One plain sentence of what the step actually does — rendered on the
|
|
94
|
+
* dashboard workflow graph. ≤200 chars, no newlines. */
|
|
95
|
+
readonly summary?: string;
|
|
96
|
+
/** Files the step promises to produce. ≤8 entries. */
|
|
97
|
+
readonly deliverables?: readonly StepDeliverable[];
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
/**
|
|
@@ -90,14 +105,14 @@ export interface Step<TInput, TOutput> {
|
|
|
90
105
|
* the step; undefined when no hooks were used.
|
|
91
106
|
*/
|
|
92
107
|
export type StepRunResult<TOutput = unknown> =
|
|
93
|
-
| { status: "completed"; output: TOutput; durationMs: number; observability?:
|
|
94
|
-
| { status: "failed"; error: string; durationMs: number; observability?:
|
|
108
|
+
| { status: "completed"; output: TOutput; durationMs: number; observability?: StepObservability }
|
|
109
|
+
| { status: "failed"; error: string; durationMs: number; observability?: StepObservability };
|
|
95
110
|
|
|
96
111
|
/**
|
|
97
112
|
* Workflow — a list of typed steps plus the workflow's input/output
|
|
98
|
-
* schemas plus its server-side metadata bag. Returned by
|
|
99
|
-
*
|
|
100
|
-
*
|
|
113
|
+
* schemas plus its server-side metadata bag. Returned by
|
|
114
|
+
* `defineWorkflow(...).step(...)...build()`. Engine adapters consume
|
|
115
|
+
* this shape.
|
|
101
116
|
*
|
|
102
117
|
* `input` validates the workflow input before the first step runs.
|
|
103
118
|
* `output` validates the final step's output before the workflow
|
|
@@ -23,7 +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, SandboxResources } from "../types/workflow-metadata.js";
|
|
26
|
+
import type { SnapshotConfig, SandboxResources, ConnectorRequirements, ConnectorOperationTag, InvokePolicy, DriveMergePolicy } from "../types/workflow-metadata.js";
|
|
27
27
|
import type { SandboxNetworkPolicy } from "../sandbox.js";
|
|
28
28
|
import type { Processor } from "../processors/processor.js";
|
|
29
29
|
|
|
@@ -55,7 +55,35 @@ export interface StepWorkflowDefinition<TInput, TOutput> {
|
|
|
55
55
|
* and `provider` (`vercel` | `e2b`). Vercel maps `size` to vCPUs; E2B
|
|
56
56
|
* sizing is template-defined. Omit → smallest SKU on the default provider. */
|
|
57
57
|
resources?: SandboxResources;
|
|
58
|
+
/** What happens to this workflow's drive branch when a run ends:
|
|
59
|
+
* `"auto"` (the default when omitted — today's behaviour) folds it into
|
|
60
|
+
* `main`; `"manual"` proposes a merge approval at the same terminus
|
|
61
|
+
* instead, leaving the branch durable until a human clicks approve. A
|
|
62
|
+
* cancelled run neither merges nor proposes under either policy. */
|
|
63
|
+
mergePolicy?: DriveMergePolicy;
|
|
58
64
|
processors?: readonly Processor[];
|
|
65
|
+
/** Connector requirements (ADR-0007) — providers whose APIs this workflow
|
|
66
|
+
* calls. Dispatch resolves an authorized grant per provider and injects a
|
|
67
|
+
* fresh access token at the network layer. */
|
|
68
|
+
connectors?: ConnectorRequirements;
|
|
69
|
+
/** Marks this workflow as a catalogue OPERATION of a connector — e.g. the
|
|
70
|
+
* `create-issue` operation of the `github` connector. */
|
|
71
|
+
connectorOperation?: ConnectorOperationTag;
|
|
72
|
+
/** Tier-1 invoke ACL — who may dispatch this connector-brokering workflow.
|
|
73
|
+
* See `InvokePolicy`. */
|
|
74
|
+
invokePolicy?: InvokePolicy;
|
|
75
|
+
/** Internal — set by `defineSandboxEnvironment`, not by workflow authors.
|
|
76
|
+
* Marks the workflow as an environment build (base-env / agent-env) so the
|
|
77
|
+
* server skips mounting the shared factory drive for its runs (#13). See
|
|
78
|
+
* `WorkflowMetadata.environmentBuild`. */
|
|
79
|
+
environmentBuild?: boolean;
|
|
80
|
+
/** Whether this workflow's runs need the factory drive. Omit (⇒
|
|
81
|
+
* `"required"`) for any workflow that reads or writes /factory: a failed
|
|
82
|
+
* mount then FAILS the run instead of silently proceeding drive-less.
|
|
83
|
+
* Declare `"none"` for a drive-agnostic workflow — the server skips the
|
|
84
|
+
* /factory mount entirely for its runs. See
|
|
85
|
+
* `WorkflowMetadata.factoryDrive`. */
|
|
86
|
+
factoryDrive?: "required" | "none";
|
|
59
87
|
}
|
|
60
88
|
|
|
61
89
|
export function createStepWorkflow<TInput, TOutput>(
|
package/src/workflows/engine.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* (platform problem) for the runner harness to surface upstream.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { WorkflowHooks
|
|
12
|
+
import type { WorkflowHooks } from "../types/workflow.js";
|
|
13
|
+
import type { InvokeChild } from "../types/execution-context.js";
|
|
13
14
|
import { makeLocalSandboxProvider } from "../sandbox.js";
|
|
14
15
|
import { formatError } from "../utils/errors.js";
|
|
15
16
|
import { RequestContext } from "../request-context/request-context.js";
|
|
@@ -94,7 +95,7 @@ export interface RunWorkflowOptions {
|
|
|
94
95
|
/** Provider-specific child workflow invocation. Temporal/Inngest providers
|
|
95
96
|
* inject their native child-workflow primitive; the LocalProvider injects
|
|
96
97
|
* the public Agent Compose API client. */
|
|
97
|
-
invokeChild?:
|
|
98
|
+
invokeChild?: InvokeChild;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
export async function runWorkflow<TInput, TOutput>(
|