flowneer 0.6.1 → 0.7.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.
@@ -250,4 +250,4 @@ declare class FlowBuilder<S = any, P extends Record<string, unknown> = Record<st
250
250
  private _runSub;
251
251
  }
252
252
 
253
- export { FlowBuilder as F, type NodeFn as N, type RunOptions as R, type StepMeta as S, type Validator as V, type NodeOptions as a, type FlowneerPlugin as b, type FlowHooks as c, type NumberOrFn as d, type StreamEvent as e };
253
+ export { type AnchorStep as A, type BatchStep as B, FlowBuilder as F, type LoopStep as L, type NodeFn as N, type ParallelStep as P, type RunOptions as R, type StepMeta as S, type Validator as V, type FlowneerPlugin as a, type NodeOptions as b, type FlowHooks as c, type BranchStep as d, type FnStep as e, type NumberOrFn as f, type Step as g, type StreamEvent as h };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { F as FlowBuilder, c as FlowHooks, b as FlowneerPlugin, N as NodeFn, a as NodeOptions, d as NumberOrFn, R as RunOptions, S as StepMeta, e as StreamEvent, V as Validator } from './FlowBuilder-DssSGM7v.js';
1
+ export { F as FlowBuilder, c as FlowHooks, a as FlowneerPlugin, N as NodeFn, b as NodeOptions, f as NumberOrFn, R as RunOptions, S as StepMeta, h as StreamEvent, V as Validator } from './FlowBuilder-C3B91Fzo.js';
2
2
  export { FlowError, Fragment, InterruptError, fragment } from './src/index.js';
package/dist/index.js CHANGED
@@ -453,6 +453,8 @@ var Fragment = class extends FlowBuilder {
453
453
  );
454
454
  }
455
455
  /** @internal Fragments cannot be streamed — embed them via `.add()`. */
