agentfootprint 7.8.0 → 7.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +2 -2
  2. package/dist/core-flow/Workflow.js +210 -0
  3. package/dist/core-flow/Workflow.js.map +1 -0
  4. package/dist/embedders/index.js +74 -26
  5. package/dist/embedders/index.js.map +1 -1
  6. package/dist/esm/core-flow/Workflow.d.ts +146 -0
  7. package/dist/esm/core-flow/Workflow.js +205 -0
  8. package/dist/esm/core-flow/Workflow.js.map +1 -0
  9. package/dist/esm/embedders/index.d.ts +96 -1
  10. package/dist/esm/embedders/index.js +74 -26
  11. package/dist/esm/embedders/index.js.map +1 -1
  12. package/dist/esm/index.d.ts +1 -0
  13. package/dist/esm/index.js +1 -0
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/patterns/LlmRouter.d.ts +221 -0
  16. package/dist/esm/patterns/LlmRouter.js +400 -0
  17. package/dist/esm/patterns/LlmRouter.js.map +1 -0
  18. package/dist/esm/patterns/LlmSwarm.d.ts +100 -0
  19. package/dist/esm/patterns/LlmSwarm.js +109 -0
  20. package/dist/esm/patterns/LlmSwarm.js.map +1 -0
  21. package/dist/esm/patterns/index.d.ts +2 -0
  22. package/dist/esm/patterns/index.js +2 -0
  23. package/dist/esm/patterns/index.js.map +1 -1
  24. package/dist/index.js +4 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/patterns/LlmRouter.js +406 -0
  27. package/dist/patterns/LlmRouter.js.map +1 -0
  28. package/dist/patterns/LlmSwarm.js +113 -0
  29. package/dist/patterns/LlmSwarm.js.map +1 -0
  30. package/dist/patterns/index.js +6 -1
  31. package/dist/patterns/index.js.map +1 -1
  32. package/dist/types/core-flow/Workflow.d.ts +147 -0
  33. package/dist/types/core-flow/Workflow.d.ts.map +1 -0
  34. package/dist/types/embedders/index.d.ts +96 -1
  35. package/dist/types/embedders/index.d.ts.map +1 -1
  36. package/dist/types/index.d.ts +1 -0
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/dist/types/patterns/LlmRouter.d.ts +222 -0
  39. package/dist/types/patterns/LlmRouter.d.ts.map +1 -0
  40. package/dist/types/patterns/LlmSwarm.d.ts +101 -0
  41. package/dist/types/patterns/LlmSwarm.d.ts.map +1 -0
  42. package/dist/types/patterns/index.d.ts +2 -0
  43. package/dist/types/patterns/index.d.ts.map +1 -1
  44. package/package.json +4 -1
package/README.md CHANGED
@@ -171,7 +171,7 @@ const pipeline = Sequence.create()
171
171
  await pipeline.run({ message: 'URGENT: refund dispute on order #4411' });
