@noetic-tools/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +38 -0
  3. package/dist/adapters/in-memory-fs-adapter.d.ts +3 -0
  4. package/dist/adapters/in-memory-fs-adapter.js +245 -0
  5. package/dist/adapters/in-memory-shell-adapter.d.ts +9 -0
  6. package/dist/adapters/in-memory-shell-adapter.js +20 -0
  7. package/dist/adapters/in-memory-subprocess-adapter.d.ts +5 -0
  8. package/dist/adapters/in-memory-subprocess-adapter.js +126 -0
  9. package/dist/adapters/local-fs-adapter.d.ts +3 -0
  10. package/dist/adapters/local-fs-adapter.js +43 -0
  11. package/dist/adapters/local-shell-adapter.d.ts +51 -0
  12. package/dist/adapters/local-shell-adapter.js +167 -0
  13. package/dist/adapters/local-subprocess-adapter.d.ts +16 -0
  14. package/dist/adapters/local-subprocess-adapter.js +187 -0
  15. package/dist/adapters/openrouter.d.ts +57 -0
  16. package/dist/adapters/openrouter.js +248 -0
  17. package/dist/builders/channel-builder.d.ts +36 -0
  18. package/dist/builders/channel-builder.js +15 -0
  19. package/dist/builders/control-flow-builders.d.ts +79 -0
  20. package/dist/builders/control-flow-builders.js +53 -0
  21. package/dist/builders/every.d.ts +45 -0
  22. package/dist/builders/every.js +60 -0
  23. package/dist/builders/layer-provides-builders.d.ts +32 -0
  24. package/dist/builders/layer-provides-builders.js +33 -0
  25. package/dist/builders/loop-builder.d.ts +23 -0
  26. package/dist/builders/loop-builder.js +46 -0
  27. package/dist/builders/memory-builder.d.ts +11 -0
  28. package/dist/builders/memory-builder.js +15 -0
  29. package/dist/builders/provide-builder.d.ts +20 -0
  30. package/dist/builders/provide-builder.js +34 -0
  31. package/dist/builders/spawn-builder.d.ts +20 -0
  32. package/dist/builders/spawn-builder.js +33 -0
  33. package/dist/builders/step-builders.d.ts +62 -0
  34. package/dist/builders/step-builders.js +99 -0
  35. package/dist/builders/tool-builder.d.ts +52 -0
  36. package/dist/builders/tool-builder.js +59 -0
  37. package/dist/conditions/conditions.d.ts +114 -0
  38. package/dist/conditions/conditions.js +271 -0
  39. package/dist/conditions/cosine-similarity.d.ts +11 -0
  40. package/dist/conditions/cosine-similarity.js +28 -0
  41. package/dist/conditions/index.d.ts +3 -0
  42. package/dist/conditions/index.js +2 -0
  43. package/dist/errors/noetic-config-error.d.ts +41 -0
  44. package/dist/errors/noetic-config-error.js +44 -0
  45. package/dist/errors/noetic-error.d.ts +18 -0
  46. package/dist/errors/noetic-error.js +52 -0
  47. package/dist/index.d.ts +152 -0
  48. package/dist/index.js +112 -0
  49. package/dist/internal-test.d.ts +11 -0
  50. package/dist/internal-test.js +11 -0
  51. package/dist/interpreter/clone-guard.d.ts +4 -0
  52. package/dist/interpreter/clone-guard.js +18 -0
  53. package/dist/interpreter/collect-tools.d.ts +12 -0
  54. package/dist/interpreter/collect-tools.js +70 -0
  55. package/dist/interpreter/execute-branch.d.ts +3 -0
  56. package/dist/interpreter/execute-branch.js +10 -0
  57. package/dist/interpreter/execute-every.d.ts +15 -0
  58. package/dist/interpreter/execute-every.js +143 -0
  59. package/dist/interpreter/execute-fork.d.ts +3 -0
  60. package/dist/interpreter/execute-fork.js +215 -0
  61. package/dist/interpreter/execute-llm.d.ts +4 -0
  62. package/dist/interpreter/execute-llm.js +217 -0
  63. package/dist/interpreter/execute-loop.d.ts +3 -0
  64. package/dist/interpreter/execute-loop.js +171 -0
  65. package/dist/interpreter/execute-provide.d.ts +10 -0
  66. package/dist/interpreter/execute-provide.js +64 -0
  67. package/dist/interpreter/execute-run.d.ts +3 -0
  68. package/dist/interpreter/execute-run.js +39 -0
  69. package/dist/interpreter/execute-spawn.d.ts +11 -0
  70. package/dist/interpreter/execute-spawn.js +113 -0
  71. package/dist/interpreter/execute-tool.d.ts +5 -0
  72. package/dist/interpreter/execute-tool.js +81 -0
  73. package/dist/interpreter/execute.d.ts +14 -0
  74. package/dist/interpreter/execute.js +119 -0
  75. package/dist/interpreter/framework-cast.d.ts +10 -0
  76. package/dist/interpreter/framework-cast.js +16 -0
  77. package/dist/interpreter/message-helpers.d.ts +9 -0
  78. package/dist/interpreter/message-helpers.js +42 -0
  79. package/dist/interpreter/typeguards.d.ts +25 -0
  80. package/dist/interpreter/typeguards.js +50 -0
  81. package/dist/memory/budget.d.ts +23 -0
  82. package/dist/memory/budget.js +117 -0
  83. package/dist/memory/function-call-utils.d.ts +6 -0
  84. package/dist/memory/function-call-utils.js +25 -0
  85. package/dist/memory/layer-api.d.ts +14 -0
  86. package/dist/memory/layer-api.js +91 -0
  87. package/dist/memory/layer-lifecycle.d.ts +160 -0
  88. package/dist/memory/layer-lifecycle.js +552 -0
  89. package/dist/memory/layer-usage.d.ts +15 -0
  90. package/dist/memory/layer-usage.js +48 -0
  91. package/dist/memory/layers/durable-task-state.d.ts +79 -0
  92. package/dist/memory/layers/durable-task-state.js +118 -0
  93. package/dist/memory/layers/file-reference.d.ts +55 -0
  94. package/dist/memory/layers/file-reference.js +538 -0
  95. package/dist/memory/layers/history-window.d.ts +29 -0
  96. package/dist/memory/layers/history-window.js +114 -0
  97. package/dist/memory/layers/observational-memory.d.ts +53 -0
  98. package/dist/memory/layers/observational-memory.js +110 -0
  99. package/dist/memory/layers/plan.d.ts +55 -0
  100. package/dist/memory/layers/plan.js +369 -0
  101. package/dist/memory/layers/static-content.d.ts +27 -0
  102. package/dist/memory/layers/static-content.js +35 -0
  103. package/dist/memory/layers/steering.d.ts +51 -0
  104. package/dist/memory/layers/steering.js +258 -0
  105. package/dist/memory/layers/tool-memory-layer.d.ts +16 -0
  106. package/dist/memory/layers/tool-memory-layer.js +43 -0
  107. package/dist/memory/layers/working-memory.d.ts +44 -0
  108. package/dist/memory/layers/working-memory.js +92 -0
  109. package/dist/memory/projector.d.ts +10 -0
  110. package/dist/memory/projector.js +19 -0
  111. package/dist/memory/scope.d.ts +3 -0
  112. package/dist/memory/scope.js +31 -0
  113. package/dist/memory/strip-unresolved.d.ts +13 -0
  114. package/dist/memory/strip-unresolved.js +35 -0
  115. package/dist/observability/genai-attributes.d.ts +11 -0
  116. package/dist/observability/genai-attributes.js +12 -0
  117. package/dist/observability/span-impl.d.ts +20 -0
  118. package/dist/observability/span-impl.js +33 -0
  119. package/dist/observability/trace-exporter.d.ts +15 -0
  120. package/dist/observability/trace-exporter.js +26 -0
  121. package/dist/patterns/flow.d.ts +52 -0
  122. package/dist/patterns/flow.js +103 -0
  123. package/dist/patterns/interview.d.ts +53 -0
  124. package/dist/patterns/interview.js +113 -0
  125. package/dist/patterns/plans.d.ts +45 -0
  126. package/dist/patterns/plans.js +125 -0
  127. package/dist/patterns/ralph-wiggum.d.ts +20 -0
  128. package/dist/patterns/ralph-wiggum.js +37 -0
  129. package/dist/patterns/react.d.ts +18 -0
  130. package/dist/patterns/react.js +39 -0
  131. package/dist/portable.d.ts +26 -0
  132. package/dist/portable.js +34 -0
  133. package/dist/runtime/agent-harness.d.ts +206 -0
  134. package/dist/runtime/agent-harness.js +1170 -0
  135. package/dist/runtime/broadcaster-utils.d.ts +30 -0
  136. package/dist/runtime/broadcaster-utils.js +53 -0
  137. package/dist/runtime/channel-store.d.ts +18 -0
  138. package/dist/runtime/channel-store.js +187 -0
  139. package/dist/runtime/context-impl.d.ts +80 -0
  140. package/dist/runtime/context-impl.js +140 -0
  141. package/dist/runtime/cwd-helpers.d.ts +42 -0
  142. package/dist/runtime/cwd-helpers.js +68 -0
  143. package/dist/runtime/detached-handle.d.ts +14 -0
  144. package/dist/runtime/detached-handle.js +56 -0
  145. package/dist/runtime/event-broadcaster.d.ts +61 -0
  146. package/dist/runtime/event-broadcaster.js +187 -0
  147. package/dist/runtime/exec-context-factory.d.ts +25 -0
  148. package/dist/runtime/exec-context-factory.js +30 -0
  149. package/dist/runtime/in-memory-storage.d.ts +9 -0
  150. package/dist/runtime/in-memory-storage.js +33 -0
  151. package/dist/runtime/item-log-impl.d.ts +11 -0
  152. package/dist/runtime/item-log-impl.js +25 -0
  153. package/dist/runtime/message-queue.d.ts +30 -0
  154. package/dist/runtime/message-queue.js +46 -0
  155. package/dist/runtime/session-runner.d.ts +77 -0
  156. package/dist/runtime/session-runner.js +253 -0
  157. package/dist/runtime/session-streams.d.ts +11 -0
  158. package/dist/runtime/session-streams.js +204 -0
  159. package/dist/runtime/tool-memory.d.ts +6 -0
  160. package/dist/runtime/tool-memory.js +22 -0
  161. package/dist/schemas/item.d.ts +35 -0
  162. package/dist/schemas/item.js +155 -0
  163. package/dist/types/channel.d.ts +29 -0
  164. package/dist/types/channel.js +1 -0
  165. package/dist/types/common.d.ts +111 -0
  166. package/dist/types/common.js +1 -0
  167. package/dist/types/context.d.ts +93 -0
  168. package/dist/types/context.js +1 -0
  169. package/dist/types/detached.d.ts +17 -0
  170. package/dist/types/detached.js +7 -0
  171. package/dist/types/embed.d.ts +5 -0
  172. package/dist/types/embed.js +1 -0
  173. package/dist/types/error.d.ts +51 -0
  174. package/dist/types/error.js +1 -0
  175. package/dist/types/fs-adapter.d.ts +51 -0
  176. package/dist/types/fs-adapter.js +3 -0
  177. package/dist/types/harness-result.d.ts +43 -0
  178. package/dist/types/harness-result.js +2 -0
  179. package/dist/types/items.d.ts +115 -0
  180. package/dist/types/items.js +2 -0
  181. package/dist/types/memory.d.ts +336 -0
  182. package/dist/types/memory.js +13 -0
  183. package/dist/types/mutable-context.d.ts +13 -0
  184. package/dist/types/mutable-context.js +1 -0
  185. package/dist/types/observability.d.ts +30 -0
  186. package/dist/types/observability.js +1 -0
  187. package/dist/types/runtime.d.ts +255 -0
  188. package/dist/types/runtime.js +1 -0
  189. package/dist/types/shell-adapter.d.ts +34 -0
  190. package/dist/types/shell-adapter.js +10 -0
  191. package/dist/types/steering.d.ts +89 -0
  192. package/dist/types/steering.js +14 -0
  193. package/dist/types/step.d.ts +206 -0
  194. package/dist/types/step.js +1 -0
  195. package/dist/types/subprocess-adapter.d.ts +41 -0
  196. package/dist/types/subprocess-adapter.js +1 -0
  197. package/dist/types/tool-context.d.ts +34 -0
  198. package/dist/types/tool-context.js +1 -0
  199. package/dist/unstable.d.ts +28 -0
  200. package/dist/unstable.js +24 -0
  201. package/dist/until/combinators.d.ts +17 -0
  202. package/dist/until/combinators.js +49 -0
  203. package/dist/until/never.d.ts +9 -0
  204. package/dist/until/never.js +12 -0
  205. package/dist/until/predicates.d.ts +43 -0
  206. package/dist/until/predicates.js +126 -0
  207. package/package.json +81 -0