456
+ // v8 ignore next 5 — async generator has an implicit "resume after yield"
457
+ // branch that is unreachable here because we always throw before yielding.
456
458
  async *stream(_shared, _params) {
457
459
  throw new Error(
458
460
  "Fragment cannot be streamed directly \u2014 use .add() to embed it in a FlowBuilder"
@@ -0,0 +1,195 @@
1
+ import { a as FlowneerPlugin, F as FlowBuilder, N as NodeFn } from './FlowBuilder-C3B91Fzo.js';
2
+ import { ToolCall, ToolResult } from './plugins/tools/index.js';
3
+
4
+ /**
5
+ * The result of a `think` function.
6
+ *
7
+ * - Return `{ action: "finish", output }` to end the loop.
8
+ * - Return `{ action: "tool", calls }` to invoke tools, the results of
9
+ * which will be available in `shared.__toolResults` on the next iteration.
10
+ */
11
+ type ThinkResult = {
12
+ action: "finish";
13
+ output?: unknown;
14
+ } | {
15
+ action: "tool";
16
+ calls: ToolCall[];
17
+ };
18
+ interface ReActLoopOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
19
+ /**
20
+ * The "think" step — receives shared state (which includes tool results
21
+ * from the previous iteration) and returns the next action.
22
+ */
23
+ think: (shared: S, params: P) => ThinkResult | Promise<ThinkResult>;
24
+ /**
25
+ * Maximum number of think → act iterations. Defaults to 10.
26
+ * When exceeded the loop breaks and `shared.__reactExhausted = true`.
27
+ */
28
+ maxIterations?: number;
29
+ /**
30
+ * Optional callback invoked after each tool execution round.
31
+ * Useful for logging, appending to conversation history, etc.
32
+ */
33
+ onObservation?: (results: ToolResult[], shared: S, params: P) => void | Promise<void>;
34
+ }
35
+ declare module "../../Flowneer" {
36
+ interface FlowBuilder<S, P> {
37
+ /**
38
+ * Inserts a ReAct agent loop into the flow.
39
+ *
40
+ * The loop runs `think → [tool calls] → observation → repeat` until
41
+ * the think step returns `{ action: "finish" }` or `maxIterations`
42
+ * is reached.
43
+ *
44
+ * Requires `.withTools(tools)` to have been called first so that
45
+ * `shared.__tools` is available.
46
+ *
47
+ * @example
48
+ * flow
49
+ * .withTools([calculatorTool, searchTool])
50
+ * .withReActLoop({
51
+ * think: async (s) => {
52
+ * const response = await callLlm(buildPrompt(s));
53
+ * if (response.toolCalls.length) {
54
+ * return { action: "tool", calls: response.toolCalls };
55
+ * }
56
+ * return { action: "finish", output: response.text };
57
+ * },
58
+ * maxIterations: 5,
59
+ * })
60
+ */
61
+ withReActLoop(options: ReActLoopOptions<S, P>): this;
62
+ }
63
+ }
64
+ declare const withReActLoop: FlowneerPlugin;
65
+
66
+ interface HumanNodeOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
67
+ /**
68
+ * Key on `shared` where the prompt / question for the human is stored.
69
+ * The step will set `shared[promptKey]` before interrupting so the
70
+ * caller knows what input is expected.
71
+ * Defaults to `"__humanPrompt"`.
72
+ */
73
+ promptKey?: string;
74
+ /**
75
+ * Optional condition — when provided, the node only interrupts when
76
+ * the condition returns `true`. Defaults to always interrupt.
77
+ */
78
+ condition?: (shared: S, params: P) => boolean | Promise<boolean>;
79
+ /**
80
+ * Optional prompt message to store on `shared[promptKey]` before
81
+ * interrupting. If omitted, step uses whatever is already on shared.
82
+ */
83
+ prompt?: string | ((shared: S, params: P) => string | Promise<string>);
84
+ }
85
+ declare module "../../Flowneer" {
86
+ interface FlowBuilder<S, P> {
87
+ /**
88
+ * Insert a human-in-the-loop pause point.
89
+ *
90
+ * When this step executes it throws an `InterruptError` carrying a
91
+ * deep clone of `shared`. The caller catches the interrupt, obtains
92
+ * human input, and calls `resumeFlow()` to continue.
93
+ *
94
+ * @example
95
+ * const flow = new FlowBuilder<MyState>()
96
+ * .startWith(generateDraft)
97
+ * .humanNode({ prompt: "Please review the draft above." })
98
+ * .then(applyFeedback);
99
+ *
100
+ * try {
101
+ * await flow.run(shared);
102
+ * } catch (e) {
103
+ * if (e instanceof InterruptError) {
104
+ * const userInput = await getUserInput(e.savedShared.__humanPrompt);
105
+ * await resumeFlow(flow, e.savedShared, { feedback: userInput }, 2);
106
+ * }
107
+ * }
108
+ */
109
+ humanNode(options?: HumanNodeOptions<S, P>): this;
110
+ }
111
+ }
112
+ declare const withHumanNode: FlowneerPlugin;
113
+ /**
114
+ * Resume a flow from an `InterruptError`.
115
+ *
116
+ * Merges `edits` into the saved shared state, then re-runs the flow
117
+ * starting from step `fromStep` (defaults to 0, which replays from
118
+ * the beginning — combine with `withReplay(fromStep)` to skip already-
119
+ * completed steps).
120
+ *
121
+ * @param flow The same FlowBuilder instance that was interrupted.
122
+ * @param saved The `savedShared` captured by `InterruptError`.
123
+ * @param edits Partial state to merge (human's input / corrections).
124
+ * @param fromStep Step index to effectively resume from (uses `withReplay`).
125
+ */
126
+ declare function resumeFlow<S extends Record<string, any>>(flow: FlowBuilder<S, any>, saved: S, edits?: Partial<S>, fromStep?: number): Promise<void>;
127
+
128
+ /**
129
+ * Creates a supervisor → workers pattern.
130
+ *
131
+ * 1. `supervisor` runs first to set up context / assign tasks.
132
+ * 2. All `workers` run in parallel (with an optional reducer).
133
+ * 3. `supervisor` runs again (post step) to aggregate results.
134
+ *
135
+ * @example
136
+ * const flow = supervisorCrew<MyState>(
137
+ * async (s) => { s.tasks = splitIntoChunks(s.input); },
138
+ * [
139
+ * async (s) => { s.results ??= []; s.results.push(await doWork(s)); },
140
+ * async (s) => { s.results ??= []; s.results.push(await doWork(s)); },
141
+ * ],
142
+ * { post: async (s) => { s.summary = combine(s.results); } },
143
+ * );
144
+ */
145
+ declare function supervisorCrew<S = any, P extends Record<string, unknown> = Record<string, unknown>>(supervisor: NodeFn<S, P>, workers: NodeFn<S, P>[], options?: {
146
+ /** Optional post-parallel supervisor step for aggregation. */
147
+ post?: NodeFn<S, P>;
148
+ /** Optional reducer for parallel isolation. */
149
+ reducer?: (shared: S, drafts: S[]) => void;
150
+ }): FlowBuilder<S, P>;
151
+ /**
152
+ * Creates a strict sequential pipeline: each step runs in order,
153
+ * reading/writing to shared state as a baton pass.
154
+ *
155
+ * @example
156
+ * const flow = sequentialCrew<MyState>([
157
+ * async (s) => { s.research = await research(s.query); },
158
+ * async (s) => { s.draft = await writeDraft(s.research); },
159
+ * async (s) => { s.final = await editDraft(s.draft); },
160
+ * ]);
161
+ */
162
+ declare function sequentialCrew<S = any, P extends Record<string, unknown> = Record<string, unknown>>(steps: NodeFn<S, P>[]): FlowBuilder<S, P>;
163
+ /**
164
+ * Creates a hierarchical pattern: a top-level manager delegates to
165
+ * sub-team flows (each is its own `FlowBuilder` or supervisor crew).
166
+ *
167
+ * The manager runs first, then each team runs sequentially (for safety;
168
+ * use `parallel` within each team if needed), and a final aggregation
169
+ * step runs at the end.
170
+ *
171
+ * @example
172
+ * const flow = hierarchicalCrew<MyState>(
173
+ * async (s) => { s.plan = planTasks(s.input); },
174
+ * [researchTeamFlow, writingTeamFlow],
175
+ * async (s) => { s.output = mergeTeamResults(s); },
176
+ * );
177
+ */
178
+ declare function hierarchicalCrew<S = any, P extends Record<string, unknown> = Record<string, unknown>>(manager: NodeFn<S, P>, teams: FlowBuilder<S, P>[], aggregate?: NodeFn<S, P>): FlowBuilder<S, P>;
179
+ /**
180
+ * Creates a round-robin debate: each agent step runs in sequence,
181
+ * repeated `rounds` times, appending perspectives to shared state.
182
+ *
183
+ * @example
184
+ * const flow = roundRobinDebate<MyState>(
185
+ * [
186
+ * async (s) => { s.debate.push({ agent: "optimist", text: await optimist(s) }); },
187
+ * async (s) => { s.debate.push({ agent: "critic", text: await critic(s) }); },
188
+ * async (s) => { s.debate.push({ agent: "synthesiser", text: await synth(s) }); },
189
+ * ],
190
+ * 3, // 3 rounds
191
+ * );
192
+ */
193
+ declare function roundRobinDebate<S = any, P extends Record<string, unknown> = Record<string, unknown>>(agents: NodeFn<S, P>[], rounds: number): FlowBuilder<S, P>;
194
+
195
+ export { type HumanNodeOptions as H, type ReActLoopOptions as R, type ThinkResult as T, roundRobinDebate as a, supervisorCrew as b, withReActLoop as c, hierarchicalCrew as h, resumeFlow as r, sequentialCrew as s, withHumanNode as w };
@@ -1,195 +1,152 @@
1
- import { b as FlowneerPlugin, F as FlowBuilder, N as NodeFn } from '../../FlowBuilder-DssSGM7v.js';
2
- import { ToolCall, ToolResult } from '../tools/index.js';
1
+ export { H as HumanNodeOptions, R as ReActLoopOptions, T as ThinkResult, h as hierarchicalCrew, r as resumeFlow, a as roundRobinDebate, s as sequentialCrew, b as supervisorCrew, w as withHumanNode, c as withReActLoop } from '../../patterns-C3oKxgYZ.js';
2
+ import { F as FlowBuilder } from '../../FlowBuilder-C3B91Fzo.js';
3
+ import { ToolRegistry, ToolResult, Tool, ToolCall, ToolParam } from '../tools/index.js';
3
4
 
