@noetic-tools/core 5.2.0 → 5.4.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 (92) hide show
  1. package/README.md +8 -6
  2. package/dist/adapters/in-memory-subprocess/step-completion.js +1 -1
  3. package/dist/adapters/openrouter.d.ts +20 -2
  4. package/dist/adapters/openrouter.js +102 -6
  5. package/dist/builders/control-flow-builders.d.ts +13 -13
  6. package/dist/builders/control-flow-builders.js +11 -11
  7. package/dist/{patterns → builders}/dynamic-workflow.d.ts +1 -1
  8. package/dist/{patterns → builders}/dynamic-workflow.js +18 -12
  9. package/dist/builders/every.d.ts +14 -14
  10. package/dist/builders/every.js +16 -16
  11. package/dist/builders/layer-provides-builders.d.ts +1 -1
  12. package/dist/builders/layer-provides-builders.js +1 -1
  13. package/dist/builders/provide-builder.d.ts +5 -12
  14. package/dist/builders/provide-builder.js +9 -10
  15. package/dist/builders/spawn-builder.d.ts +0 -2
  16. package/dist/builders/spawn-builder.js +2 -3
  17. package/dist/builders/step-builders.d.ts +66 -55
  18. package/dist/builders/step-builders.js +118 -111
  19. package/dist/builders/tool-builder.d.ts +18 -22
  20. package/dist/builders/tool-builder.js +6 -4
  21. package/dist/builders/workflow-hydrator.d.ts +3 -3
  22. package/dist/builders/workflow-hydrator.js +59 -63
  23. package/dist/builders/workflow-step.d.ts +13 -25
  24. package/dist/builders/workflow-step.js +13 -18
  25. package/dist/conditions/conditions.d.ts +2 -2
  26. package/dist/conditions/conditions.js +1 -1
  27. package/dist/harness/agent-harness.d.ts +58 -40
  28. package/dist/harness/agent-harness.js +125 -51
  29. package/dist/harness/deps/context.d.ts +1 -1
  30. package/dist/harness/deps/context.js +1 -1
  31. package/dist/harness/deps/interpreter.d.ts +1 -0
  32. package/dist/harness/deps/interpreter.js +1 -0
  33. package/dist/harness/deps/types.d.ts +2 -2
  34. package/dist/harness/model-call.d.ts +1 -1
  35. package/dist/harness/model-call.js +45 -23
  36. package/dist/index.d.ts +23 -35
  37. package/dist/index.js +10 -26
  38. package/dist/interpreter/action-deps.d.ts +2 -2
  39. package/dist/interpreter/action-deps.js +1 -1
  40. package/dist/interpreter/action-types.d.ts +1 -1
  41. package/dist/interpreter/collect-tools.js +7 -7
  42. package/dist/interpreter/context-assembly.d.ts +50 -0
  43. package/dist/interpreter/context-assembly.js +547 -0
  44. package/dist/interpreter/execute-action.d.ts +5 -5
  45. package/dist/interpreter/execute-action.js +337 -208
  46. package/dist/interpreter/execute-control.d.ts +9 -9
  47. package/dist/interpreter/execute-control.js +27 -27
  48. package/dist/interpreter/execute-sub-harness.d.ts +1 -1
  49. package/dist/interpreter/execute-sub-harness.js +7 -26
  50. package/dist/interpreter/execute.d.ts +1 -1
  51. package/dist/interpreter/execute.js +26 -25
  52. package/dist/interpreter/step-dispatcher.d.ts +1 -1
  53. package/dist/interpreter/step-dispatcher.js +3 -3
  54. package/dist/interpreter/structured-output.d.ts +14 -0
  55. package/dist/interpreter/structured-output.js +40 -0
  56. package/dist/interpreter/typeguards.d.ts +1 -1
  57. package/dist/interpreter/typeguards.js +1 -1
  58. package/dist/observability/genai-attributes.d.ts +20 -0
  59. package/dist/observability/genai-attributes.js +20 -0
  60. package/dist/portable/builders.d.ts +3 -3
  61. package/dist/portable/builders.js +6 -5
  62. package/dist/portable/context-layers.d.ts +1 -1
  63. package/dist/portable/context-layers.js +1 -1
  64. package/dist/runtime/broadcaster-utils.d.ts +1 -1
  65. package/dist/runtime/broadcaster-utils.js +1 -1
  66. package/dist/runtime/context-impl.d.ts +8 -10
  67. package/dist/runtime/context-impl.js +8 -12
  68. package/dist/runtime/durable/step-ledger.d.ts +2 -2
  69. package/dist/runtime/durable/step-ledger.js +3 -3
  70. package/dist/runtime/session-runner.d.ts +12 -1
  71. package/dist/runtime/session-runner.js +33 -1
  72. package/dist/runtime/tool-context.js +0 -2
  73. package/dist/schemas/workflow.d.ts +3 -3
  74. package/dist/schemas/workflow.js +2 -2
  75. package/dist/types/checkpoint.d.ts +11 -2
  76. package/dist/types/checkpoint.js +10 -1
  77. package/dist/{runtime → util}/detached-handle.js +1 -1
  78. package/package.json +6 -4
  79. package/schema/noetic-workflow.schema.json +24 -11
  80. package/dist/builders/context-option.d.ts +0 -23
  81. package/dist/builders/context-option.js +0 -22
  82. package/dist/deprecated.d.ts +0 -30
  83. package/dist/deprecated.js +0 -28
  84. package/dist/patterns/interview.d.ts +0 -53
  85. package/dist/patterns/interview.js +0 -113
  86. package/dist/patterns/plans.d.ts +0 -44
  87. package/dist/patterns/plans.js +0 -125
  88. package/dist/patterns/ralph-wiggum.d.ts +0 -19
  89. package/dist/patterns/ralph-wiggum.js +0 -37
  90. package/dist/patterns/react.d.ts +0 -19
  91. package/dist/patterns/react.js +0 -41
  92. /package/dist/{runtime → util}/detached-handle.d.ts +0 -0
