@noetic-tools/core 0.0.1 → 0.1.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 (202) hide show
  1. package/dist/adapters/in-memory-fs-adapter.js +1 -1
  2. package/dist/adapters/in-memory-subprocess/manifest-persistence.d.ts +8 -0
  3. package/dist/adapters/in-memory-subprocess/manifest-persistence.js +56 -0
  4. package/dist/adapters/in-memory-subprocess/metadata.d.ts +12 -0
  5. package/dist/adapters/in-memory-subprocess/metadata.js +58 -0
  6. package/dist/adapters/in-memory-subprocess/step-completion.d.ts +3 -0
  7. package/dist/adapters/in-memory-subprocess/step-completion.js +86 -0
  8. package/dist/adapters/in-memory-subprocess/types.d.ts +88 -0
  9. package/dist/adapters/in-memory-subprocess/types.js +2 -0
  10. package/dist/adapters/in-memory-subprocess-adapter.d.ts +12 -4
  11. package/dist/adapters/in-memory-subprocess-adapter.js +164 -55
  12. package/dist/adapters/openrouter.d.ts +11 -2
  13. package/dist/adapters/openrouter.js +66 -5
  14. package/dist/builders/control-flow-builders.js +8 -3
  15. package/dist/builders/every.js +4 -1
  16. package/dist/builders/layer-provides-builders.d.ts +1 -32
  17. package/dist/builders/layer-provides-builders.js +1 -33
  18. package/dist/builders/loop-builder.js +4 -1
  19. package/dist/builders/memory-builder.js +1 -1
  20. package/dist/builders/provide-builder.js +4 -1
  21. package/dist/builders/spawn-builder.d.ts +20 -7
  22. package/dist/builders/spawn-builder.js +16 -2
  23. package/dist/builders/step-builders.d.ts +49 -28
  24. package/dist/builders/step-builders.js +30 -11
  25. package/dist/builders/tool-builder.d.ts +5 -1
  26. package/dist/builders/tool-builder.js +2 -0
  27. package/dist/builders/workflow-hydrator.d.ts +38 -0
  28. package/dist/builders/workflow-hydrator.js +336 -0
  29. package/dist/conditions/conditions.js +1 -1
  30. package/dist/errors/noetic-error.js +2 -0
  31. package/dist/{runtime → harness}/agent-harness.d.ts +65 -45
  32. package/dist/harness/agent-harness.js +576 -0
  33. package/dist/harness/deps/adapters.d.ts +4 -0
  34. package/dist/harness/deps/adapters.js +4 -0
  35. package/dist/harness/deps/interpreter.d.ts +5 -0
  36. package/dist/harness/deps/interpreter.js +4 -0
  37. package/dist/harness/deps/memory.d.ts +5 -0
  38. package/dist/harness/deps/memory.js +4 -0
  39. package/dist/harness/deps/runtime.d.ts +10 -0
  40. package/dist/harness/deps/runtime.js +7 -0
  41. package/dist/harness/deps/types.d.ts +17 -0
  42. package/dist/harness/deps/types.js +1 -0
  43. package/dist/harness/model-call.d.ts +50 -0
  44. package/dist/harness/model-call.js +694 -0
  45. package/dist/harness/model-schema.d.ts +19 -0
  46. package/dist/harness/model-schema.js +46 -0
  47. package/dist/harness/step-bootstrap.d.ts +25 -0
  48. package/dist/harness/step-bootstrap.js +170 -0
  49. package/dist/index.d.ts +48 -12
  50. package/dist/index.js +29 -7
  51. package/dist/interpreter/action-deps.d.ts +12 -0
  52. package/dist/interpreter/action-deps.js +10 -0
  53. package/dist/interpreter/action-types.d.ts +8 -0
  54. package/dist/interpreter/action-types.js +1 -0
  55. package/dist/interpreter/collect-tools.d.ts +1 -1
  56. package/dist/interpreter/collect-tools.js +6 -2
  57. package/dist/interpreter/execute-action.d.ts +22 -0
  58. package/dist/interpreter/execute-action.js +527 -0
  59. package/dist/interpreter/{execute-every.d.ts → execute-control.d.ts} +7 -1
  60. package/dist/interpreter/execute-control.js +530 -0
  61. package/dist/interpreter/execute.d.ts +13 -0
  62. package/dist/interpreter/execute.js +151 -48
  63. package/dist/interpreter/message-helpers.d.ts +1 -9
  64. package/dist/interpreter/message-helpers.js +1 -42
  65. package/dist/interpreter/step-dispatcher.d.ts +61 -0
  66. package/dist/interpreter/step-dispatcher.js +67 -0
  67. package/dist/interpreter/typeguards.d.ts +3 -10
  68. package/dist/interpreter/typeguards.js +3 -20
  69. package/dist/{runtime → memory}/exec-context-factory.js +1 -1
  70. package/dist/memory/flow-schema.d.ts +52 -0
  71. package/dist/memory/flow-schema.js +102 -0
  72. package/dist/memory/function-call-utils.js +4 -2
  73. package/dist/memory/layer-api.d.ts +1 -1
  74. package/dist/memory/layer-api.js +2 -2
  75. package/dist/memory/layer-lifecycle.d.ts +1 -1
  76. package/dist/memory/layer-lifecycle.js +2 -2
  77. package/dist/memory/layer-provides.d.ts +32 -0
  78. package/dist/memory/layer-provides.js +33 -0
  79. package/dist/memory/layer-usage.d.ts +3 -2
  80. package/dist/memory/layer-usage.js +9 -3
  81. package/dist/memory/layers/durable-task-state.js +1 -1
  82. package/dist/memory/layers/file-reference.js +302 -246
  83. package/dist/memory/layers/history-window.js +1 -1
  84. package/dist/memory/layers/observational-memory.js +1 -2
  85. package/dist/memory/layers/plan.d.ts +1 -1
  86. package/dist/memory/layers/plan.js +13 -8
  87. package/dist/memory/layers/steering.js +1 -1
  88. package/dist/memory/layers/tool-memory-layer.d.ts +1 -1
  89. package/dist/memory/layers/working-memory.js +2 -2
  90. package/dist/patterns/dynamic-workflow.d.ts +55 -0
  91. package/dist/patterns/dynamic-workflow.js +194 -0
  92. package/dist/patterns/flow.d.ts +2 -52
  93. package/dist/patterns/flow.js +1 -103
  94. package/dist/patterns/interview.js +1 -1
  95. package/dist/patterns/plans.d.ts +3 -3
  96. package/dist/patterns/plans.js +3 -3
  97. package/dist/patterns/ralph-wiggum.d.ts +1 -1
  98. package/dist/patterns/react.d.ts +1 -1
  99. package/dist/portable/adapters.d.ts +6 -0
  100. package/dist/portable/adapters.js +6 -0
  101. package/dist/portable/ask-user.d.ts +4 -0
  102. package/dist/portable/ask-user.js +2 -0
  103. package/dist/portable/builders.d.ts +12 -0
  104. package/dist/portable/builders.js +12 -0
  105. package/dist/portable/memory-layers.d.ts +12 -0
  106. package/dist/portable/memory-layers.js +12 -0
  107. package/dist/portable/runtime.d.ts +4 -0
  108. package/dist/portable/runtime.js +4 -0
  109. package/dist/portable/until.d.ts +4 -0
  110. package/dist/portable/until.js +4 -0
  111. package/dist/portable.d.ts +8 -26
  112. package/dist/portable.js +8 -34
  113. package/dist/runtime/broadcaster-utils.d.ts +5 -1
  114. package/dist/runtime/broadcaster-utils.js +11 -6
  115. package/dist/runtime/channel-store.js +1 -1
  116. package/dist/runtime/context-deps.d.ts +4 -0
  117. package/dist/runtime/context-deps.js +2 -0
  118. package/dist/runtime/context-impl.d.ts +42 -12
  119. package/dist/runtime/context-impl.js +51 -4
  120. package/dist/runtime/context-type-deps.d.ts +14 -0
  121. package/dist/runtime/context-type-deps.js +1 -0
  122. package/dist/runtime/detached-handle.d.ts +50 -2
  123. package/dist/runtime/detached-handle.js +216 -12
  124. package/dist/runtime/durable/checkpoint-store.d.ts +38 -0
  125. package/dist/runtime/durable/checkpoint-store.js +101 -0
  126. package/dist/runtime/durable/detached-signal.d.ts +37 -0
  127. package/dist/runtime/durable/detached-signal.js +52 -0
  128. package/dist/runtime/durable/harness-checkpoints.d.ts +51 -0
  129. package/dist/runtime/durable/harness-checkpoints.js +104 -0
  130. package/dist/runtime/durable/index.d.ts +10 -0
  131. package/dist/runtime/durable/index.js +5 -0
  132. package/dist/runtime/durable/runnable-loop.d.ts +100 -0
  133. package/dist/runtime/durable/runnable-loop.js +90 -0
  134. package/dist/runtime/durable/session-seed.d.ts +28 -0
  135. package/dist/runtime/durable/session-seed.js +27 -0
  136. package/dist/runtime/durable/stall-nudge.d.ts +82 -0
  137. package/dist/runtime/durable/stall-nudge.js +87 -0
  138. package/dist/runtime/in-memory-storage.js +1 -1
  139. package/dist/runtime/item-log-impl.d.ts +1 -1
  140. package/dist/runtime/step-registry.d.ts +42 -0
  141. package/dist/runtime/step-registry.js +77 -0
  142. package/dist/schemas/workflow.d.ts +139 -0
  143. package/dist/schemas/workflow.js +232 -0
  144. package/dist/types/ask-user-types.d.ts +57 -0
  145. package/dist/types/ask-user-types.js +74 -0
  146. package/dist/types/checkpoint.d.ts +98 -0
  147. package/dist/types/checkpoint.js +81 -0
  148. package/dist/types/common.d.ts +1 -50
  149. package/dist/types/context-parts/item-log.d.ts +6 -0
  150. package/dist/types/context-parts/item-log.js +1 -0
  151. package/dist/types/context-parts/layer-usage.d.ts +18 -0
  152. package/dist/types/context-parts/layer-usage.js +1 -0
  153. package/dist/types/context.d.ts +135 -26
  154. package/dist/types/error.d.ts +15 -0
  155. package/dist/types/harness-result.d.ts +1 -1
  156. package/dist/types/items.d.ts +18 -2
  157. package/dist/types/memory-context.d.ts +39 -0
  158. package/dist/types/memory-context.js +1 -0
  159. package/dist/types/memory.d.ts +35 -65
  160. package/dist/types/runtime.d.ts +2 -1
  161. package/dist/types/steering.d.ts +1 -1
  162. package/dist/types/step-registrar.d.ts +46 -0
  163. package/dist/types/step-registrar.js +56 -0
  164. package/dist/types/step.d.ts +30 -5
  165. package/dist/types/subprocess-adapter.d.ts +119 -3
  166. package/dist/types/subprocess-adapter.js +2 -0
  167. package/dist/types/tool.d.ts +64 -0
  168. package/dist/types/tool.js +1 -0
  169. package/dist/unstable.d.ts +2 -2
  170. package/dist/unstable.js +2 -2
  171. package/dist/until/predicates.d.ts +9 -0
  172. package/dist/until/predicates.js +14 -0
  173. package/dist/util/message-helpers.d.ts +17 -0
  174. package/dist/util/message-helpers.js +64 -0
  175. package/package.json +1 -1
  176. package/dist/adapters/local-fs-adapter.d.ts +0 -3
  177. package/dist/adapters/local-fs-adapter.js +0 -43
  178. package/dist/adapters/local-shell-adapter.d.ts +0 -51
  179. package/dist/adapters/local-shell-adapter.js +0 -167
  180. package/dist/adapters/local-subprocess-adapter.d.ts +0 -16
  181. package/dist/adapters/local-subprocess-adapter.js +0 -187
  182. package/dist/interpreter/execute-branch.d.ts +0 -3
  183. package/dist/interpreter/execute-branch.js +0 -10
  184. package/dist/interpreter/execute-every.js +0 -143
  185. package/dist/interpreter/execute-fork.d.ts +0 -3
  186. package/dist/interpreter/execute-fork.js +0 -215
  187. package/dist/interpreter/execute-llm.d.ts +0 -4
  188. package/dist/interpreter/execute-llm.js +0 -217
  189. package/dist/interpreter/execute-loop.d.ts +0 -3
  190. package/dist/interpreter/execute-loop.js +0 -171
  191. package/dist/interpreter/execute-provide.d.ts +0 -10
  192. package/dist/interpreter/execute-provide.js +0 -64
  193. package/dist/interpreter/execute-run.d.ts +0 -3
  194. package/dist/interpreter/execute-run.js +0 -39
  195. package/dist/interpreter/execute-spawn.d.ts +0 -11
  196. package/dist/interpreter/execute-spawn.js +0 -113
  197. package/dist/interpreter/execute-tool.d.ts +0 -5
  198. package/dist/interpreter/execute-tool.js +0 -81
  199. package/dist/runtime/agent-harness.js +0 -1170
  200. /package/dist/{runtime → memory}/exec-context-factory.d.ts +0 -0
  201. /package/dist/{interpreter → util}/framework-cast.d.ts +0 -0
  202. /package/dist/{interpreter → util}/framework-cast.js +0 -0