4
5
  /**
5
- * The result of a `think` function.
6
- *
7
- * - Return `{ action: "finish", output }` to end the loop.
8
- * - Return `{ action: "tool", calls }` to invoke tools, the results of
9
- * which will be available in `shared.__toolResults` on the next iteration.
6
+ * Minimal structural interface that matches a Zod ZodObject.
7
+ * We duck-type against `.shape` so this plugin has zero Zod dependency —
8
+ * pass a real `z.object(...)` and it just works.
10
9
  */
11
- type ThinkResult = {
12
- action: "finish";
13
- output?: unknown;
14
- } | {
15
- action: "tool";
16
- calls: ToolCall[];
17
- };
18
- interface ReActLoopOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
19
- /**
20
- * The "think" step — receives shared state (which includes tool results
21
- * from the previous iteration) and returns the next action.
22
- */
23
- think: (shared: S, params: P) => ThinkResult | Promise<ThinkResult>;
24
- /**
25
- * Maximum number of think → act iterations. Defaults to 10.
26
- * When exceeded the loop breaks and `shared.__reactExhausted = true`.
27
- */
28
- maxIterations?: number;
29
- /**
30
- * Optional callback invoked after each tool execution round.
31
- * Useful for logging, appending to conversation history, etc.
32
- */
33
- onObservation?: (results: ToolResult[], shared: S, params: P) => void | Promise<void>;
34
- }
35
- declare module "../../Flowneer" {
36
- interface FlowBuilder<S, P> {
37
- /**
38
- * Inserts a ReAct agent loop into the flow.
39
- *
40
- * The loop runs `think → [tool calls] → observation → repeat` until
41
- * the think step returns `{ action: "finish" }` or `maxIterations`
42
- * is reached.
43
- *
44
- * Requires `.withTools(tools)` to have been called first so that
45
- * `shared.__tools` is available.
46
- *
47
- * @example
48
- * flow
49
- * .withTools([calculatorTool, searchTool])
50
- * .withReActLoop({
51
- * think: async (s) => {
52
- * const response = await callLlm(buildPrompt(s));
53
- * if (response.toolCalls.length) {
54
- * return { action: "tool", calls: response.toolCalls };
55
- * }
56
- * return { action: "finish", output: response.text };
57
- * },
58
- * maxIterations: 5,
59
- * })
60
- */
61
- withReActLoop(options: ReActLoopOptions<S, P>): this;
62
- }
10
+ interface ZodLikeObject {
11
+ shape: Record<string, {
12
+ _def: {
13
+ typeName: string;
14
+ description?: string;
15
+ };
16
+ isOptional?(): boolean;
17
+ }>;
63
18
  }
64
- declare const withReActLoop: FlowneerPlugin;
65
-
66
- interface HumanNodeOptions<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
67
- /**
68
- * Key on `shared` where the prompt / question for the human is stored.
69
- * The step will set `shared[promptKey]` before interrupting so the
70
- * caller knows what input is expected.
71
- * Defaults to `"__humanPrompt"`.
72
- */
73
- promptKey?: string;
74
- /**
75
- * Optional condition — when provided, the node only interrupts when
76
- * the condition returns `true`. Defaults to always interrupt.
77
- */
78
- condition?: (shared: S, params: P) => boolean | Promise<boolean>;
79
- /**
80
- * Optional prompt message to store on `shared[promptKey]` before
81
- * interrupting. If omitted, step uses whatever is already on shared.
82
- */
83
- prompt?: string | ((shared: S, params: P) => string | Promise<string>);
19
+ /** Config when using a Zod-compatible schema. */
20
+ interface ToolConfigSchema<TArgs> {
21
+ name: string;
22
+ description: string;
23
+ schema: ZodLikeObject;
24
+ execute?: (args: TArgs) => unknown | Promise<unknown>;
84
25
  }
85
- declare module "../../Flowneer" {
86
- interface FlowBuilder<S, P> {
87
- /**
88
- * Insert a human-in-the-loop pause point.
89
- *
90
- * When this step executes it throws an `InterruptError` carrying a
91
- * deep clone of `shared`. The caller catches the interrupt, obtains
92
- * human input, and calls `resumeFlow()` to continue.
93
- *
94
- * @example
95
- * const flow = new FlowBuilder<MyState>()
96
- * .startWith(generateDraft)
97
- * .humanNode({ prompt: "Please review the draft above." })
98
- * .then(applyFeedback);
99
- *
100
- * try {
101
- * await flow.run(shared);
102
- * } catch (e) {
103
- * if (e instanceof InterruptError) {
104
- * const userInput = await getUserInput(e.savedShared.__humanPrompt);
105
- * await resumeFlow(flow, e.savedShared, { feedback: userInput }, 2);
106
- * }
107
- * }
108
- */
109
- humanNode(options?: HumanNodeOptions<S, P>): this;
110
- }
26
+ /** Config when using plain Flowneer ToolParam definitions. */
27
+ interface ToolConfigParams<TArgs> {
28
+ name: string;
29
+ description: string;
30
+ params: Record<string, ToolParam>;
31
+ execute?: (args: TArgs) => unknown | Promise<unknown>;
111
32
  }
112
- declare const withHumanNode: FlowneerPlugin;
113
- /**
114
- * Resume a flow from an `InterruptError`.
115
- *
116
- * Merges `edits` into the saved shared state, then re-runs the flow
117
- * starting from step `fromStep` (defaults to 0, which replays from
118
- * the beginning — combine with `withReplay(fromStep)` to skip already-
119
- * completed steps).
120
- *
121
- * @param flow The same FlowBuilder instance that was interrupted.
122
- * @param saved The `savedShared` captured by `InterruptError`.
123
- * @param edits Partial state to merge (human's input / corrections).
124
- * @param fromStep Step index to effectively resume from (uses `withReplay`).
125
- */
126
- declare function resumeFlow<S extends Record<string, any>>(flow: FlowBuilder<S, any>, saved: S, edits?: Partial<S>, fromStep?: number): Promise<void>;
127
-
33
+ type ToolConfig<TArgs> = ToolConfigSchema<TArgs> | ToolConfigParams<TArgs>;
128
34
  /**
129
- * Creates a supervisor workers pattern.
35
+ * Create a Flowneer `Tool` from an execute function + config.
130
36
  *
131
- * 1. `supervisor` runs first to set up context / assign tasks.
132
- * 2. All `workers` run in parallel (with an optional reducer).
133
- * 3. `supervisor` runs again (post step) to aggregate results.
37
+ * Mirrors LangChain's `tool()` factory. Accepts either:
38
+ * - `schema: z.object(...)` a Zod-compatible schema (duck-typed, no import needed)
39
+ * - `params: Record<string, ToolParam>` plain Flowneer param definitions
134
40
  *
135
41
  * @example
136
- * const flow = supervisorCrew<MyState>(
137
- * async (s) => { s.tasks = splitIntoChunks(s.input); },
138
- * [
139
- * async (s) => { s.results ??= []; s.results.push(await doWork(s)); },
140
- * async (s) => { s.results ??= []; s.results.push(await doWork(s)); },
141
- * ],
142
- * { post: async (s) => { s.summary = combine(s.results); } },
42
+ * // With Zod schema:
43
+ * const getWeather = tool(
44
+ * ({ city }) => `Always sunny in ${city}!`,
45
+ * {
46
+ * name: "get_weather",
47
+ * description: "Get the weather for a given city",
48
+ * schema: z.object({ city: z.string().describe("The city name") }),
49
+ * },
143
50
  * );
144
- */
145
- declare function supervisorCrew<S = any, P extends Record<string, unknown> = Record<string, unknown>>(supervisor: NodeFn<S, P>, workers: NodeFn<S, P>[], options?: {
146
- /** Optional post-parallel supervisor step for aggregation. */
147
- post?: NodeFn<S, P>;
148
- /** Optional reducer for parallel isolation. */
149
- reducer?: (shared: S, drafts: S[]) => void;
150
- }): FlowBuilder<S, P>;
151
- /**
152
- * Creates a strict sequential pipeline: each step runs in order,
153
- * reading/writing to shared state as a baton pass.
154
51
  *
155
- * @example
156
- * const flow = sequentialCrew<MyState>([
157
- * async (s) => { s.research = await research(s.query); },
158
- * async (s) => { s.draft = await writeDraft(s.research); },
159
- * async (s) => { s.final = await editDraft(s.draft); },
160
- * ]);
52
+ * // With plain params:
53
+ * const getTime = tool(
54
+ * () => new Date().toISOString(),
55
+ * {
56
+ * name: "get_time",
57
+ * description: "Get the current UTC time",
58
+ * params: {},
59
+ * },
60
+ * );
161
61
  */
162
- declare function sequentialCrew<S = any, P extends Record<string, unknown> = Record<string, unknown>>(steps: NodeFn<S, P>[]): FlowBuilder<S, P>;
62
+ declare function tool<TArgs = Record<string, unknown>>(execute: (args: TArgs) => unknown | Promise<unknown>, config: ToolConfig<TArgs>): Tool<TArgs>;
63
+ /** Minimal chat message shape understood by `createAgent`. */
64
+ interface ChatMessage {
65
+ role: "system" | "user" | "assistant" | "tool";
66
+ content: string;
67
+ /** Present on assistant messages when the LLM wants to call tools. */
68
+ tool_calls?: Array<{
69
+ id: string;
70
+ type: "function";
71
+ function: {
72
+ name: string;
73
+ arguments: string;
74
+ };
75
+ }>;
76
+ /** Present on tool result messages. */
77
+ tool_call_id?: string;
78
+ }
79
+ /** Tool definition shape forwarded to the LLM. */
80
+ interface LlmToolDef {
81
+ name: string;
82
+ description: string;
83
+ parameters: {
84
+ type: "object";
85
+ properties: Record<string, {
86
+ type: string;
87
+ description: string;
88
+ }>;
89
+ required: string[];
90
+ };
91
+ }
92
+ /** Response returned by an `LlmAdapter`. */
93
+ interface LlmResponse {
94
+ /** Plain-text reply — present when the model is done. */
95
+ text?: string;
96
+ /** Tool calls — present when the model wants to invoke tools. */
97
+ toolCalls?: ToolCall[];
98
+ }
163
99
  /**
164
- * Creates a hierarchical pattern: a top-level manager delegates to
165
- * sub-team flows (each is its own `FlowBuilder` or supervisor crew).
100
+ * A vendor-agnostic LLM adapter.
166
101
  *
167
- * The manager runs first, then each team runs sequentially (for safety;
168
- * use `parallel` within each team if needed), and a final aggregation
169
- * step runs at the end.
102
+ * Receives the current conversation history and available tool definitions,
103
+ * returns either a final answer or a list of tool calls.
170
104
  *
171
105
  * @example
172
- * const flow = hierarchicalCrew<MyState>(
173
- * async (s) => { s.plan = planTasks(s.input); },
174
- * [researchTeamFlow, writingTeamFlow],
175
- * async (s) => { s.output = mergeTeamResults(s); },
176
- * );
106
+ * // OpenAI adapter (see examples/agentExample.ts for a full implementation)
107
+ * const callLlm: LlmAdapter = async (messages, tools) => { ... };
177
108
  */
178
- declare function hierarchicalCrew<S = any, P extends Record<string, unknown> = Record<string, unknown>>(manager: NodeFn<S, P>, teams: FlowBuilder<S, P>[], aggregate?: NodeFn<S, P>): FlowBuilder<S, P>;
109
+ type LlmAdapter = (messages: ChatMessage[], tools: LlmToolDef[]) => Promise<LlmResponse>;
110
+ interface AgentState {
111
+ /** The user's input prompt. */
112
+ input: string;
113
+ /** The agent's final answer — set after the flow completes. */
114
+ output?: string;
115
+ /** Conversation history accumulated during the run. */
116
+ messages: ChatMessage[];
117
+ /** Optional system prompt (can also be passed to `createAgent`). */
118
+ systemPrompt?: string;
119
+ __tools?: ToolRegistry;
120
+ __reactOutput?: unknown;
121
+ __toolResults?: ToolResult[];
122
+ __reactExhausted?: boolean;
123
+ }
124
+ interface CreateAgentOptions {
125
+ /** Tools the agent can invoke. */
126
+ tools: Tool[];
127
+ /** LLM adapter — call your preferred provider here. */
128
+ callLlm: LlmAdapter;
129
+ /** System prompt inserted before the conversation. */
130
+ systemPrompt?: string;
131
+ /** Maximum think → act iterations. Defaults to 10. */
132
+ maxIterations?: number;
133
+ }
179
134
  /**
180
- * Creates a round-robin debate: each agent step runs in sequence,
181
- * repeated `rounds` times, appending perspectives to shared state.
135
+ * Create a reusable agent flow.
136
+ *
137
+ * Returns a `FlowBuilder<AgentState>`. Call `.run(state)` to execute.
182
138
  *
183
139
  * @example
184
- * const flow = roundRobinDebate<MyState>(
185
- * [
186
- * async (s) => { s.debate.push({ agent: "optimist", text: await optimist(s) }); },
187
- * async (s) => { s.debate.push({ agent: "critic", text: await critic(s) }); },
188
- * async (s) => { s.debate.push({ agent: "synthesiser", text: await synth(s) }); },
189
- * ],
190
- * 3, // 3 rounds
191
- * );
140
+ * const agent = createAgent({
141
+ * tools: [getWeather, getTime],
142
+ * callLlm: openAiAdapter,
143
+ * systemPrompt: "You are a helpful assistant.",
144
+ * });
145
+ *
146
+ * const state: AgentState = { input: "What's the weather in Paris?", messages: [] };
147
+ * await agent.run(state);
148
+ * console.log(state.output);
192
149
  */
193
- declare function roundRobinDebate<S = any, P extends Record<string, unknown> = Record<string, unknown>>(agents: NodeFn<S, P>[], rounds: number): FlowBuilder<S, P>;
150
+ declare function createAgent(options: CreateAgentOptions): FlowBuilder<AgentState>;
194
151
 
195
- export { type HumanNodeOptions, type ReActLoopOptions, type ThinkResult, hierarchicalCrew, resumeFlow, roundRobinDebate, sequentialCrew, supervisorCrew, withHumanNode, withReActLoop };
152
+ export { type AgentState, type ChatMessage, type CreateAgentOptions, type LlmAdapter, type LlmResponse, type LlmToolDef, type ToolConfig, type ToolConfigParams, type ToolConfigSchema, type ZodLikeObject, createAgent, tool };
@@ -601,12 +601,181 @@ function roundRobinDebate(agents, rounds) {
601
601
  );
602
602
  return flow;
603
603
  }
604
+
605
+ // plugins/tools/withTools.ts
606
+ var ToolRegistry = class {
607
+ _tools = /* @__PURE__ */ new Map();
608
+ constructor(tools) {
609
+ for (const t of tools) this._tools.set(t.name, t);
610
+ }
611
+ /** Get a registered tool by name. */
612
+ get(name) {
613
+ return this._tools.get(name);
614
+ }
615
+ /** Check if a tool is registered. */
616
+ has(name) {
617
+ return this._tools.has(name);
618
+ }
619
+ /** List all registered tool names. */
620
+ names() {
621
+ return [...this._tools.keys()];
622
+ }
623
+ /** Return tool definitions as plain objects (suitable for LLM schemas). */
624
+ definitions() {
625
+ return [...this._tools.values()].map((t) => {
626
+ const required = Object.entries(t.params).filter(([, p]) => p.required !== false).map(([k]) => k);
627
+ return {
628
+ name: t.name,
629
+ description: t.description,
630
+ parameters: {
631
+ type: "object",
632
+ properties: Object.fromEntries(
633
+ Object.entries(t.params).map(([k, p]) => [
634
+ k,
635
+ { type: p.type, description: p.description }
636
+ ])
637
+ ),
638
+ required
639
+ }
640
+ };
641
+ });
642
+ }
643
+ /**
644
+ * Execute a single tool call. Returns a `ToolResult`.
645
+ * Catches tool errors and returns them as `{ error }` rather than throwing.
646
+ */
647
+ async execute(call) {
648
+ const tool2 = this._tools.get(call.name);
649
+ if (!tool2) {
650
+ return {
651
+ callId: call.id,
652
+ name: call.name,
653
+ error: `unknown tool: ${call.name}`
654
+ };
655
+ }
656
+ try {
657
+ const result = await tool2.execute(call.args);
658
+ return { callId: call.id, name: call.name, result };
659
+ } catch (err) {
660
+ return {
661
+ callId: call.id,
662
+ name: call.name,
663
+ error: err instanceof Error ? err.message : String(err)
664
+ };
665
+ }
666
+ }
667
+ /**
668
+ * Execute multiple tool calls concurrently.
669
+ * Returns results in the same order as the input calls.
670
+ */
671
+ async executeAll(calls) {
672
+ return Promise.all(calls.map((c) => this.execute(c)));
673
+ }
674
+ };
675
+ var withTools = {
676
+ withTools(tools) {
677
+ const registry = new ToolRegistry(tools);
678
+ this._setHooks({
679
+ beforeFlow: (shared) => {
680
+ shared.__tools = registry;
681
+ }
682
+ });
683
+ return this;
684
+ }
685
+ };
686
+
687
+ // plugins/agent/createAgent.ts
688
+ FlowBuilder.use(withTools);
689
+ FlowBuilder.use(withReActLoop);
690
+ function zodTypeToParamType(typeName) {
691
+ switch (typeName) {
692
+ case "ZodNumber":
693
+ return "number";
694
+ case "ZodBoolean":
695
+ return "boolean";
696
+ case "ZodObject":
697
+ return "object";
698
+ case "ZodArray":
699
+ return "array";
700
+ default:
701
+ return "string";
702
+ }
703
+ }
704
+ function zodSchemaToParams(schema) {
705
+ const params = {};
706
+ for (const [key, field] of Object.entries(schema.shape)) {
707
+ params[key] = {
708
+ type: zodTypeToParamType(field._def.typeName),
709
+ description: field._def.description ?? key,
710
+ required: field.isOptional ? !field.isOptional() : true
711
+ };
712
+ }
713
+ return params;
714
+ }
715
+ function isSchemaConfig(cfg) {
716
+ return "schema" in cfg && cfg.schema != null;
717
+ }
718
+ function tool(execute, config) {
719
+ const params = isSchemaConfig(config) ? zodSchemaToParams(config.schema) : config.params;
720
+ return {
721
+ name: config.name,
722
+ description: config.description,
723
+ params,
724
+ execute
725
+ };
726
+ }
727
+ function createAgent(options) {
728
+ const { tools, callLlm, systemPrompt, maxIterations = 10 } = options;
729
+ return new FlowBuilder().withTools(tools).startWith((s) => {
730
+ s.messages = [];
731
+ const sys = systemPrompt ?? s.systemPrompt;
732
+ if (sys) s.messages.push({ role: "system", content: sys });
733
+ s.messages.push({ role: "user", content: s.input });
734
+ }).withReActLoop({
735
+ maxIterations,
736
+ think: async (s) => {
737
+ const toolDefs = s.__tools.definitions();
738
+ const response = await callLlm(s.messages, toolDefs);
739
+ if (response.toolCalls && response.toolCalls.length > 0) {
740
+ s.messages.push({
741
+ role: "assistant",
742
+ content: "",
743
+ tool_calls: response.toolCalls.map((tc) => ({
744
+ id: tc.id ?? `call_${tc.name}_${Date.now()}`,
745
+ type: "function",
746
+ function: {
747
+ name: tc.name,
748
+ arguments: JSON.stringify(tc.args)
749
+ }
750
+ }))
751
+ });
752
+ return { action: "tool", calls: response.toolCalls };
753
+ }
754
+ s.messages.push({ role: "assistant", content: response.text ?? "" });
755
+ return { action: "finish", output: response.text };
756
+ },
757
+ // After each tool round, append results so the next think step has context.
758
+ onObservation: (results, s) => {
759
+ for (const r of results) {
760
+ s.messages.push({
761
+ role: "tool",
762
+ tool_call_id: r.callId ?? r.name,
763
+ content: r.error != null ? `Error: ${r.error}` : JSON.stringify(r.result)
764
+ });
765
+ }
766
+ }
767
+ }).then((s) => {
768
+ s.output = typeof s.__reactOutput === "string" ? s.__reactOutput : JSON.stringify(s.__reactOutput ?? "");
769
+ });
770
+ }
604
771
  export {
772
+ createAgent,
605
773
  hierarchicalCrew,
606
774
  resumeFlow,
607
775
  roundRobinDebate,
608
776
  sequentialCrew,
609
777
  supervisorCrew,
778
+ tool,
610
779
  withHumanNode,
611
780
  withReActLoop
612
781
  };
@@ -1,4 +1,4 @@
1
- import { b as FlowneerPlugin, N as NodeFn, a as NodeOptions } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  declare module "../../Flowneer" {
4
4
  interface FlowBuilder<S, P> {
@@ -1,4 +1,4 @@
1
- import { F as FlowBuilder } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { F as FlowBuilder } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  /** Case-insensitive exact match. Returns 1.0 or 0.0. */
4
4
  declare function exactMatch(predicted: string, expected: string): number;
@@ -1,4 +1,49 @@
1
- import { N as NodeFn, a as NodeOptions, b as FlowneerPlugin } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-C3B91Fzo.js';
2
+
3
+ /** A single node entry in the exported graph. */
4
+ interface GraphNodeExport {
5
+ name: string;
6
+ /** Numeric options that were supplied at `.addNode()` time. */
7
+ options?: {
8
+ retries?: number | string;
9
+ delaySec?: number | string;
10
+ timeoutMs?: number | string;
11
+ };
12
+ }
13
+ /** A single edge entry in the exported graph. */
14
+ interface GraphEdgeExport {
15
+ from: string;
16
+ to: string;
17
+ /** `true` when the edge has a runtime condition (back-edge / skip-ahead). */
18
+ conditional: boolean;
19
+ }
20
+ /** The root object returned by `.exportGraph("json")`. */
21
+ interface GraphExport {
22
+ format: "json";
23
+ nodes: GraphNodeExport[];
24
+ edges: GraphEdgeExport[];
25
+ }
26
+ /**
27
+ * Supported export formats.
28
+ * - `"json"` — JSON-serialisable structure (implemented).
29
+ * - `"mermaid"` — Mermaid flowchart string (reserved for future use).
30
+ */
31
+ type ExportFormat = "json" | "mermaid";
32
+ type FormatResult<F extends ExportFormat> = F extends "json" ? GraphExport : F extends "mermaid" ? string : never;
33
+ declare module "../../Flowneer" {
34
+ interface FlowBuilder<S, P> {
35
+ /**
36
+ * Export the graph (nodes + edges) in the given format.
37
+ *
38
+ * When only `withExportGraph` is loaded this returns `GraphExport`.
39
+ * Load `withExportFlow` to get the unified `FlowExport` shape instead.
40
+ *
41
+ * Non-JSON formats are reserved for future implementation.
42
+ */
43
+ exportGraph<F extends ExportFormat>(format: F): FormatResult<F>;
44
+ }
45
+ }
46
+ declare const withExportGraph: FlowneerPlugin;
2
47
 
3
48
  interface GraphNode<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
4
49
  name: string;
@@ -42,6 +87,7 @@ declare module "../../Flowneer" {
42
87
  compile(): this;
43
88
  }
44
89
  }
90
+
45
91
  declare const withGraph: FlowneerPlugin;
46
92
 
47
- export { type GraphEdge, type GraphNode, withGraph };
93
+ export { type ExportFormat, type GraphEdge, type GraphEdgeExport, type GraphExport, type GraphNode, type GraphNodeExport, withExportGraph, withGraph };
@@ -1,3 +1,58 @@
1
+ // plugins/graph/withExportGraph.ts
2
+ function serializeOption(val) {
3
+ if (val === void 0 || val === null) return void 0;
4
+ if (typeof val === "number") return val === 0 ? void 0 : val;
5
+ if (typeof val === "function") return "<dynamic>";
6
+ return void 0;
7
+ }
8
+ function toJson(nodes, edges) {
9
+ const nodeExports = [];
10
+ for (const node of nodes.values()) {
11
+ const opts = node.options;
12
+ const optExport = opts ? {
13
+ retries: serializeOption(opts.retries),
14
+ delaySec: serializeOption(opts.delaySec),
15
+ timeoutMs: serializeOption(opts.timeoutMs)
16
+ } : void 0;
17
+ const hasOpts = optExport && (optExport.retries !== void 0 || optExport.delaySec !== void 0 || optExport.timeoutMs !== void 0);
18
+ nodeExports.push({
19
+ name: node.name,
20
+ ...hasOpts ? { options: optExport } : {}
21
+ });
22
+ }
23
+ const edgeExports = edges.map((e) => ({
24
+ from: e.from,
25
+ to: e.to,
26
+ conditional: !!e.condition
27
+ }));
28
+ return { format: "json", nodes: nodeExports, edges: edgeExports };
29
+ }
30
+ function dispatch(format, nodes, edges) {
31
+ switch (format) {
32
+ case "json":
33
+ return toJson(nodes, edges);
34
+ case "mermaid":
35
+ throw new Error(
36
+ 'exportGraph: "mermaid" format is not yet implemented. Use "json" for now.'
37
+ );
38
+ default: {
39
+ const _exhaustive = format;
40
+ throw new Error(`exportGraph: unknown format "${_exhaustive}"`);
41
+ }
42
+ }
43
+ }
44
+ var withExportGraph = {
45
+ exportGraph(format = "json") {
46
+ const store = this.__graphStore;
47
+ if (!store || store.nodes.size === 0) {
48
+ throw new Error(
49
+ "exportGraph: no graph nodes found. Call .addNode() / .addEdge() before .exportGraph()."
50
+ );
51
+ }
52
+ return dispatch(format, store.nodes, store.edges);
53
+ }
54
+ };
55
+
1
56
  // plugins/graph/index.ts
2
57
  function compileGraph(nodes, edges) {
3
58
  const unconditional = edges.filter((e) => !e.condition);
@@ -132,5 +187,6 @@ function _getGraphStore(builder) {
132
187
  return builder.__graphStore;
133
188
  }
134
189
  export {
190
+ withExportGraph,
135
191
  withGraph
136
192
  };
@@ -5,10 +5,10 @@ export { RateLimitOptions, withCostTracker, withRateLimit, withTokenBudget } fro
5
5
  export { withAtomicUpdates, withDryRun, withMocks, withStepLimit } from './dev/index.js';
6
6
  export { StreamSubscriber, emit, peekChannel, receiveFrom, sendTo, withChannels, withStream } from './messaging/index.js';
7
7
  export { Tool, ToolCall, ToolParam, ToolRegistry, ToolResult, executeTool, executeTools, getTools, withTools } from './tools/index.js';
8
- export { HumanNodeOptions, ReActLoopOptions, ThinkResult, hierarchicalCrew, resumeFlow, roundRobinDebate, sequentialCrew, supervisorCrew, withHumanNode, withReActLoop } from './agent/index.js';
8
+ export { H as HumanNodeOptions, R as ReActLoopOptions, T as ThinkResult, h as hierarchicalCrew, r as resumeFlow, a as roundRobinDebate, s as sequentialCrew, b as supervisorCrew, w as withHumanNode, c as withReActLoop } from '../patterns-C3oKxgYZ.js';
9
9
  export { BufferWindowMemory, BufferWindowOptions, KVMemory, Memory, MemoryMessage, SummaryMemory, SummaryMemoryOptions, withMemory } from './memory/index.js';
10
10
  export { parseJsonOutput, parseListOutput, parseMarkdownTable, parseRegexOutput } from './output/index.js';
11
11
  export { Span, TelemetryDaemon, TelemetryExporter, TelemetryOptions, consoleExporter, otlpExporter, withTelemetry } from './telemetry/index.js';
12
12
  export { EvalResult, EvalSummary, ScoreFn, answerRelevance, containsMatch, exactMatch, f1Score, retrievalPrecision, retrievalRecall, runEvalSuite } from './eval/index.js';
13
13
  export { GraphEdge, GraphNode, withGraph } from './graph/index.js';
14
- import '../FlowBuilder-DssSGM7v.js';
14
+ import '../FlowBuilder-C3B91Fzo.js';
@@ -1,4 +1,4 @@
1
- import { b as FlowneerPlugin, V as Validator } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin, V as Validator } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  declare module "../../Flowneer" {
4
4
  interface FlowBuilder<S, P> {
@@ -1,4 +1,4 @@
1
- import { b as FlowneerPlugin } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  /** A single message in conversational memory. */
4
4
  interface MemoryMessage {
@@ -1,4 +1,4 @@
1
- import { b as FlowneerPlugin } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  /** Send a message to a named channel on `shared.__channels`. */
4
4
  declare function sendTo<S extends Record<string, any>>(shared: S, channel: string, message: unknown): void;
@@ -1,4 +1,4 @@
1
- import { b as FlowneerPlugin, S as StepMeta } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin, S as StepMeta } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  declare module "../../Flowneer" {
4
4
  interface FlowBuilder<S, P> {
@@ -1,4 +1,4 @@
1
- import { V as Validator } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { V as Validator } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  /**
4
4
  * Extract and parse JSON from a (possibly noisy) LLM response.
@@ -1,4 +1,4 @@
1
- import { b as FlowneerPlugin } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  interface CheckpointStore<S = any> {
4
4
  /** Called after each successful step with the step index and current shared state. */
@@ -1,4 +1,4 @@
1
- import { N as NodeFn, b as FlowneerPlugin } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { N as NodeFn, a as FlowneerPlugin } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  declare module "../../Flowneer" {
4
4
  interface FlowBuilder<S, P> {
@@ -58,4 +58,56 @@ declare module "../../Flowneer" {
58
58
  }
59
59
  declare const withCycles: FlowneerPlugin;
60
60
 
61
- export { type CircuitBreakerOptions, withCircuitBreaker, withCycles, withFallback, withTimeout };
61
+ declare module "../../Flowneer" {
62
+ interface FlowBuilder<S, P> {
63
+ /**
64
+ * Begin a try / catch / finally block.
65
+ *
66
+ * Executes all steps in `tryFrag`. If any step throws:
67
+ * - the `.catch()` fragment runs (if registered), with the caught error
68
+ * available as `shared.__tryError`;
69
+ * - if no `.catch()` fragment is registered the error propagates.
70
+ *
71
+ * The `.finally()` fragment (if registered) always runs last, regardless
72
+ * of success or failure.
73
+ *
74
+ * @example
75
+ * flow
76
+ * .try(
77
+ * fragment().then(risky).then(another)
78
+ * )
79
+ * .catch(
80
+ * fragment()
81
+ * .then((s) => { s.recovery = true; })
82
+ * .anchor("recover")
83
+ * .then(recoveryStep)
84
+ * )
85
+ * .finally(
86
+ * fragment().then(cleanup)
87
+ * );
88
+ */
89
+ try(tryFrag: FlowBuilder<S, P>): this;
90
+ /**
91
+ * Register the catch handler for the immediately preceding `.try()` block.
92
+ *
93
+ * The caught error is stored on `shared.__tryError` before the fragment
94
+ * runs and removed once it completes (or throws). Access the original
95
+ * error and its `.cause` inside the catch fragment:
96
+ *
97
+ * ```ts
98
+ * .catch(fragment().then((s) => console.error(s.__tryError)))
99
+ * ```
100
+ */
101
+ catch(catchFrag: FlowBuilder<S, P>): this;
102
+ /**
103
+ * Register a finally handler that always runs after the preceding `.try()`
104
+ * (and optional `.catch()`) block — whether or not an error was thrown.
105
+ *
106
+ * Clears the pending try/catch state from the builder.
107
+ */
108
+ finally(finallyFrag: FlowBuilder<S, P>): this;
109
+ }
110
+ }
111
+ declare const withTryCatch: FlowneerPlugin;
112
+
113
+ export { type CircuitBreakerOptions, withCircuitBreaker, withCycles, withFallback, withTimeout, withTryCatch };
@@ -116,9 +116,85 @@ var withCycles = {
116
116
  return this;
117
117
  }
118
118
  };
119
+
120
+ // src/errors.ts
121
+ var FlowError = class extends Error {
122
+ step;
123
+ cause;
124
+ constructor(step, cause) {
125
+ super(
126
+ `Flow failed at ${step}: ${cause instanceof Error ? cause.message : String(cause)}`
127
+ );
128
+ this.name = "FlowError";
129
+ this.step = step;
130
+ this.cause = cause;
131
+ }
132
+ };
133
+
134
+ // plugins/resilience/withTryCatch.ts
135
+ var PENDING_KEY = "__flowneerPendingTryCatch";
136
+ var withTryCatch = {
137
+ try(tryFrag) {
138
+ const block = {
139
+ tryFrag,
140
+ catchFrag: null,
141
+ finallyFrag: null
142
+ };
143
+ this[PENDING_KEY] = block;
144
+ this._addFn(async (shared, params) => {
145
+ let thrownError;
146
+ let didThrow = false;
147
+ try {
148
+ await block.tryFrag._execute(shared, params, void 0);
149
+ } catch (tryErr) {
150
+ if (block.catchFrag) {
151
+ shared.__tryError = tryErr instanceof FlowError ? tryErr.cause : tryErr;
152
+ try {
153
+ await block.catchFrag._execute(shared, params, void 0);
154
+ } catch (catchErr) {
155
+ thrownError = catchErr;
156
+ didThrow = true;
157
+ } finally {
158
+ delete shared.__tryError;
159
+ }
160
+ } else {
161
+ thrownError = tryErr;
162
+ didThrow = true;
163
+ }
164
+ }
165
+ if (block.finallyFrag) {
166
+ await block.finallyFrag._execute(shared, params, void 0);
167
+ }
168
+ if (didThrow) throw thrownError;
169
+ });
170
+ return this;
171
+ },
172
+ catch(catchFrag) {
173
+ const block = this[PENDING_KEY];
174
+ if (!block) {
175
+ throw new Error(
176
+ "withTryCatch: .catch() must be called immediately after .try()"
177
+ );
178
+ }
179
+ block.catchFrag = catchFrag;
180
+ return this;
181
+ },
182
+ finally(finallyFrag) {
183
+ const block = this[PENDING_KEY];
184
+ if (!block) {
185
+ throw new Error(
186
+ "withTryCatch: .finally() must be called after .try() or .try().catch()"
187
+ );
188
+ }
189
+ block.finallyFrag = finallyFrag;
190
+ delete this[PENDING_KEY];
191
+ return this;
192
+ }
193
+ };
119
194
  export {
120
195
  withCircuitBreaker,
121
196
  withCycles,
122
197
  withFallback,
123
- withTimeout
198
+ withTimeout,
199
+ withTryCatch
124
200
  };
@@ -1,4 +1,4 @@
1
- import { c as FlowHooks, b as FlowneerPlugin } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { c as FlowHooks, a as FlowneerPlugin } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  interface Span {
4
4
  traceId: string;
@@ -1,4 +1,4 @@
1
- import { b as FlowneerPlugin } from '../../FlowBuilder-DssSGM7v.js';
1
+ import { a as FlowneerPlugin } from '../../FlowBuilder-C3B91Fzo.js';
2
2
 
3
3
  /** Describes a single parameter for a tool. */
4
4
  interface ToolParam {
@@ -1,5 +1,5 @@
1
- import { F as FlowBuilder } from '../FlowBuilder-DssSGM7v.js';
2
- export { c as FlowHooks, b as FlowneerPlugin, N as NodeFn, a as NodeOptions, d as NumberOrFn, R as RunOptions, S as StepMeta, e as StreamEvent, V as Validator } from '../FlowBuilder-DssSGM7v.js';
1
+ import { F as FlowBuilder } from '../FlowBuilder-C3B91Fzo.js';
2
+ export { A as AnchorStep, B as BatchStep, d as BranchStep, c as FlowHooks, a as FlowneerPlugin, e as FnStep, L as LoopStep, N as NodeFn, b as NodeOptions, f as NumberOrFn, P as ParallelStep, R as RunOptions, g as Step, S as StepMeta, h as StreamEvent, V as Validator } from '../FlowBuilder-C3B91Fzo.js';
3
3
 
4
4
  /**
5
5
  * A reusable, composable partial flow — the Flowneer equivalent of a Zod
package/dist/src/index.js CHANGED
@@ -453,6 +453,8 @@ var Fragment = class extends FlowBuilder {
453
453
  );
454
454
  }
455
455
  /** @internal Fragments cannot be streamed — embed them via `.add()`. */
456
+ // v8 ignore next 5 — async generator has an implicit "resume after yield"
457
+ // branch that is unreachable here because we always throw before yielding.
456
458
  async *stream(_shared, _params) {
457
459
  throw new Error(
458
460
  "Fragment cannot be streamed directly \u2014 use .add() to embed it in a FlowBuilder"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowneer",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Zero-dependency fluent flow builder for AI agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,7 +47,8 @@
47
47
  "dotenv": "17.3.1",
48
48
  "openai": "6.22.0",
49
49
  "tsup": "^8.5.1",
50
- "vitepress": "^2.0.0-alpha.16"
50
+ "vitepress": "^2.0.0-alpha.16",
51
+ "vitepress-plugin-llms": "^1.11.0"
51
52
  },
52
53
  "peerDependencies": {
53
54
  "typescript": "^5"