172
172
  ```
173
173
 
174
- The fourth primitive is `Loop` — `Loop.repeat(agent).until(guard).times(5)`, with a mandatory budget guard. And the named patterns from the research literature ship pre-composed from the same four: `selfConsistency` · `reflection` · `debate` · `mapReduce` · `tot` · `swarm`. Because every composition is a flowchart, the structure you wrote is the structure you see in the UI — and the trace spans the whole pipeline, not one agent at a time. [Designing systems of agents ↓](#-build--design-your-agent-or-system-of-agents)
174
+ The fourth primitive is `Loop` — `Loop.repeat(agent).until(guard).times(5)`, with a mandatory budget guard. And the named patterns from the research literature ship pre-composed from the same four: `selfConsistency` · `reflection` · `debate` · `mapReduce` · `tot` · `swarm` · `llmSwarm` (a swarm whose hand-offs an LLM decides). Because every composition is a flowchart, the structure you wrote is the structure you see in the UI — and the trace spans the whole pipeline, not one agent at a time. [Designing systems of agents ↓](#-build--design-your-agent-or-system-of-agents)
175
175
 
176
176
  ---
177
177
 
@@ -753,7 +753,7 @@ The flowchart, recorders, and tests don't change between dev and prod.
753
753
 
754
754
  **Core**
755
755
  - 2 primitives — `LLMCall`, `Agent` (the ReAct loop)
756
- - 4 control flows — `Sequence`, `Parallel`, `Conditional`, `Loop`
756
+ - 4 control flows — `Sequence`, `Parallel`, `Conditional`, `Loop` (plus `workflow()`, the same sequence with every hand-off type-checked by the compiler)
757
757
  - 1 Injection primitive — `defineSkill` / `defineSteering` / `defineInstruction` / `defineFact`
758
758
  - 1 reliability gate — `.reliability({ preCheck, postDecide, providers, circuitBreaker, fallback })`
759
759
  - 1 tool dispatch primitive — `ToolProvider` (sync OR async) — `staticTools` · `gatedTools` · `skillScopedTools` · or a custom `ToolProvider` that discovers over hubs / MCP / per-tenant catalogs
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * workflow() — sequential steps whose hand-offs are checked by the compiler.
4
+ *
5
+ * WHY this exists: `Sequence` is the workhorse for "A, then B, then C",
6
+ * and every step it accepts has the same shape — takes `{ message }`,
7
+ * returns `string`. That is exactly right for chaining LLM calls, and
8
+ * exactly wrong the moment a step wants to hand the next one something
9
+ * structured: `Sequence` coerces any non-string step output to `''`
10
+ * (Sequence.ts, the step `outputMapper`), so a step that returns a parsed
11
+ * ticket silently hands the next step nothing at all. The mistake shows up
12
+ * as an empty prompt three steps later, at runtime, in production.
13
+ *
14
+ * `workflow()` closes that gap from both ends:
15
+ *
16
+ * - **At compile time** — step N's OUTPUT type must be what step N+1
17
+ * accepts. A `Runner<{ message: string }, Ticket>` followed by a
18
+ * `Runner<{ orderId: string }, string>` does not compile. The chain is
19
+ * proven before you run it, not debugged after.
20
+ * - **At run time** — a step's value is handed to the next step
21
+ * UNCHANGED. Objects stay objects. The one convenience is the house
22
+ * convention: a step that returns a `string` feeds the next step's
23
+ * `{ message }`, because that is what every LLM runner here wants.
24
+ *
25
+ * Pattern: Adapter over footprintjs's `addSubFlowChartNext`, with the
26
+ * type-level handoff proof carried by overloads (1–8 steps).
27
+ * Role: core-flow/ layer, alongside Sequence/Parallel/Conditional/Loop.
28
+ * Pure control flow — no LLM dependency.
29
+ * Emits: agentfootprint.composition.enter / exit, reported as kind
30
+ * `'Sequence'` — a workflow IS a sequential composition, and
31
+ * widening the public `CompositionKind` union would break
32
+ * exhaustive switches in consumer code for no behavioural gain.
33
+ *
34
+ * THREE HONEST LIMITS, all inherited from the engine and all verified in
35
+ * `test/core-flow/scenario/Workflow.test.ts` — worth knowing before you
36
+ * put rich objects on the wire:
37
+ *
38
+ * 1. Only PLAIN DATA crosses a step boundary. A value with a prototype
39
+ * (Date, Map, Set, a class instance) arrives as `{}`, and `undefined`
40
+ * fields are dropped. Send strings, numbers, arrays and plain
41
+ * objects; send a timestamp as an ISO string, not a `Date`.
42
+ * 2. A step must RETURN its output — the value handed forward is the
43
+ * step chart's traversal result. A step whose last stage returns
44
+ * nothing hands its whole scope forward instead.
45
+ * 3. The workflow's own input keys stay visible to LATER steps too
46
+ * (footprintjs's `getArgs()` inherits the run's arguments). A key the
47
+ * previous step actually produced always wins; a key it did NOT
48
+ * produce can still be read from the original input rather than
49
+ * coming back `undefined`.
50
+ *
51
+ * @example a typed three-step chain
52
+ * ```ts
53
+ * interface Ticket { orderId: string; angry: boolean }
54
+ *
55
+ * const parse: Runner<{ message: string }, Ticket> = …;
56
+ * const lookup: Runner<Ticket, { refundUsd: number }> = …;
57
+ * const reply: Runner<{ refundUsd: number }, string> = …;
58
+ *
59
+ * const intake = workflow(parse, lookup, reply);
60
+ * const answer = await intake.run({ message: 'where is my refund?' });
61
+ * // ^? string — the chain's last output type
62
+ *
63
+ * workflow(parse, reply); // ✗ compile error: Ticket is not { refundUsd }
64
+ * ```
65
+ */
66
+ Object.defineProperty(exports, "__esModule", { value: true });
67
+ exports.workflow = exports.Workflow = void 0;
68
+ const footprintjs_1 = require("footprintjs");
69
+ const RunnerBase_js_1 = require("../core/RunnerBase.js");
70
+ const AgentRecorder_js_1 = require("../recorders/core/AgentRecorder.js");
71
+ const CompositionRecorder_js_1 = require("../recorders/core/CompositionRecorder.js");
72
+ const ContextRecorder_js_1 = require("../recorders/core/ContextRecorder.js");
73
+ const StreamRecorder_js_1 = require("../recorders/core/StreamRecorder.js");
74
+ const typedEmit_js_1 = require("../recorders/core/typedEmit.js");
75
+ /**
76
+ * Hand the previous step's value to the next step as its input args.
77
+ *
78
+ * `string` → `{ message }` (the house convention). Plain object → itself.
79
+ * Anything else is a broken hand-off and says so loudly: the alternative
80
+ * is an empty input three steps downstream with nothing pointing back
81
+ * here.
82
+ */
83
+ function toStepArgs(value, stepNumber) {
84
+ if (typeof value === 'string')
85
+ return { message: value };
86
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
87
+ return { ...value };
88
+ }
89
+ const got = value === null ? 'null' : Array.isArray(value) ? 'an array' : typeof value;
90
+ throw new Error(`workflow: step ${stepNumber - 1} handed forward ${got}, but step ${stepNumber} needs an ` +
91
+ 'object (or a string, which arrives as { message }). Make each step return its output.');
92
+ }
93
+ /**
94
+ * A sequential composition that passes values through untouched. Build one
95
+ * with {@link workflow} — that factory carries the type-level chain proof.
96
+ */
97
+ class Workflow extends RunnerBase_js_1.RunnerBase {
98
+ name;
99
+ id;
100
+ steps;
101
+ opts;
102
+ currentRunContext = {
103
+ runStartMs: 0,
104
+ runId: 'pending',
105
+ compositionPath: [],
106
+ };
107
+ constructor(steps, opts = {}) {
108
+ super();
109
+ if (steps.length === 0) {
110
+ throw new Error('Workflow: must have at least one step');
111
+ }
112
+ this.opts = opts;
113
+ this.name = opts.name ?? 'Workflow';
114
+ this.id = opts.id ?? 'workflow';
115
+ this.steps = steps;
116
+ // Eager chart construction — see `RunnerBase.initChart` JSDoc.
117
+ this.initChart(() => this.buildChart());
118
+ }
119
+ async run(input, options) {
120
+ const executor = this.createExecutor();
121
+ this.lastExecutor = executor;
122
+ const result = await executor.run({ input: { ...input }, ...(options ?? {}) });
123
+ return this.finalizeResult(executor, result);
124
+ }
125
+ async resume(checkpoint, input, options) {
126
+ this.emitPauseResume(checkpoint, input);
127
+ const executor = this.createExecutor();
128
+ this.lastExecutor = executor;
129
+ const result = await executor.resume(checkpoint, input, options);
130
+ return this.finalizeResult(executor, result);
131
+ }
132
+ createExecutor() {
133
+ this.currentRunContext = {
134
+ runStartMs: Date.now(),
135
+ runId: (0, RunnerBase_js_1.makeRunId)(),
136
+ compositionPath: [`Workflow:${this.id}`],
137
+ };
138
+ const executor = new footprintjs_1.FlowChartExecutor(this.getSpec());
139
+ const dispatcher = this.getDispatcher();
140
+ const getRunCtx = () => this.currentRunContext;
141
+ executor.attachCombinedRecorder(new ContextRecorder_js_1.ContextRecorder({ dispatcher, getRunContext: getRunCtx }));
142
+ executor.attachCombinedRecorder((0, StreamRecorder_js_1.streamRecorder)({ dispatcher, getRunContext: getRunCtx }));
143
+ executor.attachCombinedRecorder((0, AgentRecorder_js_1.agentRecorder)({ dispatcher, getRunContext: getRunCtx }));
144
+ executor.attachCombinedRecorder((0, CompositionRecorder_js_1.compositionRecorder)({ dispatcher, getRunContext: getRunCtx }));
145
+ for (const r of this.attachedRecorders)
146
+ executor.attachCombinedRecorder(r);
147
+ return executor;
148
+ }
149
+ finalizeResult(executor, result) {
150
+ const paused = this.detectPause(executor, result);
151
+ if (paused)
152
+ return paused;
153
+ if (result instanceof Error)
154
+ throw result;
155
+ return result;
156
+ }
157
+ buildChart() {
158
+ const steps = this.steps;
159
+ const compositionId = this.id;
160
+ const compositionName = this.name;
161
+ const seed = (scope) => {
162
+ // The workflow's own input IS step 1's input — no unwrapping, no
163
+ // re-wrapping; that is the whole point of the typed chain.
164
+ scope.current = scope.$getArgs();
165
+ (0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.composition.enter', {
166
+ kind: 'Sequence',
167
+ id: compositionId,
168
+ name: compositionName,
169
+ childCount: steps.length,
170
+ });
171
+ };
172
+ // Root description prefix `Sequence:` is the taxonomy marker every
173
+ // consumer (Lens, FlowchartRecorder.mapTopologyToSteps) already reads.
174
+ let builder = (0, footprintjs_1.flowChart)('Seed', seed, 'seed', {
175
+ ...(this.opts.structureRecorders !== undefined && {
176
+ structureRecorders: [...this.opts.structureRecorders],
177
+ }),
178
+ description: `Sequence: ${steps.length}-step typed workflow`,
179
+ });
180
+ steps.forEach((step, index) => {
181
+ const stepNumber = index + 1;
182
+ builder = builder.addSubFlowChartNext(`step-${stepNumber}`, step.getSpec(), `Step ${stepNumber}`, {
183
+ inputMapper: (parent) => toStepArgs(parent.current, stepNumber),
184
+ // Untouched: whatever the step's chart returned is what the next
185
+ // step (or the caller) receives. No string coercion.
186
+ outputMapper: (sfOutput) => ({ current: sfOutput }),
187
+ });
188
+ });
189
+ builder = builder.addFunction('Finalize', (scope) => {
190
+ (0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.composition.exit', {
191
+ kind: 'Sequence',
192
+ id: compositionId,
193
+ name: compositionName,
194
+ status: 'ok',
195
+ durationMs: Date.now() - this.currentRunContext.runStartMs,
196
+ });
197
+ return scope.current;
198
+ }, 'finalize', 'Workflow finalize');
199
+ return builder.build();
200
+ }
201
+ }
202
+ exports.Workflow = Workflow;
203
+ function workflow(...steps) {
204
+ if (steps.length === 0) {
205
+ throw new Error('workflow(): needs at least one step');
206
+ }
207
+ return new Workflow(steps);
208
+ }
209
+ exports.workflow = workflow;
210
+ //# sourceMappingURL=Workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Workflow.js","sourceRoot":"","sources":["../../src/core-flow/Workflow.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,35 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.staticEmbedder = exports.localEmbedder = exports.openaiEmbedder = void 0;
27
+ /**
28
+ * Native output size of every OpenAI embedding model, so `.dimensions` reports
29
+ * the truth instead of one hard-coded guess.
30
+ *
31
+ * Sources: OpenAI embeddings guide — text-embedding-3-small "By default, the
32
+ * length of the embedding vector is 1536", text-embedding-3-large "3072"
33
+ * (https://developers.openai.com/api/docs/guides/embeddings); Microsoft Learn's
34
+ * Azure OpenAI model table, "Output Dimensions" column, for ada-002 = 1,536
35
+ * (https://learn.microsoft.com/en-us/azure/ai-foundry/openai/concepts/models).
36
+ */
37
+ const NATIVE_DIMENSIONS = {
38
+ 'text-embedding-3-small': 1536,
39
+ 'text-embedding-3-large': 3072,
40
+ 'text-embedding-ada-002': 1536,
41
+ };
42
+ /**
43
+ * OpenAI's hosted embeddings endpoint.
44
+ *
45
+ * `.dimensions` is the length callers WILL get back, never an assumption:
46
+ * an explicit `{ dimensions }` is sent to the API and reported; otherwise the
47
+ * model's documented native size is reported. A model outside
48
+ * {@link NATIVE_DIMENSIONS} (a gateway, a self-hosted model behind `baseURL`,
49
+ * an Azure deployment name, a future OpenAI model) has no size this library can
50
+ * know, so it is a construction-time error rather than a guess that a vector
51
+ * store would silently trust.
52
+ *
53
+ * @throws if there is no API key, or if `model` is unknown and `dimensions`
54
+ * was not supplied.
55
+ */
27
56
  function openaiEmbedder(options = {}) {
28
57
  const apiKey = options.apiKey ??
29
58
  (typeof process !== 'undefined' ? process.env?.['OPENAI_API_KEY'] : undefined);
@@ -31,13 +60,22 @@ function openaiEmbedder(options = {}) {
31
60
  throw new Error('openaiEmbedder: no API key — set OPENAI_API_KEY or pass { apiKey }.');
32
61
  }
33
62
  const model = options.model ?? 'text-embedding-3-small';
34
- const dimensions = options.dimensions ?? 1536;
63
+ // Only an EXPLICIT request is sent. Defaulting it and sending that would
64
+ // break ada-002 (which rejects the parameter) for callers who asked for
65
+ // nothing — the request body stays byte-identical unless you opt in.
66
+ const requested = options.dimensions;
67
+ const dimensions = requested ?? NATIVE_DIMENSIONS[model];
68
+ if (dimensions === undefined) {
69
+ throw new Error(`openaiEmbedder: unknown model '${model}' — its vector length is not something this ` +
70
+ `library can know, and reporting a wrong .dimensions silently corrupts a vector store. ` +
71
+ `Pass { dimensions } with the length that model returns.`);
72
+ }
35
73
  const url = `${options.baseURL ?? 'https://api.openai.com/v1'}/embeddings`;
36
74
  async function call(input, signal) {
37
75
  const res = await fetch(url, {
38
76
  method: 'POST',
39
77
  headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },
40
- body: JSON.stringify({ model, input }),
78
+ body: JSON.stringify(requested === undefined ? { model, input } : { model, input, dimensions: requested }),
41
79
  ...(signal ? { signal } : {}),
42
80
  });
43
81
  if (!res.ok)
@@ -61,17 +99,22 @@ function localEmbedder(options = {}) {
61
99
  const dimensions = options.dimensions ?? 384;
62
100
  const dtype = options.dtype ?? 'q8';
63
101
  let pipe;
102
+ const build = (m) => {
103
+ if (options.cacheDir && m.env && typeof m.env === 'object') {
104
+ m.env['cacheDir'] = options.cacheDir;
105
+ }
106
+ return m.pipeline('feature-extraction', model, { dtype });
107
+ };
64
108
  const getPipe = () => {
109
+ const injected = options.backend;
110
+ if (injected)
111
+ return (pipe ??= build(injected));
65
112
  // Variable specifier so the compiler/bundler does NOT resolve the module at
66
113
  // build time — @huggingface/transformers stays an optional peer dep, loaded
67
- // only when localEmbedder is actually used.
114
+ // only when localEmbedder is actually used. A bundler cannot see through
115
+ // this; bundled apps pass { backend } instead.
68
116
  const spec = '@huggingface/transformers';
69
- return (pipe ??= Promise.resolve(`${spec}`).then(s => __importStar(require(s))).then((mod) => {
70
- const m = mod;
71
- if (options.cacheDir)
72
- m.env['cacheDir'] = options.cacheDir;
73
- return m.pipeline('feature-extraction', model, { dtype });
74
- }));
117
+ return (pipe ??= Promise.resolve(`${spec}`).then(s => __importStar(require(s))).then((mod) => build(mod)));
75
118
  };
76
119
  return {
77
120
  dimensions,
@@ -92,24 +135,29 @@ function staticEmbedder(options = {}) {
92
135
  const dimensions = options.dimensions ?? 256;
93
136
  const spec = options.module ?? '@yarflam/potion-base-8m';
94
137
  let embedFn;
138
+ // potion-base-8m exports `embed(texts) => Promise<Float32Array[]>` (a batch
139
+ // async fn, also on its default export). Accept a small set of shapes so
140
+ // other Model2Vec builds slot in: a named `embed`/`encode` on the module or
141
+ // its default, or a default export that IS the fn.
142
+ const pick = (mod, source) => {
143
+ const m = mod;
144
+ const d = (m.default ?? {});
145
+ const fn = m['embed'] ??
146
+ d['embed'] ??
147
+ m['encode'] ??
148
+ d['encode'] ??
149
+ (typeof m.default === 'function' ? m.default : undefined);
150
+ if (!fn) {
151
+ throw new Error(`staticEmbedder: no embed()/encode() export on ${source}. Pass { module } or wrap it in your own Embedder.`);
152
+ }
153
+ return fn;
154
+ };
95
155
  const getEmbed = () => {
96
- return (embedFn ??= Promise.resolve(`${spec}`).then(s => __importStar(require(s))).then((mod) => {
97
- // potion-base-8m exports `embed(texts) => Promise<Float32Array[]>` (a batch
98
- // async fn, also on its default export). Accept a small set of shapes so
99
- // other Model2Vec builds slot in: a named `embed`/`encode` on the module or
100
- // its default, or a default export that IS the fn.
101
- const m = mod;
102
- const d = (m.default ?? {});
103
- const fn = m['embed'] ??
104
- d['embed'] ??
105
- m['encode'] ??
106
- d['encode'] ??
107
- (typeof m.default === 'function' ? m.default : undefined);
108
- if (!fn) {
109
- throw new Error(`staticEmbedder: no embed()/encode() export on '${spec}'. Pass { module } or wrap it in your own Embedder.`);
110
- }
111
- return fn;
112
- }));
156
+ const injected = options.backend;
157
+ if (injected) {
158
+ return (embedFn ??= Promise.resolve(pick(injected, 'the module passed as { backend }')));
159
+ }
160
+ return (embedFn ??= Promise.resolve(`${spec}`).then(s => __importStar(require(s))).then((mod) => pick(mod, `'${spec}'`)));
113
161
  };
114
162
  // Normalize a batch result into number[][] (one row per input). Handles
115
163
  // Float32Array[] (potion), number[][], and a single flat vector for the call.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embedders/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/embedders/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * workflow() — sequential steps whose hand-offs are checked by the compiler.
3
+ *
4
+ * WHY this exists: `Sequence` is the workhorse for "A, then B, then C",
5
+ * and every step it accepts has the same shape — takes `{ message }`,
6
+ * returns `string`. That is exactly right for chaining LLM calls, and
7
+ * exactly wrong the moment a step wants to hand the next one something
8
+ * structured: `Sequence` coerces any non-string step output to `''`
9
+ * (Sequence.ts, the step `outputMapper`), so a step that returns a parsed
10
+ * ticket silently hands the next step nothing at all. The mistake shows up
11
+ * as an empty prompt three steps later, at runtime, in production.
12
+ *
13
+ * `workflow()` closes that gap from both ends:
14
+ *
15
+ * - **At compile time** — step N's OUTPUT type must be what step N+1
16
+ * accepts. A `Runner<{ message: string }, Ticket>` followed by a
17
+ * `Runner<{ orderId: string }, string>` does not compile. The chain is
18
+ * proven before you run it, not debugged after.
19
+ * - **At run time** — a step's value is handed to the next step
20
+ * UNCHANGED. Objects stay objects. The one convenience is the house
21
+ * convention: a step that returns a `string` feeds the next step's
22
+ * `{ message }`, because that is what every LLM runner here wants.
23
+ *
24
+ * Pattern: Adapter over footprintjs's `addSubFlowChartNext`, with the
25
+ * type-level handoff proof carried by overloads (1–8 steps).
26
+ * Role: core-flow/ layer, alongside Sequence/Parallel/Conditional/Loop.
27
+ * Pure control flow — no LLM dependency.
28
+ * Emits: agentfootprint.composition.enter / exit, reported as kind
29
+ * `'Sequence'` — a workflow IS a sequential composition, and
30
+ * widening the public `CompositionKind` union would break
31
+ * exhaustive switches in consumer code for no behavioural gain.
32
+ *
33
+ * THREE HONEST LIMITS, all inherited from the engine and all verified in
34
+ * `test/core-flow/scenario/Workflow.test.ts` — worth knowing before you
35
+ * put rich objects on the wire:
36
+ *
37
+ * 1. Only PLAIN DATA crosses a step boundary. A value with a prototype
38
+ * (Date, Map, Set, a class instance) arrives as `{}`, and `undefined`
39
+ * fields are dropped. Send strings, numbers, arrays and plain
40
+ * objects; send a timestamp as an ISO string, not a `Date`.
41
+ * 2. A step must RETURN its output — the value handed forward is the
42
+ * step chart's traversal result. A step whose last stage returns
43
+ * nothing hands its whole scope forward instead.
44
+ * 3. The workflow's own input keys stay visible to LATER steps too
45
+ * (footprintjs's `getArgs()` inherits the run's arguments). A key the
46
+ * previous step actually produced always wins; a key it did NOT
47
+ * produce can still be read from the original input rather than
48
+ * coming back `undefined`.
49
+ *
50
+ * @example a typed three-step chain
51
+ * ```ts
52
+ * interface Ticket { orderId: string; angry: boolean }
53
+ *
54
+ * const parse: Runner<{ message: string }, Ticket> = …;
55
+ * const lookup: Runner<Ticket, { refundUsd: number }> = …;
56
+ * const reply: Runner<{ refundUsd: number }, string> = …;
57
+ *
58
+ * const intake = workflow(parse, lookup, reply);
59
+ * const answer = await intake.run({ message: 'where is my refund?' });
60
+ * // ^? string — the chain's last output type
61
+ *
62
+ * workflow(parse, reply); // ✗ compile error: Ticket is not { refundUsd }
63
+ * ```
64
+ */
65
+ import { type FlowchartCheckpoint, type RunOptions, type StructureRecorder } from 'footprintjs';
66
+ import type { RunnerPauseOutcome } from '../core/pause.js';
67
+ import type { Runner } from '../core/runner.js';
68
+ import { RunnerBase } from '../core/RunnerBase.js';
69
+ /**
70
+ * What the NEXT step must accept, given what the previous one returns.
71
+ *
72
+ * A `string` output feeds `{ message }` — the convention every runner in
73
+ * this library already speaks (LLMCall, Agent, Sequence, Swarm). Anything
74
+ * else is handed over as-is, so the next step's input type must be that
75
+ * same type.
76
+ */
77
+ export type NextStepInput<TPreviousOutput> = TPreviousOutput extends string ? {
78
+ message: string;
79
+ } : TPreviousOutput;
80
+ /** Any runner, viewed only as "a thing with a chart" — the workflow never
81
+ * needs a step's own input/output types at run time. */
82
+ type AnyStep = Runner<never, unknown>;
83
+ export interface WorkflowOptions {
84
+ /** Human-friendly name for events + topology. Default `'Workflow'`. */
85
+ readonly name?: string;
86
+ /** Stable id used for topology + events. Default `'workflow'`. */
87
+ readonly id?: string;
88
+ /**
89
+ * Optional build-time recorders passed through to footprintjs's
90
+ * `flowChart()` factory — they observe this workflow's own nodes (Seed +
91
+ * one mount per step + Finalize). Not propagated into the mounted step
92
+ * charts; attach them to each step runner for full coverage.
93
+ */
94
+ readonly structureRecorders?: readonly StructureRecorder[];
95
+ }
96
+ /**
97
+ * A sequential composition that passes values through untouched. Build one
98
+ * with {@link workflow} — that factory carries the type-level chain proof.
99
+ */
100
+ export declare class Workflow<TIn extends object = object, TOut = unknown> extends RunnerBase<TIn, TOut> {
101
+ readonly name: string;
102
+ readonly id: string;
103
+ private readonly steps;
104
+ private readonly opts;
105
+ private currentRunContext;
106
+ constructor(steps: readonly AnyStep[], opts?: WorkflowOptions);
107
+ run(input: TIn, options?: RunOptions): Promise<TOut | RunnerPauseOutcome>;
108
+ resume(checkpoint: FlowchartCheckpoint, input?: unknown, options?: RunOptions): Promise<TOut | RunnerPauseOutcome>;
109
+ private createExecutor;
110
+ private finalizeResult;
111
+ private buildChart;
112
+ }
113
+ /**
114
+ * Chain 1–8 runners into one, with every hand-off checked by the compiler.
115
+ *
116
+ * Step N's output type must be what step N+1 accepts — a `string` output
117
+ * feeds the next step's `{ message }` (the house convention), anything
118
+ * else is handed over as-is. A chain that does not line up is a COMPILE
119
+ * error, not a silent empty value at run time.
120
+ *
121
+ * @example LLM steps chain as they always have
122
+ * ```ts
123
+ * const draft = LLMCall.create({ provider, model }).system('Draft it.').build();
124
+ * const edit = LLMCall.create({ provider, model }).system('Tighten it.').build();
125
+ *
126
+ * const pipeline = workflow(draft, edit);
127
+ * const text = await pipeline.run({ message: 'a note about refunds' });
128
+ * ```
129
+ *
130
+ * @example structured hand-offs survive
131
+ * ```ts
132
+ * const classify: Runner<{ message: string }, { topic: string }> = …;
133
+ * const answer: Runner<{ topic: string }, string> = …;
134
+ *
135
+ * await workflow(classify, answer).run({ message: 'my card was declined' });
136
+ * ```
137
+ */
138
+ export declare function workflow<A extends object, B>(s1: Runner<A, B>): Workflow<A, B>;
139
+ export declare function workflow<A extends object, B, C>(s1: Runner<A, B>, s2: Runner<NextStepInput<B>, C>): Workflow<A, C>;
140
+ export declare function workflow<A extends object, B, C, D>(s1: Runner<A, B>, s2: Runner<NextStepInput<B>, C>, s3: Runner<NextStepInput<C>, D>): Workflow<A, D>;
141
+ export declare function workflow<A extends object, B, C, D, E>(s1: Runner<A, B>, s2: Runner<NextStepInput<B>, C>, s3: Runner<NextStepInput<C>, D>, s4: Runner<NextStepInput<D>, E>): Workflow<A, E>;
142
+ export declare function workflow<A extends object, B, C, D, E, F>(s1: Runner<A, B>, s2: Runner<NextStepInput<B>, C>, s3: Runner<NextStepInput<C>, D>, s4: Runner<NextStepInput<D>, E>, s5: Runner<NextStepInput<E>, F>): Workflow<A, F>;
143
+ export declare function workflow<A extends object, B, C, D, E, F, G>(s1: Runner<A, B>, s2: Runner<NextStepInput<B>, C>, s3: Runner<NextStepInput<C>, D>, s4: Runner<NextStepInput<D>, E>, s5: Runner<NextStepInput<E>, F>, s6: Runner<NextStepInput<F>, G>): Workflow<A, G>;
144
+ export declare function workflow<A extends object, B, C, D, E, F, G, H>(s1: Runner<A, B>, s2: Runner<NextStepInput<B>, C>, s3: Runner<NextStepInput<C>, D>, s4: Runner<NextStepInput<D>, E>, s5: Runner<NextStepInput<E>, F>, s6: Runner<NextStepInput<F>, G>, s7: Runner<NextStepInput<G>, H>): Workflow<A, H>;
145
+ export declare function workflow<A extends object, B, C, D, E, F, G, H, I>(s1: Runner<A, B>, s2: Runner<NextStepInput<B>, C>, s3: Runner<NextStepInput<C>, D>, s4: Runner<NextStepInput<D>, E>, s5: Runner<NextStepInput<E>, F>, s6: Runner<NextStepInput<F>, G>, s7: Runner<NextStepInput<G>, H>, s8: Runner<NextStepInput<H>, I>): Workflow<A, I>;
146
+ export {};