@@ -1,143 +0,0 @@
1
- import { isNoeticError, NoeticErrorImpl } from '../errors/noetic-error';
2
- import { getContextChannelStore } from './typeguards';
3
- //#endregion
4
- //#region Helper Functions
5
- /** Returns the next park duration in ms, applying random jitter clamped to `[ms - jitter, ms + jitter]`. */
6
- function nextParkMs(ms, jitter) {
7
- if (jitter <= 0) {
8
- return ms;
9
- }
10
- // Math.random() returns [0, 1); shift to [-1, 1) so jitter is symmetric.
11
- const offset = (Math.random() * 2 - 1) * jitter;
12
- const value = ms + offset;
13
- if (value < 0) {
14
- return 0;
15
- }
16
- return value;
17
- }
18
- /**
19
- * Parks for `ms` ms, returning early if `wakeOn` receives a message or the
20
- * context is aborted. Always resolves; never throws (cancellation surfaces on
21
- * the next iteration's abort check).
22
- */
23
- function park(parkCtx) {
24
- const duration = nextParkMs(parkCtx.ms, parkCtx.jitter);
25
- return new Promise((resolve) => {
26
- let settled = false;
27
- let timer = null;
28
- let abortPoll = null;
29
- let wakeUnsub = null;
30
- const settle = () => {
31
- if (settled) {
32
- return;
33
- }
34
- settled = true;
35
- if (timer !== null) {
36
- clearTimeout(timer);
37
- }
38
- if (abortPoll !== null) {
39
- clearInterval(abortPoll);
40
- }
41
- if (wakeUnsub !== null) {
42
- wakeUnsub();
43
- }
44
- resolve();
45
- };
46
- timer = setTimeout(settle, duration);
47
- // Poll the abort flag at a coarse interval to wake parking when the
48
- // context is aborted. The Context interface exposes only `aborted` as a
49
- // boolean, so this is the simplest interruption strategy that respects
50
- // the public contract.
51
- abortPoll = setInterval(() => {
52
- if (parkCtx.ctx.aborted) {
53
- settle();
54
- }
55
- }, 5);
56
- if (!parkCtx.wakeOn) {
57
- return;
58
- }
59
- const { channelStore } = parkCtx;
60
- if (channelStore) {
61
- // Non-consuming subscription so the body still observes the message that
62
- // woke us — `recv()` would dequeue queue-mode messages and leave the
63
- // body's drain loop empty.
64
- wakeUnsub = channelStore.subscribeWake(parkCtx.wakeOn, settle);
65
- return;
66
- }
67
- // Contexts without a ContextImpl/channel store have no body draining the
68
- // channel anyway; the destructive `recv` is acceptable in that edge case.
69
- parkCtx.ctx
70
- .recv(parkCtx.wakeOn, {
71
- timeout: Math.max(duration, 1) + 1e2,
72
- })
73
- .then(settle)
74
- .catch(() => {
75
- // Channel timeout / store error — another path settles us.
76
- });
77
- });
78
- }
79
- function recordIterationError(ctx, error) {
80
- const message = error instanceof Error ? error.message : String(error);
81
- const stack = error instanceof Error && error.stack ? error.stack : '';
82
- ctx.span.addEvent('every.iteration.error', {
83
- message,
84
- stack,
85
- });
86
- }
87
- function throwCancelled(reason) {
88
- throw new NoeticErrorImpl({
89
- kind: 'cancelled',
90
- reason: reason ?? 'context aborted',
91
- });
92
- }
93
- //#endregion
94
- //#region Public API
95
- /**
96
- * Executes an `every` step: runs the body step forever, paced by `ms ± jitter`,
97
- * woken early by `wakeOn`. Throws `cancelled` when the context is aborted.
98
- *
99
- * On body throw, behavior depends on `onError`:
100
- * - `'continue'` (default): emit `every.iteration.error` span event and proceed
101
- * to the park step as if no error occurred.
102
- * - `'fail'`: re-throw, terminating the operator.
103
- *
104
- * The body's output is discarded — `every` runs forever and does not accumulate
105
- * iteration outputs. Only ever returns by throwing on cancellation or `fail`.
106
- */
107
- export async function executeEvery(step, input, ctx, executeStep) {
108
- const onError = step.onError ?? 'continue';
109
- const jitter = step.jitter ?? 0;
110
- // Resolve once — the channel store reference is stable for the lifetime
111
- // of the every loop, no need to re-derive it per park.
112
- const channelStore = getContextChannelStore(ctx);
113
- while (true) {
114
- if (ctx.aborted) {
115
- throwCancelled(ctx.abortReason);
116
- }
117
- try {
118
- await executeStep(step.step, input, ctx);
119
- }
120
- catch (e) {
121
- if (onError === 'fail') {
122
- throw e;
123
- }
124
- // 'continue' policy — but a cancellation should still terminate the
125
- // operator promptly rather than be swallowed and re-parked.
126
- if (isNoeticError(e) && e.noeticError.kind === 'cancelled') {
127
- throw e;
128
- }
129
- recordIterationError(ctx, e);
130
- }
131
- if (ctx.aborted) {
132
- throwCancelled(ctx.abortReason);
133
- }
134
- await park({
135
- ms: step.ms,
136
- jitter,
137
- wakeOn: step.wakeOn,
138
- ctx,
139
- channelStore,
140
- });
141
- }
142
- }
143
- //#endregion
@@ -1,3 +0,0 @@
1
- import type { Context } from '../types/context';
2
- import type { ExecuteStepFn, StepFork } from '../types/step';
3
- export declare function executeFork<TMemory, I, O>(step: StepFork<TMemory, I, O>, input: I, ctx: Context<TMemory>, executeStep: ExecuteStepFn): Promise<O>;
@@ -1,215 +0,0 @@
1
- import { isNoeticError, NoeticErrorImpl } from '../errors/noetic-error';
2
- import { ContextImpl } from '../runtime/context-impl';
3
- import { snapshotCwdState } from '../runtime/cwd-helpers';
4
- import { cloneWithGuard } from './clone-guard';
5
- import { frameworkCast } from './framework-cast';
6
- import { getContextChannelStore, isContextImpl } from './typeguards';
7
- function createChildContexts(ctx, count, stepId) {
8
- const threadId = isContextImpl(ctx) ? ctx.threadId : crypto.randomUUID();
9
- const resourceId = isContextImpl(ctx) ? ctx.resourceId : undefined;
10
- const channelStore = getContextChannelStore(ctx);
11
- return Array.from({
12
- length: count,
13
- }, () => new ContextImpl({
14
- harness: ctx.harness,
15
- parent: ctx,
16
- items: [
17
- ...ctx.itemLog.items,
18
- ],
19
- state: cloneWithGuard(ctx.state, `Fork '${stepId}'`),
20
- threadId,
21
- resourceId,
22
- channelStore,
23
- cwdState: snapshotCwdState(ctx),
24
- }));
25
- }
26
- export async function executeFork(step, input, ctx, executeStep) {
27
- const baseCtx = frameworkCast(ctx);
28
- const paths = step.paths(input, ctx);
29
- if (paths.length === 0) {
30
- if (step.mode === 'all') {
31
- return step.merge([], ctx);
32
- }
33
- if (step.mode === 'settle') {
34
- return step.merge([], ctx);
35
- }
36
- throw new NoeticErrorImpl({
37
- kind: 'fork_partial',
38
- stepId: step.id,
39
- succeeded: [],
40
- failed: [],
41
- });
42
- }
43
- const childContexts = createChildContexts(baseCtx, paths.length, step.id);
44
- const concurrency = step.concurrency ?? paths.length;
45
- switch (step.mode) {
46
- case 'all':
47
- return executeAll(step, paths, input, ctx, childContexts, executeStep, concurrency);
48
- case 'race':
49
- return executeRace(step, paths, input, ctx, childContexts, executeStep, concurrency);
50
- case 'settle':
51
- return executeSettle(step, paths, input, ctx, childContexts, executeStep, concurrency);
52
- default: {
53
- const _exhaustive = step;
54
- throw new NoeticErrorImpl({
55
- kind: 'step_failed',
56
- stepId: 'unknown',
57
- cause: new Error('Unknown fork mode'),
58
- retriesExhausted: false,
59
- });
60
- }
61
- }
62
- }
63
- async function runWithConcurrency(tasks, concurrency) {
64
- const results = new Array(tasks.length);
65
- let nextIndex = 0;
66
- async function runNext() {
67
- while (nextIndex < tasks.length) {
68
- const index = nextIndex++;
69
- try {
70
- const value = await tasks[index]();
71
- results[index] = {
72
- status: 'fulfilled',
73
- value,
74
- };
75
- }
76
- catch (reason) {
77
- results[index] = {
78
- status: 'rejected',
79
- reason,
80
- };
81
- }
82
- }
83
- }
84
- const workers = Array.from({
85
- length: Math.min(concurrency, tasks.length),
86
- }, () => runNext());
87
- await Promise.all(workers);
88
- return results;
89
- }
90
- function toNoeticError(err, stepId) {
91
- if (isNoeticError(err)) {
92
- return err.noeticError;
93
- }
94
- return {
95
- kind: 'step_failed',
96
- stepId,
97
- cause: err instanceof Error ? err : new Error(String(err)),
98
- retriesExhausted: false,
99
- };
100
- }
101
- function classifyResults(settled, paths) {
102
- const succeeded = [];
103
- const failed = [];
104
- settled.forEach((result, i) => {
105
- if (result.status === 'fulfilled') {
106
- succeeded.push({
107
- stepId: paths[i].id,
108
- value: result.value,
109
- });
110
- }
111
- else {
112
- failed.push({
113
- stepId: paths[i].id,
114
- error: toNoeticError(result.reason, paths[i].id),
115
- });
116
- }
117
- });
118
- return {
119
- succeeded,
120
- failed,
121
- };
122
- }
123
- async function executeAll(step, paths, _input, ctx, childContexts, executeStep, concurrency) {
124
- const tasks = paths.map((path, i) => () => executeStep(path, _input, frameworkCast(childContexts[i])));
125
- const settled = await runWithConcurrency(tasks, concurrency);
126
- const { succeeded, failed } = classifyResults(settled, paths);
127
- if (failed.length > 0) {
128
- throw new NoeticErrorImpl({
129
- kind: 'fork_partial',
130
- stepId: step.id,
131
- succeeded,
132
- failed,
133
- });
134
- }
135
- const results = succeeded.map((s) => s.value);
136
- return step.merge(results, ctx);
137
- }
138
- async function executeRace(step, paths, input, ctx, childContexts, executeStep, concurrency) {
139
- const raceBaseCtx = frameworkCast(ctx);
140
- return new Promise((resolve, reject) => {
141
- let settled = false;
142
- let failedCount = 0;
143
- let nextIndex = 0;
144
- const errors = [];
145
- const totalPaths = paths.length;
146
- function startNext() {
147
- if (settled || nextIndex >= totalPaths) {
148
- return;
149
- }
150
- const i = nextIndex++;
151
- executeStep(paths[i], input, frameworkCast(childContexts[i]))
152
- .then((result) => {
153
- if (settled) {
154
- return;
155
- }
156
- settled = true;
157
- // Winner's state replaces parent's
158
- if (isContextImpl(raceBaseCtx)) {
159
- raceBaseCtx.state = childContexts[i].state;
160
- }
161
- // Resolve first, then abort losers (non-critical path)
162
- resolve(result);
163
- for (let j = 0; j < childContexts.length; j++) {
164
- if (j !== i) {
165
- childContexts[j].abort('race lost');
166
- }
167
- }
168
- })
169
- .catch((err) => {
170
- failedCount++;
171
- errors.push({
172
- stepId: paths[i].id,
173
- error: toNoeticError(err, paths[i].id),
174
- });
175
- // Start next task if available
176
- if (!settled) {
177
- startNext();
178
- }
179
- if (failedCount === totalPaths && !settled) {
180
- settled = true;
181
- reject(new NoeticErrorImpl({
182
- kind: 'fork_partial',
183
- stepId: step.id,
184
- succeeded: [],
185
- failed: errors,
186
- }));
187
- }
188
- });
189
- }
190
- // Launch initial batch respecting concurrency
191
- const initialBatch = Math.min(concurrency, totalPaths);
192
- for (let i = 0; i < initialBatch; i++) {
193
- startNext();
194
- }
195
- });
196
- }
197
- async function executeSettle(step, paths, input, _ctx, childContexts, executeStep, concurrency) {
198
- const tasks = paths.map((path, i) => () => executeStep(path, input, frameworkCast(childContexts[i])));
199
- const settled = await runWithConcurrency(tasks, concurrency);
200
- const results = settled.map((result, i) => {
201
- if (result.status === 'fulfilled') {
202
- return {
203
- stepId: paths[i].id,
204
- status: 'fulfilled',
205
- value: result.value,
206
- };
207
- }
208
- return {
209
- stepId: paths[i].id,
210
- status: 'rejected',
211
- error: toNoeticError(result.reason, paths[i].id),
212
- };
213
- });
214
- return step.merge(results, _ctx);
215
- }
@@ -1,4 +0,0 @@
1
- import type { Context } from '../types/context';
2
- import type { MemoryLayer } from '../types/memory';
3
- import type { StepLLM } from '../types/step';
4
- export declare function executeLLM<TMemory, I, O>(step: StepLLM<TMemory, I, O>, input: I, ctx: Context<TMemory>, layers?: MemoryLayer[]): Promise<O>;
@@ -1,217 +0,0 @@
1
- import { ZodError } from 'zod';
2
- import { NoeticErrorImpl } from '../errors/noetic-error';
3
- import { resolveLayerTools } from '../memory/layer-api';
4
- import { commitLayerUsage, computeLayerUsage } from '../memory/layer-usage';
5
- import { assembleView } from '../memory/projector';
6
- import { SteeringAction } from '../types/steering';
7
- import { frameworkCast } from './framework-cast';
8
- import { createMessage, extractAssistantText, trackUsage } from './message-helpers';
9
- import { isFunctionCall, isMutableContext } from './typeguards';
10
- const MAX_STEERING_RETRIES = 3;
11
- const emptyRecall = [];
12
- /**
13
- * Resolves which tools to send and what restrictions to apply.
14
- *
15
- * When a unified tool set exists on the context (collected before execution),
16
- * every LLM call sends the full set and step.tools narrows via allowedToolNames.
17
- *
18
- * Semantics:
19
- * step.tools = undefined → unrestricted (all tools, no allowedToolNames)
20
- * step.tools = [] → no tools at all
21
- * step.tools = [a, b] → full set sent, restrict to a and b
22
- *
23
- * Fallback: when no unified set exists (e.g. harness.run() called directly),
24
- * merge step tools with layer tools as before.
25
- */
26
- function resolveToolsAndRestrictions(step, layers, ctx) {
27
- // step.tools = [] → explicit opt-out
28
- if (step.tools && step.tools.length === 0) {
29
- return {
30
- tools: undefined,
31
- allowedToolNames: undefined,
32
- };
33
- }
34
- const unified = ctx.unifiedTools;
35
- if (unified && unified.length > 0) {
36
- const allowedToolNames = step.tools ? step.tools.map((t) => t.name) : undefined;
37
- return {
38
- tools: [
39
- ...unified,
40
- ],
41
- allowedToolNames,
42
- };
43
- }
44
- // Fallback: no unified set (direct harness.run() path)
45
- const layerTools = layers && layers.length > 0 ? resolveLayerTools(layers, ctx.harness, ctx) : [];
46
- if (layerTools.length === 0 && !step.tools) {
47
- return {
48
- tools: undefined,
49
- allowedToolNames: undefined,
50
- };
51
- }
52
- const merged = [
53
- ...(step.tools ?? []),
54
- ...layerTools,
55
- ];
56
- return {
57
- tools: merged.length > 0 ? merged : undefined,
58
- allowedToolNames: undefined,
59
- };
60
- }
61
- async function runInputPipeline({ ctx, layers, input }) {
62
- const userItem = createMessage(input, 'user');
63
- const { items: finalItems } = await ctx.harness.runAppendPipeline(layers, [
64
- userItem,
65
- ], ctx);
66
- for (const item of finalItems) {
67
- ctx.itemLog.append(item);
68
- }
69
- }
70
- //#endregion
71
- export async function executeLLM(step, input, ctx, layers) {
72
- const baseCtx = frameworkCast(ctx);
73
- const hasLayers = layers !== undefined && layers.length > 0;
74
- // Append user input — through layer pipeline if layers exist, otherwise direct.
75
- if (typeof input === 'string' && input.length > 0) {
76
- if (hasLayers) {
77
- await runInputPipeline({
78
- ctx: baseCtx,
79
- layers,
80
- input,
81
- });
82
- }
83
- else {
84
- baseCtx.itemLog.append(createMessage(input, 'user'));
85
- }
86
- }
87
- const { tools: resolvedTools, allowedToolNames } = resolveToolsAndRestrictions(step, layers, baseCtx);
88
- // Recall once per LLM step: every layer with a recall hook contributes its
89
- // current view. Results drive both the assembled context window and the
90
- // per-layer usage breakdown (ctx.lastLayerUsage). Recall fires before the
91
- // steering retry loop because retries replay the same context.
92
- const recallQuery = typeof input === 'string' ? input : '';
93
- const recallResults = hasLayers
94
- ? await baseCtx.harness.recallLayers(layers, recallQuery, baseCtx)
95
- : emptyRecall;
96
- const layerOutputItems = recallResults.flatMap((r) => r.items);
97
- let retries = 0;
98
- while (retries <= MAX_STEERING_RETRIES) {
99
- const rawHistoryItems = baseCtx.itemLog.items;
100
- const projectedHistoryItems = hasLayers
101
- ? await baseCtx.harness.projectHistory(layers, rawHistoryItems, baseCtx)
102
- : rawHistoryItems;
103
- const assembledItems = layerOutputItems.length > 0
104
- ? assembleView({
105
- systemPromptItems: [],
106
- layerOutputItems,
107
- historyItems: [
108
- ...projectedHistoryItems,
109
- ],
110
- })
111
- : projectedHistoryItems === rawHistoryItems
112
- ? rawHistoryItems
113
- : [
114
- ...projectedHistoryItems,
115
- ];
116
- const request = resolvedTools
117
- ? {
118
- model: step.model,
119
- items: assembledItems,
120
- instructions: step.instructions,
121
- tools: resolvedTools,
122
- params: step.params,
123
- outputSchema: step.output,
124
- emit: step.emit,
125
- ctx: baseCtx,
126
- layers,
127
- allowedToolNames,
128
- }
129
- : {
130
- model: step.model,
131
- items: assembledItems,
132
- instructions: step.instructions,
133
- params: step.params,
134
- outputSchema: step.output,
135
- emit: step.emit,
136
- };
137
- const response = await baseCtx.harness.callModel(request);
138
- if (hasLayers) {
139
- const decision = await baseCtx.harness.afterModelCall(layers, response, baseCtx);
140
- if (decision.action === SteeringAction.Deny) {
141
- throw new NoeticErrorImpl({
142
- kind: 'steering_denied',
143
- guidance: decision.guidance,
144
- });
145
- }
146
- if (decision.action === SteeringAction.Guide && retries < MAX_STEERING_RETRIES) {
147
- baseCtx.itemLog.append(createMessage(decision.guidance ?? 'Please adjust your response.', 'developer'));
148
- retries++;
149
- continue;
150
- }
151
- }
152
- const toolCalls = [];
153
- for (const item of response.items) {
154
- baseCtx.itemLog.append(item);
155
- if (isFunctionCall(item)) {
156
- toolCalls.push(item);
157
- }
158
- }
159
- const meta = {
160
- toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
161
- usage: response.usage,
162
- cost: response.cost,
163
- responseItems: response.items,
164
- };
165
- if (isMutableContext(baseCtx)) {
166
- baseCtx.lastStepMeta = meta;
167
- }
168
- trackUsage(baseCtx, response);
169
- commitLayerUsage(baseCtx, computeLayerUsage({
170
- ctx: baseCtx,
171
- modelId: step.model,
172
- instructions: step.instructions,
173
- tools: resolvedTools,
174
- recallResults,
175
- }));
176
- if (hasLayers) {
177
- await baseCtx.harness.storeLayers(layers, response, baseCtx);
178
- }
179
- const lastText = extractAssistantText(response.items);
180
- if (step.output) {
181
- try {
182
- const parsed = JSON.parse(lastText);
183
- return step.output.parse(parsed);
184
- }
185
- catch (e) {
186
- if (e instanceof SyntaxError || e instanceof ZodError) {
187
- throw new NoeticErrorImpl({
188
- kind: 'llm_parse_error',
189
- stepId: step.id,
190
- raw: lastText,
191
- schema: step.output,
192
- zodError: e instanceof ZodError
193
- ? e
194
- : new ZodError([
195
- {
196
- code: 'custom',
197
- message: `Invalid JSON: ${e.message}`,
198
- path: [],
199
- },
200
- ]),
201
- });
202
- }
203
- throw e;
204
- }
205
- }
206
- return frameworkCast(lastText);
207
- }
208
- // Safety net: the loop above always returns or throws within the body.
209
- // This throw is unreachable but protects against future refactors that
210
- // might break the loop invariant.
211
- throw new NoeticErrorImpl({
212
- kind: 'step_failed',
213
- stepId: step.id,
214
- cause: new Error('Steering retries exhausted'),
215
- retriesExhausted: true,
216
- });
217
- }
@@ -1,3 +0,0 @@
1
- import type { Context } from '../types/context';
2
- import type { ExecuteStepFn, StepLoop } from '../types/step';
3
- export declare function executeLoop<TMemory, I, O>(step: StepLoop<TMemory, I, O>, input: I, ctx: Context<TMemory>, executeStep: ExecuteStepFn): Promise<O>;