@@ -0,0 +1,33 @@
1
+ export class SpanImpl {
2
+ traceId;
3
+ spanId;
4
+ parentSpanId;
5
+ name;
6
+ startTime;
7
+ endTime;
8
+ attributes = new Map();
9
+ events = [];
10
+ constructor(name, parent, traceId) {
11
+ this.name = name;
12
+ this.traceId = traceId ?? parent?.traceId ?? crypto.randomUUID();
13
+ this.spanId = crypto.randomUUID();
14
+ this.parentSpanId = parent?.spanId ?? null;
15
+ this.startTime = Date.now();
16
+ }
17
+ setAttribute(key, value) {
18
+ this.attributes.set(key, value);
19
+ }
20
+ addEvent(name, attributes) {
21
+ this.events.push({
22
+ name,
23
+ timestamp: Date.now(),
24
+ attributes,
25
+ });
26
+ }
27
+ end() {
28
+ this.endTime = Date.now();
29
+ }
30
+ get duration() {
31
+ return (this.endTime ?? Date.now()) - this.startTime;
32
+ }
33
+ }
@@ -0,0 +1,15 @@
1
+ import type { Span, TraceExporter } from '../types/observability';
2
+ import { SpanImpl } from './span-impl';
3
+ /** @public No-op trace exporter that silently discards all spans. */
4
+ export declare class NoopExporter implements TraceExporter {
5
+ export(_spans: Span[]): Promise<void>;
6
+ }
7
+ /** @public Collects SpanImpl instances for test assertions and diagnostics. Non-SpanImpl spans are filtered out. */
8
+ export declare class InMemoryExporter implements TraceExporter {
9
+ readonly spans: SpanImpl[];
10
+ export(spans: Span[]): Promise<void>;
11
+ clear(): void;
12
+ getSpansByName(name: string): SpanImpl[];
13
+ getChildSpans(parentSpanId: string): SpanImpl[];
14
+ getTraceTree(traceId: string): SpanImpl[];
15
+ }
@@ -0,0 +1,26 @@
1
+ import { SpanImpl } from './span-impl';
2
+ /** @public No-op trace exporter that silently discards all spans. */
3
+ export class NoopExporter {
4
+ async export(_spans) {
5
+ // No-op
6
+ }
7
+ }
8
+ /** @public Collects SpanImpl instances for test assertions and diagnostics. Non-SpanImpl spans are filtered out. */
9
+ export class InMemoryExporter {
10
+ spans = [];
11
+ async export(spans) {
12
+ this.spans.push(...spans.filter((s) => s instanceof SpanImpl));
13
+ }
14
+ clear() {
15
+ this.spans.length = 0;
16
+ }
17
+ getSpansByName(name) {
18
+ return this.spans.filter((s) => s.name === name);
19
+ }
20
+ getChildSpans(parentSpanId) {
21
+ return this.spans.filter((s) => s.parentSpanId === parentSpanId);
22
+ }
23
+ getTraceTree(traceId) {
24
+ return this.spans.filter((s) => s.traceId === traceId);
25
+ }
26
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Zod schema for plan-mode flow JSON.
3
+ *
4
+ * A "flow" is a JSON-serialisable subset of noetic's `Step` shape that the model
5
+ * may emit during plan mode. The runtime expands each node into a concrete `Step`
6
+ * via the existing builders (`step.llm`, `fork`, `spawn`) at execute time.
7
+ *
8
+ * Tool references inside `llm` nodes are name strings — they get resolved against
9
+ * the harness's live tool registry when the flow runs.
10
+ */
11
+ import { z } from 'zod';
12
+ interface NodeBase {
13
+ id: string;
14
+ subPlanRef?: string;
15
+ }
16
+ export interface LlmFlowNode extends NodeBase {
17
+ kind: 'llm';
18
+ model?: string;
19
+ instructions: string;
20
+ tools?: string[];
21
+ }
22
+ export interface SubagentFlowNode extends NodeBase {
23
+ kind: 'subagent';
24
+ preset: string;
25
+ prompt: string;
26
+ }
27
+ export interface ForkFlowNode extends NodeBase {
28
+ kind: 'fork';
29
+ mode: 'all' | 'race' | 'settle';
30
+ paths: FlowNode[];
31
+ }
32
+ export interface SpawnFlowNode extends NodeBase {
33
+ kind: 'spawn';
34
+ child: FlowNode;
35
+ }
36
+ export interface SequenceFlowNode extends NodeBase {
37
+ kind: 'sequence';
38
+ steps: FlowNode[];
39
+ }
40
+ export type FlowNode = LlmFlowNode | SubagentFlowNode | ForkFlowNode | SpawnFlowNode | SequenceFlowNode;
41
+ /** Top-level flow document — a single root node. */
42
+ export declare const FlowSchema: z.ZodType<FlowNode, unknown, z.core.$ZodTypeInternals<FlowNode, unknown>>;
43
+ /** Validates a candidate flow document. Throws `ZodError` on invalid input. */
44
+ export declare function validateFlow(input: unknown): FlowNode;
45
+ /** Walks a flow tree depth-first, yielding each node. */
46
+ export declare function walkFlow(root: FlowNode): Iterable<FlowNode>;
47
+ /**
48
+ * Returns the maximum depth of a flow tree. A leaf (llm/subagent) is depth 0.
49
+ * Structural nodes (sequence/fork/spawn) contribute +1 over their deepest child.
50
+ */
51
+ export declare function flowDepth(node: FlowNode): number;
52
+ export {};
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Zod schema for plan-mode flow JSON.
3
+ *
4
+ * A "flow" is a JSON-serialisable subset of noetic's `Step` shape that the model
5
+ * may emit during plan mode. The runtime expands each node into a concrete `Step`
6
+ * via the existing builders (`step.llm`, `fork`, `spawn`) at execute time.
7
+ *
8
+ * Tool references inside `llm` nodes are name strings — they get resolved against
9
+ * the harness's live tool registry when the flow runs.
10
+ */
11
+ import { z } from 'zod';
12
+ //#endregion
13
+ //#region Schema
14
+ const FlowNodeRef = z.lazy(() => FlowNodeSchema);
15
+ const SHARED_FIELDS = {
16
+ id: z.string().min(1),
17
+ subPlanRef: z.string().optional(),
18
+ };
19
+ const LlmNodeSchema = z.object({
20
+ kind: z.literal('llm'),
21
+ ...SHARED_FIELDS,
22
+ model: z.string().optional(),
23
+ instructions: z.string(),
24
+ tools: z.array(z.string()).optional(),
25
+ });
26
+ const SubagentNodeSchema = z.object({
27
+ kind: z.literal('subagent'),
28
+ ...SHARED_FIELDS,
29
+ preset: z.string().min(1),
30
+ prompt: z.string(),
31
+ });
32
+ const ForkNodeSchema = z.object({
33
+ kind: z.literal('fork'),
34
+ ...SHARED_FIELDS,
35
+ mode: z.enum([
36
+ 'all',
37
+ 'race',
38
+ 'settle',
39
+ ]),
40
+ paths: z.array(FlowNodeRef).min(1),
41
+ });
42
+ const SpawnNodeSchema = z.object({
43
+ kind: z.literal('spawn'),
44
+ ...SHARED_FIELDS,
45
+ child: FlowNodeRef,
46
+ });
47
+ const SequenceNodeSchema = z.object({
48
+ kind: z.literal('sequence'),
49
+ ...SHARED_FIELDS,
50
+ steps: z.array(FlowNodeRef).min(1),
51
+ });
52
+ const FlowNodeSchema = z.discriminatedUnion('kind', [
53
+ LlmNodeSchema,
54
+ SubagentNodeSchema,
55
+ ForkNodeSchema,
56
+ SpawnNodeSchema,
57
+ SequenceNodeSchema,
58
+ ]);
59
+ /** Top-level flow document — a single root node. */
60
+ export const FlowSchema = FlowNodeSchema;
61
+ //#endregion
62
+ //#region Public API
63
+ /** Validates a candidate flow document. Throws `ZodError` on invalid input. */
64
+ export function validateFlow(input) {
65
+ return FlowSchema.parse(input);
66
+ }
67
+ /** Walks a flow tree depth-first, yielding each node. */
68
+ export function* walkFlow(root) {
69
+ yield root;
70
+ if (root.kind === 'fork') {
71
+ for (const path of root.paths) {
72
+ yield* walkFlow(path);
73
+ }
74
+ return;
75
+ }
76
+ if (root.kind === 'sequence') {
77
+ for (const step of root.steps) {
78
+ yield* walkFlow(step);
79
+ }
80
+ return;
81
+ }
82
+ if (root.kind === 'spawn') {
83
+ yield* walkFlow(root.child);
84
+ return;
85
+ }
86
+ }
87
+ /**
88
+ * Returns the maximum depth of a flow tree. A leaf (llm/subagent) is depth 0.
89
+ * Structural nodes (sequence/fork/spawn) contribute +1 over their deepest child.
90
+ */
91
+ export function flowDepth(node) {
92
+ if (node.kind === 'sequence') {
93
+ return 1 + Math.max(0, ...node.steps.map(flowDepth));
94
+ }
95
+ if (node.kind === 'fork') {
96
+ return 1 + Math.max(0, ...node.paths.map(flowDepth));
97
+ }
98
+ if (node.kind === 'spawn') {
99
+ return 1 + flowDepth(node.child);
100
+ }
101
+ return 0;
102
+ }
103
+ //#endregion
@@ -0,0 +1,53 @@
1
+ import type { ZodType } from 'zod';
2
+ import type { ContextMemory } from '../types/memory';
3
+ import type { Step } from '../types/step';
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<ContextMemory, string, InterviewResult<Q, C>>;
@@ -0,0 +1,113 @@
1
+ import { z } from 'zod';
2
+ import { loop } from '../builders/loop-builder';
3
+ import { step } from '../builders/step-builders';
4
+ import { frameworkCast } from '../interpreter/framework-cast';
5
+ import { any } from '../until/combinators';
6
+ import { until } from '../until/predicates';
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
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod';
2
+ import type { Context } from '../types/context';
3
+ import type { ContextMemory } from '../types/memory';
4
+ import type { ExecuteStepFn, Step } from '../types/step';
5
+ export interface PlanNode {
6
+ id: string;
7
+ description: string;
8
+ assignee: string;
9
+ execution: 'sequential' | 'parallel';
10
+ children?: PlanNode[];
11
+ }
12
+ /** @public Zod schema for recursive `PlanNode` validation. */
13
+ export declare const PlanNodeSchema: z.ZodType<PlanNode>;
14
+ export interface PlanConstraints {
15
+ toolAllowlist?: Record<string, string[]>;
16
+ maxStepsPerNode?: number;
17
+ requireApproval?: string[];
18
+ validate?: (taskId: string, input: unknown, ctx: Context) => Promise<boolean>;
19
+ }
20
+ /**
21
+ * Compiles a `PlanNode` tree into an executable step graph using the provided agent factories.
22
+ *
23
+ * @public
24
+ * @param plan - Root plan node describing the task tree.
25
+ * @param agents - Map of agent names to factory functions producing steps.
26
+ * @param constraints - Optional constraints for tool allowlists and approval.
27
+ * @param executeStep - Optional step executor for non-run step kinds.
28
+ * @returns A compiled `Step` ready for execution.
29
+ */
30
+ export declare function compilePlan<O>(plan: PlanNode, agents: Record<string, (prompt: string) => Step<ContextMemory, string, unknown>>, constraints?: PlanConstraints, executeStep?: ExecuteStepFn): Step<ContextMemory, string, O>;
31
+ /**
32
+ * Creates a step that dynamically generates a plan via a planner step and executes it,
33
+ * retrying with error feedback up to `maxRevisions` times on failure.
34
+ *
35
+ * @public
36
+ * @param opts - Planner step, agent factories, optional constraints, max revision count, and step executor.
37
+ * @returns A `Step` that adaptively plans and executes.
38
+ */
39
+ export declare function adaptivePlan<O>(opts: {
40
+ planner: Step<ContextMemory, string, PlanNode>;
41
+ agents: Record<string, (prompt: string) => Step<ContextMemory, string, unknown>>;
42
+ constraints?: PlanConstraints;
43
+ maxRevisions: number;
44
+ executeStep?: ExecuteStepFn;
45
+ }): Step<ContextMemory, string, O>;
@@ -0,0 +1,125 @@
1
+ import { z } from 'zod';
2
+ import { fork } from '../builders/control-flow-builders';
3
+ import { frameworkCast } from '../interpreter/framework-cast';
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) => 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 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
+ }
@@ -0,0 +1,20 @@
1
+ import type { Tool } from '../types/common';
2
+ import type { ContextMemory } from '../types/memory';
3
+ import type { StepLoop } from '../types/step';
4
+ import type { VerifyFn } from '../until/predicates';
5
+ /**
6
+ * Creates a retry-with-feedback loop wrapping a ReAct agent. Each iteration spawns an inner agent
7
+ * and retries with verification feedback until the verifier passes or max iterations is reached.
8
+ *
9
+ * @public
10
+ * @param opts - Model, tools, instructions, verify function, and optional iteration/step limits.
11
+ * @returns A `StepLoop` that retries the inner agent with feedback.
12
+ */
13
+ export declare function ralphWiggum(opts: {
14
+ model: string;
15
+ instructions: string;
16
+ tools: Tool[];
17
+ verify: VerifyFn;
18
+ maxIterations?: number;
19
+ innerMaxSteps?: number;
20
+ }): StepLoop<ContextMemory, string, string>;
@@ -0,0 +1,37 @@
1
+ import { loop } from '../builders/loop-builder';
2
+ import { spawn } from '../builders/spawn-builder';
3
+ import { any } from '../until/combinators';
4
+ import { until } from '../until/predicates';
5
+ import { react } from './react';
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
+ }
@@ -0,0 +1,18 @@
1
+ import type { Tool } from '../types/common';
2
+ import type { ContextMemory, MemoryConfig, MemoryLayer } from '../types/memory';
3
+ import type { StepLoop, StepSpawn } from '../types/step';
4
+ /**
5
+ * Creates a ReAct (Reason + Act) agent loop: an LLM step with tools iterated until no tool calls or limits are hit.
6
+ *
7
+ * @public
8
+ * @param opts - Model, tools, optional instructions, step/cost limits, and memory layers.
9
+ * @returns A `StepLoop` (no memory) or `StepSpawn` wrapping a loop (with memory).
10
+ */
11
+ export declare function react(opts: {
12
+ model: string;
13
+ instructions?: string;
14
+ tools: Tool[];
15
+ maxSteps?: number;
16
+ maxCost?: number;
17
+ memory?: MemoryConfig | MemoryLayer[];
18
+ }): StepLoop<ContextMemory, string, string> | StepSpawn<ContextMemory, string, string>;