@@ -1,28 +0,0 @@
1
- /**
2
- * Deprecated aliases from the "memory layer" era of the framework.
3
- *
4
- * The layer system assembles the model's *context window* — recall, budget
5
- * allocation, history projection, the item-append pipeline — so it is named
6
- * after context, not memory. Every name below still resolves to its
7
- * replacement; nothing here changes behaviour.
8
- *
9
- * Most of these are plain pass-throughs: the alias and its replacement already
10
- * live in `@noetic-tools/types` / `@noetic-tools/context`, and re-exporting
11
- * carries each name's `@deprecated` pointer along with it. Only the renaming
12
- * aliases — where core exports a symbol under a *different* name than its
13
- * source package does — are declared here.
14
- *
15
- * The matching runtime aliases (the `memory:` config key on `provide`/`spawn`/
16
- * `tool`/`react`/`AgentHarness`, and the `ctx.memory` accessor) are handled at
17
- * their call sites — see `resolveContextOption`.
18
- *
19
- * Scheduled for removal in the next major of `@noetic-tools/core`.
20
- */
21
- export { observationalMemory, planMemory, temporalMemory, toolMemoryLayer, workingMemory, } from '@noetic-tools/context';
22
- export {
23
- /**
24
- * @public
25
- * @deprecated Renamed to `context`. Declared here rather than re-exported
26
- * because the builder itself lives in core under its new name.
27
- */
28
- context as memory, } from './builders/context-builder.js';
@@ -1,53 +0,0 @@
1
- import type { ContextData } from '@noetic-tools/context';
2
- import type { Step } from '@noetic-tools/types';
3
- import type { ZodType } from 'zod';
4
- /** @public Answer captured by the host for a single interview question. */
5
- export interface InterviewQuestionAnswer {
6
- questionId: string;
7
- question: string;
8
- answer: string | string[];
9
- notes?: string;
10
- }
11
- /** @public Result of running an `interview()` step. */
12
- export type InterviewResult<Q, C> = {
13
- status: 'complete';
14
- envelope: C;
15
- } | {
16
- status: 'maxQuestions';
17
- lastQuestion?: Q;
18
- };
19
- /** @public Configuration for the `interview()` pattern. */
20
- export interface InterviewOpts<Q, C> {
21
- /** System prompt instructing the model on how to interview the user. */
22
- systemPrompt: string;
23
- /** Model identifier passed to `step.llm`. */
24
- model: string;
25
- /** Zod schema for the `data` payload of a `question` envelope. */
26
- questionSchema: ZodType<Q>;
27
- /** Zod schema for the `data` payload of a `complete` envelope. */
28
- completeSchema: ZodType<C>;
29
- /** Host renders the question (e.g. via AskUserService) and returns the answer. */
30
- askQuestion: (envelope: Q) => Promise<InterviewQuestionAnswer>;
31
- /** Called once when the model emits the complete envelope. */
32
- onComplete: (envelope: C) => Promise<void>;
33
- /** Hard cap on question/complete turns before the loop exits with `maxQuestions`. Default 8. */
34
- maxQuestions?: number;
35
- /**
36
- * Override how a captured answer becomes the next user message. Default emits a
37
- * compact, machine-readable JSON line that the model can parse to build context.
38
- */
39
- formatAnswer?: (answer: InterviewQuestionAnswer) => string;
40
- }
41
- /**
42
- * Creates a host-callback-driven structured interview pattern. Internally a loop over a single
43
- * body step that delegates to `step.llm` (with `output: discriminatedUnion('type', [...])`) and
44
- * then hands the envelope to the host. `askQuestion` renders questions and returns answers fed
45
- * back as the next user message; `onComplete` fires once when the model emits `complete`.
46
- * Termination via `until.verified(complete) || until.maxSteps(maxQuestions)`.
47
- *
48
- * @public
49
- * @param opts - System prompt, model, question/complete schemas, host callbacks, optional caps.
50
- * @returns A `Step` whose output is `InterviewResult<Q, C>` — either the completion envelope or
51
- * `maxQuestions` when the budget is exhausted before the model emits `complete`.
52
- */
53
- export declare function interview<Q, C>(opts: InterviewOpts<Q, C>): Step<ContextData, string, InterviewResult<Q, C>>;
@@ -1,113 +0,0 @@
1
- import { frameworkCast } from '@noetic-tools/types';
2
- import { z } from 'zod';
3
- import { loop } from '../builders/loop-builder.js';
4
- import { step } from '../builders/step-builders.js';
5
- import { any } from '../until/combinators.js';
6
- import { until } from '../until/predicates.js';
7
- //#endregion
8
- //#region Helpers
9
- const DEFAULT_MAX_QUESTIONS = 8;
10
- function defaultFormatAnswer(answer) {
11
- return JSON.stringify({
12
- questionId: answer.questionId,
13
- question: answer.question,
14
- answer: answer.answer,
15
- notes: answer.notes,
16
- });
17
- }
18
- function isCompleteIterState(value) {
19
- if (typeof value !== 'object' || value === null) {
20
- return false;
21
- }
22
- if (!('status' in value)) {
23
- return false;
24
- }
25
- return value.status === 'complete';
26
- }
27
- function buildEnvelopeSchema(questionSchema, completeSchema) {
28
- const schema = z.discriminatedUnion('type', [
29
- z.object({
30
- type: z.literal('question'),
31
- data: questionSchema,
32
- }),
33
- z.object({
34
- type: z.literal('complete'),
35
- data: completeSchema,
36
- }),
37
- ]);
38
- // ZodDiscriminatedUnion narrows to a non-generic ZodType<Envelope> shape, but
39
- // step.llm's `output` field is typed as ZodType<O> for the generic O.
40
- return frameworkCast(schema);
41
- }
42
- //#endregion
43
- //#region Public API
44
- /**
45
- * Creates a host-callback-driven structured interview pattern. Internally a loop over a single
46
- * body step that delegates to `step.llm` (with `output: discriminatedUnion('type', [...])`) and
47
- * then hands the envelope to the host. `askQuestion` renders questions and returns answers fed
48
- * back as the next user message; `onComplete` fires once when the model emits `complete`.
49
- * Termination via `until.verified(complete) || until.maxSteps(maxQuestions)`.
50
- *
51
- * @public
52
- * @param opts - System prompt, model, question/complete schemas, host callbacks, optional caps.
53
- * @returns A `Step` whose output is `InterviewResult<Q, C>` — either the completion envelope or
54
- * `maxQuestions` when the budget is exhausted before the model emits `complete`.
55
- */
56
- export function interview(opts) {
57
- const maxQuestions = opts.maxQuestions ?? DEFAULT_MAX_QUESTIONS;
58
- const formatAnswer = opts.formatAnswer ?? defaultFormatAnswer;
59
- const envelopeSchema = buildEnvelopeSchema(opts.questionSchema, opts.completeSchema);
60
- const llmStep = step.llm({
61
- id: 'interview-llm',
62
- model: opts.model,
63
- instructions: opts.systemPrompt,
64
- output: envelopeSchema,
65
- });
66
- const turnStep = step.run({
67
- id: 'interview-turn',
68
- execute: async (input, ctx) => {
69
- const envelope = await ctx.harness.run(llmStep, input, ctx);
70
- if (envelope.type === 'complete') {
71
- await opts.onComplete(envelope.data);
72
- return {
73
- status: 'complete',
74
- envelope: envelope.data,
75
- nextInput: '',
76
- };
77
- }
78
- const answer = await opts.askQuestion(envelope.data);
79
- return {
80
- status: 'inProgress',
81
- lastQuestion: envelope.data,
82
- nextInput: formatAnswer(answer),
83
- };
84
- },
85
- });
86
- const loopStep = loop({
87
- id: 'interview-loop',
88
- steps: [
89
- turnStep,
90
- ],
91
- until: any(until.verified(async (out) => ({
92
- pass: isCompleteIterState(out),
93
- })), until.maxSteps(maxQuestions)),
94
- prepareNext: (output) => output.nextInput,
95
- });
96
- return step.run({
97
- id: 'interview',
98
- execute: async (input, ctx) => {
99
- const final = await ctx.harness.run(loopStep, input, ctx);
100
- if (final.status === 'complete') {
101
- return {
102
- status: 'complete',
103
- envelope: final.envelope,
104
- };
105
- }
106
- return {
107
- status: 'maxQuestions',
108
- lastQuestion: final.lastQuestion,
109
- };
110
- },
111
- });
112
- }
113
- //#endregion
@@ -1,44 +0,0 @@
1
- import type { ContextData } from '@noetic-tools/context';
2
- import type { Context, ExecuteStepFn, Step } from '@noetic-tools/types';
3
- import { z } from 'zod';
4
- export interface PlanNode {
5
- id: string;
6
- description: string;
7
- assignee: string;
8
- execution: 'sequential' | 'parallel';
9
- children?: PlanNode[];
10
- }
11
- /** @public Zod schema for recursive `PlanNode` validation. */
12
- export declare const PlanNodeSchema: z.ZodType<PlanNode>;
13
- export interface PlanConstraints {
14
- toolAllowlist?: Record<string, string[]>;
15
- maxStepsPerNode?: number;
16
- requireApproval?: string[];
17
- validate?: (taskId: string, input: unknown, ctx: Context) => Promise<boolean>;
18
- }
19
- /**
20
- * Compiles a `PlanNode` tree into an executable step graph using the provided agent factories.
21
- *
22
- * @public
23
- * @param plan - Root plan node describing the task tree.
24
- * @param agents - Map of agent names to factory functions producing steps.
25
- * @param constraints - Optional constraints for tool allowlists and approval.
26
- * @param executeStep - Optional step executor for non-run step kinds.
27
- * @returns A compiled `Step` ready for execution.
28
- */
29
- export declare function compilePlan<O, A = unknown>(plan: PlanNode, agents: Record<string, (prompt: string) => Step<ContextData, string, A>>, constraints?: PlanConstraints, executeStep?: ExecuteStepFn): Step<ContextData, string, O>;
30
- /**
31
- * Creates a step that dynamically generates a plan via a planner step and executes it,
32
- * retrying with error feedback up to `maxRevisions` times on failure.
33
- *
34
- * @public
35
- * @param opts - Planner step, agent factories, optional constraints, max revision count, and step executor.
36
- * @returns A `Step` that adaptively plans and executes.
37
- */
38
- export declare function adaptivePlan<O, A = unknown>(opts: {
39
- planner: Step<ContextData, string, PlanNode>;
40
- agents: Record<string, (prompt: string) => Step<ContextData, string, A>>;
41
- constraints?: PlanConstraints;
42
- maxRevisions: number;
43
- executeStep?: ExecuteStepFn;
44
- }): Step<ContextData, string, O>;
@@ -1,125 +0,0 @@
1
- import { frameworkCast } from '@noetic-tools/types';
2
- import { z } from 'zod';
3
- import { fork } from '../builders/control-flow-builders.js';
4
- // PlanNode schema — the variable annotation provides the recursive type;
5
- // z.lazy() returns a compatible type that TypeScript checks against it.
6
- /** @public Zod schema for recursive `PlanNode` validation. */
7
- export const PlanNodeSchema = z.lazy(() => z.object({
8
- id: z.string(),
9
- description: z.string(),
10
- assignee: z.string(),
11
- execution: z.enum([
12
- 'sequential',
13
- 'parallel',
14
- ]),
15
- children: z.array(PlanNodeSchema).optional(),
16
- }));
17
- /**
18
- * Compiles a `PlanNode` tree into an executable step graph using the provided agent factories.
19
- *
20
- * @public
21
- * @param plan - Root plan node describing the task tree.
22
- * @param agents - Map of agent names to factory functions producing steps.
23
- * @param constraints - Optional constraints for tool allowlists and approval.
24
- * @param executeStep - Optional step executor for non-run step kinds.
25
- * @returns A compiled `Step` ready for execution.
26
- */
27
- export function compilePlan(plan, agents, constraints, executeStep) {
28
- return frameworkCast(compileNode(plan, {
29
- agents,
30
- constraints,
31
- executeStep,
32
- }));
33
- }
34
- function compileNode(node, opts) {
35
- const agentFactory = opts.agents[node.assignee];
36
- if (!agentFactory) {
37
- throw new Error(`Unknown agent: ${node.assignee}`);
38
- }
39
- // Leaf node - return the step directly from the factory
40
- if (!node.children || node.children.length === 0) {
41
- return agentFactory(node.description);
42
- }
43
- // Has children - compile them
44
- const childSteps = node.children.map((child) => compileNode(child, opts));
45
- if (node.execution === 'parallel') {
46
- return fork({
47
- id: `plan-fork-${node.id}`,
48
- mode: 'all',
49
- paths: () => childSteps,
50
- merge: (results) => frameworkCast(results[results.length - 1]),
51
- });
52
- }
53
- // Sequential - chain steps via a run step that uses executeStep
54
- return {
55
- kind: 'run',
56
- id: `plan-seq-${node.id}`,
57
- execute: async (input, ctx) => {
58
- let currentOutput = input;
59
- for (const child of childSteps) {
60
- const childInput = typeof currentOutput === 'string' ? currentOutput : JSON.stringify(currentOutput);
61
- if (opts.executeStep) {
62
- currentOutput = await opts.executeStep(child, childInput, ctx);
63
- }
64
- else if (child.kind === 'run') {
65
- currentOutput = await child.execute(childInput, ctx);
66
- }
67
- else {
68
- throw new Error(`No executeStep provided and child step kind '${child.kind}' cannot be executed directly`);
69
- }
70
- }
71
- return frameworkCast(currentOutput);
72
- },
73
- };
74
- }
75
- /**
76
- * Creates a step that dynamically generates a plan via a planner step and executes it,
77
- * retrying with error feedback up to `maxRevisions` times on failure.
78
- *
79
- * @public
80
- * @param opts - Planner step, agent factories, optional constraints, max revision count, and step executor.
81
- * @returns A `Step` that adaptively plans and executes.
82
- */
83
- export function adaptivePlan(opts) {
84
- return {
85
- kind: 'run',
86
- id: 'adaptive-plan',
87
- execute: async (input, ctx) => {
88
- let lastError = null;
89
- for (let revision = 0; revision < opts.maxRevisions; revision++) {
90
- // Generate plan
91
- const planInput = lastError
92
- ? `${input}\n\nPrevious plan failed: ${lastError.message}`
93
- : input;
94
- let plan;
95
- if (opts.executeStep) {
96
- plan = PlanNodeSchema.parse(await opts.executeStep(opts.planner, planInput, ctx));
97
- }
98
- else if (opts.planner.kind === 'run') {
99
- plan = PlanNodeSchema.parse(await opts.planner.execute(planInput, ctx));
100
- }
101
- else {
102
- throw new Error('Planner must be a run step when no executeStep provided');
103
- }
104
- // Compile and execute
105
- try {
106
- const compiled = compilePlan(plan, opts.agents, opts.constraints, opts.executeStep);
107
- if (opts.executeStep) {
108
- return frameworkCast(await opts.executeStep(compiled, input, ctx));
109
- }
110
- if (compiled.kind === 'run') {
111
- return frameworkCast(await compiled.execute(input, ctx));
112
- }
113
- throw new Error(`No executeStep provided and compiled plan kind '${compiled.kind}' cannot be executed directly`);
114
- }
115
- catch (e) {
116
- lastError = e instanceof Error ? e : new Error(String(e));
117
- if (revision === opts.maxRevisions - 1) {
118
- throw lastError;
119
- }
120
- }
121
- }
122
- throw lastError;
123
- },
124
- };
125
- }
@@ -1,19 +0,0 @@
1
- import type { ContextData } from '@noetic-tools/context';
2
- import type { StepLoop, Tool } from '@noetic-tools/types';
3
- import type { VerifyFn } from '../until/predicates.js';
4
- /**
5
- * Creates a retry-with-feedback loop wrapping a ReAct agent. Each iteration spawns an inner agent
6
- * and retries with verification feedback until the verifier passes or max iterations is reached.
7
- *
8
- * @public
9
- * @param opts - Model, tools, instructions, verify function, and optional iteration/step limits.
10
- * @returns A `StepLoop` that retries the inner agent with feedback.
11
- */
12
- export declare function ralphWiggum(opts: {
13
- model: string;
14
- instructions: string;
15
- tools: Tool[];
16
- verify: VerifyFn;
17
- maxIterations?: number;
18
- innerMaxSteps?: number;
19
- }): StepLoop<ContextData, string, string>;
@@ -1,37 +0,0 @@
1
- import { loop } from '../builders/loop-builder.js';
2
- import { spawn } from '../builders/spawn-builder.js';
3
- import { any } from '../until/combinators.js';
4
- import { until } from '../until/predicates.js';
5
- import { react } from './react.js';
6
- /**
7
- * Creates a retry-with-feedback loop wrapping a ReAct agent. Each iteration spawns an inner agent
8
- * and retries with verification feedback until the verifier passes or max iterations is reached.
9
- *
10
- * @public
11
- * @param opts - Model, tools, instructions, verify function, and optional iteration/step limits.
12
- * @returns A `StepLoop` that retries the inner agent with feedback.
13
- */
14
- export function ralphWiggum(opts) {
15
- const inner = react({
16
- model: opts.model,
17
- instructions: opts.instructions,
18
- tools: opts.tools,
19
- maxSteps: opts.innerMaxSteps ?? 20,
20
- });
21
- return loop({
22
- id: 'ralph-wiggum-loop',
23
- steps: [
24
- spawn({
25
- id: 'ralph-iteration',
26
- child: inner,
27
- }),
28
- ],
29
- until: any(until.verified(opts.verify), until.maxSteps(opts.maxIterations ?? 50)),
30
- prepareNext: (_output, verdict) => {
31
- if (verdict.feedback) {
32
- return `Previous attempt feedback: ${verdict.feedback}\nContinue working.`;
33
- }
34
- return 'Continue working on the task.';
35
- },
36
- });
37
- }
@@ -1,19 +0,0 @@
1
- import type { ContextConfig, ContextData, ContextLayer } from '@noetic-tools/context';
2
- import type { StepLoop, StepSpawn, Tool } from '@noetic-tools/types';
3
- /**
4
- * Creates a ReAct (Reason + Act) agent loop: an LLM step with tools iterated until no tool calls or limits are hit.
5
- *
6
- * @public
7
- * @param opts - Model, tools, optional instructions, step/cost limits, and context layers.
8
- * @returns A `StepLoop` (no context layers) or `StepSpawn` wrapping a loop (with them).
9
- */
10
- export declare function react(opts: {
11
- model: string;
12
- instructions?: string;
13
- tools: Tool[];
14
- maxSteps?: number;
15
- maxCost?: number;
16
- context?: ContextConfig | ContextLayer[];
17
- /** @deprecated Renamed to `context`. */
18
- memory?: ContextConfig | ContextLayer[];
19
- }): StepLoop<ContextData, string, string> | StepSpawn<ContextData, string, string>;
@@ -1,41 +0,0 @@
1
- import { resolveContextOption } from '../builders/context-option.js';
2
- import { loop } from '../builders/loop-builder.js';
3
- import { spawn } from '../builders/spawn-builder.js';
4
- import { step } from '../builders/step-builders.js';
5
- import { any } from '../until/combinators.js';
6
- import { until } from '../until/predicates.js';
7
- /**
8
- * Creates a ReAct (Reason + Act) agent loop: an LLM step with tools iterated until no tool calls or limits are hit.
9
- *
10
- * @public
11
- * @param opts - Model, tools, optional instructions, step/cost limits, and context layers.
12
- * @returns A `StepLoop` (no context layers) or `StepSpawn` wrapping a loop (with them).
13
- */
14
- export function react(opts) {
15
- const llmStep = step.llm({
16
- id: 'react-step',
17
- model: opts.model,
18
- instructions: opts.instructions,
19
- tools: opts.tools,
20
- });
21
- const loopStep = loop({
22
- id: 'react-loop',
23
- steps: [
24
- llmStep,
25
- ],
26
- until: any(until.noToolCalls(), until.maxSteps(opts.maxSteps ?? 10), ...(opts.maxCost
27
- ? [
28
- until.maxCost(opts.maxCost),
29
- ]
30
- : [])),
31
- });
32
- const layers = resolveContextOption(opts);
33
- if (!layers) {
34
- return loopStep;
35
- }
36
- return spawn({
37
- id: 'react-agent',
38
- child: loopStep,
39
- context: layers,
40
- });
41
- }
File without changes