@noetic-tools/core 0.0.1 → 0.1.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 (254) hide show
  1. package/README.md +31 -11
  2. package/dist/adapters/in-memory-fs-adapter.d.ts +1 -1
  3. package/dist/adapters/in-memory-fs-adapter.js +1 -1
  4. package/dist/adapters/in-memory-shell-adapter.d.ts +1 -1
  5. package/dist/adapters/in-memory-subprocess/manifest-persistence.d.ts +8 -0
  6. package/dist/adapters/in-memory-subprocess/manifest-persistence.js +56 -0
  7. package/dist/adapters/in-memory-subprocess/metadata.d.ts +12 -0
  8. package/dist/adapters/in-memory-subprocess/metadata.js +58 -0
  9. package/dist/adapters/in-memory-subprocess/step-completion.d.ts +3 -0
  10. package/dist/adapters/in-memory-subprocess/step-completion.js +86 -0
  11. package/dist/adapters/in-memory-subprocess/types.d.ts +88 -0
  12. package/dist/adapters/in-memory-subprocess/types.js +2 -0
  13. package/dist/adapters/in-memory-subprocess-adapter.d.ts +12 -4
  14. package/dist/adapters/in-memory-subprocess-adapter.js +164 -55
  15. package/dist/adapters/openrouter.d.ts +16 -7
  16. package/dist/adapters/openrouter.js +68 -7
  17. package/dist/builders/channel-builder.d.ts +1 -1
  18. package/dist/builders/control-flow-builders.d.ts +3 -3
  19. package/dist/builders/control-flow-builders.js +9 -4
  20. package/dist/builders/every.d.ts +3 -3
  21. package/dist/builders/every.js +5 -2
  22. package/dist/builders/layer-provides-builders.d.ts +1 -32
  23. package/dist/builders/layer-provides-builders.js +1 -33
  24. package/dist/builders/loop-builder.d.ts +2 -2
  25. package/dist/builders/loop-builder.js +5 -2
  26. package/dist/builders/memory-builder.d.ts +1 -1
  27. package/dist/builders/memory-builder.js +1 -1
  28. package/dist/builders/provide-builder.d.ts +2 -2
  29. package/dist/builders/provide-builder.js +5 -2
  30. package/dist/builders/spawn-builder.d.ts +22 -9
  31. package/dist/builders/spawn-builder.js +17 -3
  32. package/dist/builders/step-builders.d.ts +51 -30
  33. package/dist/builders/step-builders.js +31 -12
  34. package/dist/builders/tool-builder.d.ts +7 -3
  35. package/dist/builders/tool-builder.js +3 -1
  36. package/dist/builders/workflow-hydrator.d.ts +38 -0
  37. package/dist/builders/workflow-hydrator.js +336 -0
  38. package/dist/conditions/conditions.d.ts +4 -4
  39. package/dist/conditions/conditions.js +2 -2
  40. package/dist/errors/noetic-error.d.ts +1 -1
  41. package/dist/errors/noetic-error.js +2 -0
  42. package/dist/{runtime → harness}/agent-harness.d.ts +65 -45
  43. package/dist/harness/agent-harness.js +576 -0
  44. package/dist/harness/deps/adapters.d.ts +4 -0
  45. package/dist/harness/deps/adapters.js +4 -0
  46. package/dist/harness/deps/interpreter.d.ts +5 -0
  47. package/dist/harness/deps/interpreter.js +4 -0
  48. package/dist/harness/deps/memory.d.ts +5 -0
  49. package/dist/harness/deps/memory.js +4 -0
  50. package/dist/harness/deps/runtime.d.ts +10 -0
  51. package/dist/harness/deps/runtime.js +7 -0
  52. package/dist/harness/deps/types.d.ts +17 -0
  53. package/dist/harness/deps/types.js +1 -0
  54. package/dist/harness/model-call.d.ts +50 -0
  55. package/dist/harness/model-call.js +694 -0
  56. package/dist/harness/model-schema.d.ts +19 -0
  57. package/dist/harness/model-schema.js +46 -0
  58. package/dist/harness/step-bootstrap.d.ts +25 -0
  59. package/dist/harness/step-bootstrap.js +170 -0
  60. package/dist/index.d.ts +114 -76
  61. package/dist/index.js +74 -50
  62. package/dist/internal-test.d.ts +4 -4
  63. package/dist/internal-test.js +4 -4
  64. package/dist/interpreter/action-deps.d.ts +12 -0
  65. package/dist/interpreter/action-deps.js +10 -0
  66. package/dist/interpreter/action-types.d.ts +8 -0
  67. package/dist/interpreter/action-types.js +1 -0
  68. package/dist/interpreter/collect-tools.d.ts +2 -2
  69. package/dist/interpreter/collect-tools.js +6 -2
  70. package/dist/interpreter/execute-action.d.ts +22 -0
  71. package/dist/interpreter/execute-action.js +527 -0
  72. package/dist/interpreter/{execute-every.d.ts → execute-control.d.ts} +8 -2
  73. package/dist/interpreter/execute-control.js +530 -0
  74. package/dist/interpreter/execute.d.ts +16 -3
  75. package/dist/interpreter/execute.js +154 -51
  76. package/dist/interpreter/message-helpers.d.ts +1 -9
  77. package/dist/interpreter/message-helpers.js +1 -42
  78. package/dist/interpreter/step-dispatcher.d.ts +61 -0
  79. package/dist/interpreter/step-dispatcher.js +67 -0
  80. package/dist/interpreter/typeguards.d.ts +8 -15
  81. package/dist/interpreter/typeguards.js +4 -21
  82. package/dist/memory/budget.d.ts +2 -2
  83. package/dist/memory/budget.js +1 -1
  84. package/dist/{runtime → memory}/exec-context-factory.d.ts +2 -2
  85. package/dist/{runtime → memory}/exec-context-factory.js +1 -1
  86. package/dist/memory/flow-schema.d.ts +52 -0
  87. package/dist/memory/flow-schema.js +102 -0
  88. package/dist/memory/function-call-utils.d.ts +1 -1
  89. package/dist/memory/function-call-utils.js +4 -2
  90. package/dist/memory/layer-api.d.ts +4 -4
  91. package/dist/memory/layer-api.js +2 -2
  92. package/dist/memory/layer-lifecycle.d.ts +6 -6
  93. package/dist/memory/layer-lifecycle.js +6 -6
  94. package/dist/memory/layer-provides.d.ts +32 -0
  95. package/dist/memory/layer-provides.js +33 -0
  96. package/dist/memory/layer-usage.d.ts +4 -3
  97. package/dist/memory/layer-usage.js +9 -3
  98. package/dist/memory/layers/durable-task-state.d.ts +8 -8
  99. package/dist/memory/layers/durable-task-state.js +2 -2
  100. package/dist/memory/layers/file-reference.d.ts +1 -1
  101. package/dist/memory/layers/file-reference.js +303 -247
  102. package/dist/memory/layers/history-window.d.ts +1 -1
  103. package/dist/memory/layers/history-window.js +4 -4
  104. package/dist/memory/layers/observational-memory.d.ts +5 -5
  105. package/dist/memory/layers/observational-memory.js +2 -3
  106. package/dist/memory/layers/plan.d.ts +2 -2
  107. package/dist/memory/layers/plan.js +15 -10
  108. package/dist/memory/layers/static-content.d.ts +2 -2
  109. package/dist/memory/layers/static-content.js +1 -1
  110. package/dist/memory/layers/steering.d.ts +7 -7
  111. package/dist/memory/layers/steering.js +5 -5
  112. package/dist/memory/layers/tool-memory-layer.d.ts +2 -2
  113. package/dist/memory/layers/tool-memory-layer.js +1 -1
  114. package/dist/memory/layers/working-memory.d.ts +7 -7
  115. package/dist/memory/layers/working-memory.js +4 -4
  116. package/dist/memory/projector.d.ts +2 -2
  117. package/dist/memory/scope.d.ts +1 -1
  118. package/dist/memory/strip-unresolved.d.ts +1 -1
  119. package/dist/observability/span-impl.d.ts +1 -1
  120. package/dist/observability/trace-exporter.d.ts +2 -2
  121. package/dist/observability/trace-exporter.js +1 -1
  122. package/dist/patterns/dynamic-workflow.d.ts +55 -0
  123. package/dist/patterns/dynamic-workflow.js +194 -0
  124. package/dist/patterns/flow.d.ts +2 -52
  125. package/dist/patterns/flow.js +1 -103
  126. package/dist/patterns/interview.d.ts +2 -2
  127. package/dist/patterns/interview.js +5 -5
  128. package/dist/patterns/plans.d.ts +6 -6
  129. package/dist/patterns/plans.js +4 -4
  130. package/dist/patterns/ralph-wiggum.d.ts +4 -4
  131. package/dist/patterns/ralph-wiggum.js +5 -5
  132. package/dist/patterns/react.d.ts +3 -3
  133. package/dist/patterns/react.js +5 -5
  134. package/dist/portable/adapters.d.ts +6 -0
  135. package/dist/portable/adapters.js +6 -0
  136. package/dist/portable/ask-user.d.ts +4 -0
  137. package/dist/portable/ask-user.js +2 -0
  138. package/dist/portable/builders.d.ts +12 -0
  139. package/dist/portable/builders.js +12 -0
  140. package/dist/portable/memory-layers.d.ts +12 -0
  141. package/dist/portable/memory-layers.js +12 -0
  142. package/dist/portable/runtime.d.ts +4 -0
  143. package/dist/portable/runtime.js +4 -0
  144. package/dist/portable/until.d.ts +4 -0
  145. package/dist/portable/until.js +4 -0
  146. package/dist/portable.d.ts +8 -26
  147. package/dist/portable.js +8 -34
  148. package/dist/runtime/broadcaster-utils.d.ts +7 -3
  149. package/dist/runtime/broadcaster-utils.js +11 -6
  150. package/dist/runtime/channel-store.d.ts +1 -1
  151. package/dist/runtime/channel-store.js +2 -2
  152. package/dist/runtime/context-deps.d.ts +4 -0
  153. package/dist/runtime/context-deps.js +2 -0
  154. package/dist/runtime/context-impl.d.ts +42 -12
  155. package/dist/runtime/context-impl.js +52 -5
  156. package/dist/runtime/context-type-deps.d.ts +14 -0
  157. package/dist/runtime/context-type-deps.js +1 -0
  158. package/dist/runtime/cwd-helpers.d.ts +1 -1
  159. package/dist/runtime/detached-handle.d.ts +52 -4
  160. package/dist/runtime/detached-handle.js +218 -14
  161. package/dist/runtime/durable/checkpoint-store.d.ts +38 -0
  162. package/dist/runtime/durable/checkpoint-store.js +101 -0
  163. package/dist/runtime/durable/detached-signal.d.ts +37 -0
  164. package/dist/runtime/durable/detached-signal.js +52 -0
  165. package/dist/runtime/durable/harness-checkpoints.d.ts +51 -0
  166. package/dist/runtime/durable/harness-checkpoints.js +104 -0
  167. package/dist/runtime/durable/index.d.ts +10 -0
  168. package/dist/runtime/durable/index.js +5 -0
  169. package/dist/runtime/durable/runnable-loop.d.ts +100 -0
  170. package/dist/runtime/durable/runnable-loop.js +90 -0
  171. package/dist/runtime/durable/session-seed.d.ts +28 -0
  172. package/dist/runtime/durable/session-seed.js +27 -0
  173. package/dist/runtime/durable/stall-nudge.d.ts +82 -0
  174. package/dist/runtime/durable/stall-nudge.js +87 -0
  175. package/dist/runtime/event-broadcaster.d.ts +1 -1
  176. package/dist/runtime/in-memory-storage.d.ts +1 -1
  177. package/dist/runtime/in-memory-storage.js +1 -1
  178. package/dist/runtime/item-log-impl.d.ts +3 -3
  179. package/dist/runtime/item-log-impl.js +1 -1
  180. package/dist/runtime/message-queue.d.ts +2 -2
  181. package/dist/runtime/session-runner.d.ts +7 -7
  182. package/dist/runtime/session-runner.js +3 -3
  183. package/dist/runtime/session-streams.d.ts +3 -3
  184. package/dist/runtime/session-streams.js +1 -1
  185. package/dist/runtime/step-registry.d.ts +42 -0
  186. package/dist/runtime/step-registry.js +77 -0
  187. package/dist/runtime/tool-memory.d.ts +3 -3
  188. package/dist/schemas/item.d.ts +1 -1
  189. package/dist/schemas/workflow.d.ts +139 -0
  190. package/dist/schemas/workflow.js +232 -0
  191. package/dist/types/ask-user-types.d.ts +57 -0
  192. package/dist/types/ask-user-types.js +74 -0
  193. package/dist/types/checkpoint.d.ts +98 -0
  194. package/dist/types/checkpoint.js +81 -0
  195. package/dist/types/common.d.ts +1 -50
  196. package/dist/types/context-parts/item-log.d.ts +6 -0
  197. package/dist/types/context-parts/item-log.js +1 -0
  198. package/dist/types/context-parts/layer-usage.d.ts +18 -0
  199. package/dist/types/context-parts/layer-usage.js +1 -0
  200. package/dist/types/context.d.ts +140 -31
  201. package/dist/types/error.d.ts +15 -0
  202. package/dist/types/harness-result.d.ts +2 -2
  203. package/dist/types/items.d.ts +18 -2
  204. package/dist/types/memory-context.d.ts +39 -0
  205. package/dist/types/memory-context.js +1 -0
  206. package/dist/types/memory.d.ts +38 -68
  207. package/dist/types/mutable-context.d.ts +3 -3
  208. package/dist/types/runtime.d.ts +14 -13
  209. package/dist/types/steering.d.ts +2 -2
  210. package/dist/types/step-registrar.d.ts +46 -0
  211. package/dist/types/step-registrar.js +56 -0
  212. package/dist/types/step.d.ts +34 -9
  213. package/dist/types/subprocess-adapter.d.ts +119 -3
  214. package/dist/types/subprocess-adapter.js +2 -0
  215. package/dist/types/tool-context.d.ts +6 -6
  216. package/dist/types/tool.d.ts +64 -0
  217. package/dist/types/tool.js +1 -0
  218. package/dist/unstable.d.ts +9 -9
  219. package/dist/unstable.js +7 -7
  220. package/dist/until/combinators.d.ts +1 -1
  221. package/dist/until/never.d.ts +1 -1
  222. package/dist/until/predicates.d.ts +13 -4
  223. package/dist/until/predicates.js +16 -2
  224. package/dist/util/message-helpers.d.ts +17 -0
  225. package/dist/util/message-helpers.js +64 -0
  226. package/package.json +6 -4
  227. package/dist/adapters/local-fs-adapter.d.ts +0 -3
  228. package/dist/adapters/local-fs-adapter.js +0 -43
  229. package/dist/adapters/local-shell-adapter.d.ts +0 -51
  230. package/dist/adapters/local-shell-adapter.js +0 -167
  231. package/dist/adapters/local-subprocess-adapter.d.ts +0 -16
  232. package/dist/adapters/local-subprocess-adapter.js +0 -187
  233. package/dist/conditions/index.d.ts +0 -3
  234. package/dist/conditions/index.js +0 -2
  235. package/dist/interpreter/execute-branch.d.ts +0 -3
  236. package/dist/interpreter/execute-branch.js +0 -10
  237. package/dist/interpreter/execute-every.js +0 -143
  238. package/dist/interpreter/execute-fork.d.ts +0 -3
  239. package/dist/interpreter/execute-fork.js +0 -215
  240. package/dist/interpreter/execute-llm.d.ts +0 -4
  241. package/dist/interpreter/execute-llm.js +0 -217
  242. package/dist/interpreter/execute-loop.d.ts +0 -3
  243. package/dist/interpreter/execute-loop.js +0 -171
  244. package/dist/interpreter/execute-provide.d.ts +0 -10
  245. package/dist/interpreter/execute-provide.js +0 -64
  246. package/dist/interpreter/execute-run.d.ts +0 -3
  247. package/dist/interpreter/execute-run.js +0 -39
  248. package/dist/interpreter/execute-spawn.d.ts +0 -11
  249. package/dist/interpreter/execute-spawn.js +0 -113
  250. package/dist/interpreter/execute-tool.d.ts +0 -5
  251. package/dist/interpreter/execute-tool.js +0 -81
  252. package/dist/runtime/agent-harness.js +0 -1170
  253. /package/dist/{interpreter → util}/framework-cast.d.ts +0 -0
  254. /package/dist/{interpreter → util}/framework-cast.js +0 -0
@@ -1,5 +1,5 @@
1
- import { createMessage, estimateTokens } from '../../interpreter/message-helpers';
2
- import { Slot } from '../../types/memory';
1
+ import { Slot } from '../../types/memory.js';
2
+ import { createMessage, estimateTokens } from '../../util/message-helpers.js';
3
3
  /** Default max file size: 1MB */
4
4
  const DEFAULT_MAX_FILE_SIZE = 1024 * 1024;
5
5
  /** Default allowed extensions */
@@ -122,12 +122,20 @@ function simpleHash(content) {
122
122
  // Include content length to reduce collision probability
123
123
  return `${content.length}:${hash.toString(16)}`;
124
124
  }
125
+ function isInputTextPart(value) {
126
+ return (typeof value === 'object' &&
127
+ value !== null &&
128
+ 'type' in value &&
129
+ value.type === 'input_text' &&
130
+ 'text' in value &&
131
+ typeof value.text === 'string');
132
+ }
125
133
  function isInputMessage(item) {
126
134
  return (item.type === 'message' &&
127
135
  'role' in item &&
128
136
  'content' in item &&
129
137
  Array.isArray(item.content) &&
130
- item.content.every((c) => typeof c === 'object' && c !== null && 'type' in c && c.type === 'input_text'));
138
+ item.content.every(isInputTextPart));
131
139
  }
132
140
  function extractTextFromItem(item) {
133
141
  if (!isInputMessage(item)) {
@@ -264,6 +272,293 @@ Score:`;
264
272
  return 50;
265
273
  }
266
274
  }
275
+ function createFileReferenceRuntime(opts) {
276
+ const baseDir = opts?.baseDir ?? currentWorkingDirectory();
277
+ const maxFileSize = opts?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
278
+ const followSymlinks = opts?.followSymlinks ?? false;
279
+ const allowedExtensions = opts?.allowedExtensions ?? DEFAULT_ALLOWED_EXTENSIONS;
280
+ return {
281
+ baseDir,
282
+ slot: opts?.slot ?? Slot.RAG,
283
+ scoringModel: opts?.scoringModel ?? 'anthropic/claude-haiku-4-5-20251001',
284
+ readOpts: {
285
+ maxFileSize,
286
+ followSymlinks,
287
+ baseDir,
288
+ allowedExtensions,
289
+ },
290
+ };
291
+ }
292
+ async function initFileReferenceState(baseDir) {
293
+ return {
294
+ state: {
295
+ files: new Map(),
296
+ baseDir,
297
+ },
298
+ };
299
+ }
300
+ function absolutePathReference(ref) {
301
+ return {
302
+ absolutePath: ref,
303
+ referencePath: ref,
304
+ content: null,
305
+ priority: 0,
306
+ contentHash: '',
307
+ deleted: false,
308
+ tokenCount: 0,
309
+ error: 'ABSOLUTE_PATH: Only relative paths are allowed',
310
+ };
311
+ }
312
+ async function trackNewReference(args) {
313
+ if (isAbsolutePath(args.ref)) {
314
+ return absolutePathReference(args.ref);
315
+ }
316
+ const absolutePath = resolvePath(args.state.baseDir, args.ref);
317
+ const result = await readFileContent(absolutePath, args.readOpts, args.ctx.fs);
318
+ const priority = result.content
319
+ ? await scoreFileRelevance({
320
+ filePath: args.ref,
321
+ fileContent: result.content,
322
+ userQuery: args.userQuery,
323
+ ctx: args.ctx,
324
+ model: args.scoringModel,
325
+ })
326
+ : 0;
327
+ return {
328
+ absolutePath,
329
+ referencePath: args.ref,
330
+ content: result.content,
331
+ priority,
332
+ contentHash: result.content ? simpleHash(result.content) : '',
333
+ deleted: result.deleted,
334
+ tokenCount: result.content ? estimateTokens(result.content) : 0,
335
+ error: result.error,
336
+ };
337
+ }
338
+ async function addNewReferences(args) {
339
+ let hasChanges = false;
340
+ for (const ref of args.refs) {
341
+ if (args.files.has(ref)) {
342
+ continue;
343
+ }
344
+ args.files.set(ref, await trackNewReference({
345
+ ...args,
346
+ ref,
347
+ }));
348
+ hasChanges = true;
349
+ }
350
+ return hasChanges;
351
+ }
352
+ function transformItemReferences(item, refs) {
353
+ if (refs.length === 0 || !isInputMessage(item)) {
354
+ return item;
355
+ }
356
+ const transformedContent = item.content.map((c) => ({
357
+ type: 'input_text',
358
+ text: transformReferencesToAnchors(c.text),
359
+ }));
360
+ return {
361
+ ...item,
362
+ content: transformedContent,
363
+ };
364
+ }
365
+ async function processAppendedItems(args) {
366
+ const transformedItems = [];
367
+ let hasChanges = false;
368
+ let userQuery = '';
369
+ for (const item of args.items) {
370
+ const text = extractTextFromItem(item);
371
+ if (!text) {
372
+ transformedItems.push(item);
373
+ continue;
374
+ }
375
+ if (item.type === 'message' && 'role' in item && item.role === 'user') {
376
+ userQuery = text;
377
+ }
378
+ const refs = extractFileReferences(text);
379
+ const added = await addNewReferences({
380
+ refs,
381
+ files: args.files,
382
+ state: args.state,
383
+ readOpts: args.readOpts,
384
+ ctx: args.ctx,
385
+ userQuery,
386
+ scoringModel: args.scoringModel,
387
+ });
388
+ hasChanges = hasChanges || added;
389
+ transformedItems.push(transformItemReferences(item, refs));
390
+ }
391
+ return {
392
+ transformedItems,
393
+ hasChanges,
394
+ userQuery,
395
+ };
396
+ }
397
+ async function refreshTrackedFile(args) {
398
+ if (args.tracked.error) {
399
+ return null;
400
+ }
401
+ const result = await readFileContent(args.tracked.absolutePath, args.readOpts, args.ctx.fs);
402
+ if (result.deleted && !args.tracked.deleted) {
403
+ return {
404
+ ...args.tracked,
405
+ content: null,
406
+ deleted: true,
407
+ };
408
+ }
409
+ if (result.error && !args.tracked.error) {
410
+ return {
411
+ ...args.tracked,
412
+ content: null,
413
+ error: result.error,
414
+ };
415
+ }
416
+ if (result.deleted || !result.content) {
417
+ return null;
418
+ }
419
+ const newHash = simpleHash(result.content);
420
+ if (newHash === args.tracked.contentHash) {
421
+ return null;
422
+ }
423
+ const priority = args.userQuery
424
+ ? await scoreFileRelevance({
425
+ filePath: args.ref,
426
+ fileContent: result.content,
427
+ userQuery: args.userQuery,
428
+ ctx: args.ctx,
429
+ model: args.scoringModel,
430
+ })
431
+ : args.tracked.priority;
432
+ return {
433
+ ...args.tracked,
434
+ content: result.content,
435
+ contentHash: newHash,
436
+ deleted: false,
437
+ tokenCount: estimateTokens(result.content),
438
+ priority,
439
+ error: undefined,
440
+ };
441
+ }
442
+ async function refreshTrackedFiles(args) {
443
+ let hasChanges = false;
444
+ for (const [ref, tracked] of args.files) {
445
+ const next = await refreshTrackedFile({
446
+ ...args,
447
+ ref,
448
+ tracked,
449
+ });
450
+ if (next === null) {
451
+ continue;
452
+ }
453
+ args.files.set(ref, next);
454
+ hasChanges = true;
455
+ }
456
+ return hasChanges;
457
+ }
458
+ async function onFileReferenceItemAppend(args, runtime) {
459
+ const newFiles = new Map(args.state.files);
460
+ const currentReadOpts = {
461
+ ...runtime.readOpts,
462
+ baseDir: args.state.baseDir,
463
+ };
464
+ const processed = await processAppendedItems({
465
+ items: args.items,
466
+ files: newFiles,
467
+ state: args.state,
468
+ readOpts: currentReadOpts,
469
+ ctx: args.ctx,
470
+ scoringModel: runtime.scoringModel,
471
+ });
472
+ const refreshed = await refreshTrackedFiles({
473
+ files: newFiles,
474
+ readOpts: currentReadOpts,
475
+ ctx: args.ctx,
476
+ userQuery: processed.userQuery,
477
+ scoringModel: runtime.scoringModel,
478
+ });
479
+ return {
480
+ items: processed.transformedItems,
481
+ state: {
482
+ ...args.state,
483
+ files: newFiles,
484
+ },
485
+ rerender: processed.hasChanges || refreshed,
486
+ timing: 'immediate',
487
+ scope: 'self',
488
+ };
489
+ }
490
+ function buildDeletedOrErrorBlock(file) {
491
+ if (file.deleted) {
492
+ return `## ${file.referencePath}\n\n[FILE DELETED: ${file.absolutePath}]`;
493
+ }
494
+ if (file.error) {
495
+ return `## ${file.referencePath}\n\n[ERROR: ${file.error}]`;
496
+ }
497
+ return null;
498
+ }
499
+ function truncateFileContent(file, remainingBudget) {
500
+ if (!file.content || file.tokenCount <= remainingBudget) {
501
+ return file.content ?? '';
502
+ }
503
+ const lines = file.content.split('\n');
504
+ const headLines = Math.floor(lines.length * 0.6);
505
+ const tailLines = Math.floor(lines.length * 0.3);
506
+ const head = lines.slice(0, headLines).join('\n');
507
+ const tail = lines.slice(-tailLines).join('\n');
508
+ return `${head}\n\n... [truncated ${lines.length - headLines - tailLines} lines] ...\n\n${tail}`;
509
+ }
510
+ function buildContentBlock(file, totalTokens, budget) {
511
+ const statusBlock = buildDeletedOrErrorBlock(file);
512
+ if (statusBlock !== null) {
513
+ return totalTokens + estimateTokens(statusBlock) <= budget ? statusBlock : null;
514
+ }
515
+ if (!file.content) {
516
+ return null;
517
+ }
518
+ const header = `## ${file.referencePath}`;
519
+ const headerTokens = estimateTokens(header);
520
+ if (totalTokens + headerTokens >= budget) {
521
+ return null;
522
+ }
523
+ const remainingBudget = budget - totalTokens - headerTokens - 10;
524
+ const contentToInclude = truncateFileContent(file, remainingBudget);
525
+ return `${header}\n\n\`\`\`\n${contentToInclude}\n\`\`\``;
526
+ }
527
+ async function recallFileReferences({ state, budget, }) {
528
+ if (state.files.size === 0) {
529
+ return {
530
+ items: [],
531
+ tokenCount: 0,
532
+ };
533
+ }
534
+ const sortedFiles = [
535
+ ...state.files.values(),
536
+ ].sort((a, b) => b.priority - a.priority);
537
+ const blocks = [];
538
+ let totalTokens = 0;
539
+ for (const file of sortedFiles) {
540
+ const block = buildContentBlock(file, totalTokens, budget);
541
+ if (block === null) {
542
+ continue;
543
+ }
544
+ const blockTokens = estimateTokens(block);
545
+ blocks.push(block);
546
+ totalTokens += blockTokens;
547
+ }
548
+ if (blocks.length === 0) {
549
+ return {
550
+ items: [],
551
+ tokenCount: 0,
552
+ };
553
+ }
554
+ const content = `# Referenced Files\n\n${blocks.join('\n\n')}`;
555
+ return {
556
+ items: [
557
+ createMessage(content, 'developer'),
558
+ ],
559
+ tokenCount: estimateTokens(content),
560
+ };
561
+ }
267
562
  //#endregion
268
563
  //#region Layer Factory
269
564
  /**
@@ -281,257 +576,18 @@ Score:`;
281
576
  * @public
282
577
  */
283
578
  export function fileReference(opts) {
284
- const baseDir = opts?.baseDir ?? currentWorkingDirectory();
285
- const slot = opts?.slot ?? Slot.RAG;
286
- const scoringModel = opts?.scoringModel ?? 'anthropic/claude-haiku-4-5-20251001';
287
- const maxFileSize = opts?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
288
- const followSymlinks = opts?.followSymlinks ?? false;
289
- const allowedExtensions = opts?.allowedExtensions ?? DEFAULT_ALLOWED_EXTENSIONS;
290
- // Pre-compute read options for reuse
291
- const readOpts = {
292
- maxFileSize,
293
- followSymlinks,
294
- baseDir,
295
- allowedExtensions,
296
- };
579
+ const runtime = createFileReferenceRuntime(opts);
297
580
  return {
298
581
  id: 'file-reference',
299
582
  name: 'File Reference',
300
- slot,
583
+ slot: runtime.slot,
301
584
  scope: 'thread',
302
585
  budget: 'auto',
303
586
  rerenderTiming: 'immediate',
304
587
  hooks: {
305
- async init() {
306
- return {
307
- state: {
308
- files: new Map(),
309
- baseDir,
310
- },
311
- };
312
- },
313
- async onItemAppend({ items, ctx, state }) {
314
- const newFiles = new Map(state.files);
315
- let hasChanges = false;
316
- let userQuery = '';
317
- // Update readOpts with current state's baseDir
318
- const currentReadOpts = {
319
- ...readOpts,
320
- baseDir: state.baseDir,
321
- };
322
- // Process each item
323
- const transformedItems = [];
324
- for (const item of items) {
325
- const text = extractTextFromItem(item);
326
- if (!text) {
327
- transformedItems.push(item);
328
- continue;
329
- }
330
- // Extract user query for scoring
331
- if (item.type === 'message' && 'role' in item && item.role === 'user') {
332
- userQuery = text;
333
- }
334
- // Extract file references
335
- const refs = extractFileReferences(text);
336
- // Process new references
337
- for (const ref of refs) {
338
- if (!newFiles.has(ref)) {
339
- // Security: Reject absolute paths - all paths must be relative to baseDir
340
- if (isAbsolutePath(ref)) {
341
- newFiles.set(ref, {
342
- absolutePath: ref,
343
- referencePath: ref,
344
- content: null,
345
- priority: 0,
346
- contentHash: '',
347
- deleted: false,
348
- tokenCount: 0,
349
- error: 'ABSOLUTE_PATH: Only relative paths are allowed',
350
- });
351
- hasChanges = true;
352
- continue;
353
- }
354
- const absolutePath = resolvePath(state.baseDir, ref);
355
- const result = await readFileContent(absolutePath, currentReadOpts, ctx.fs);
356
- const priority = result.content
357
- ? await scoreFileRelevance({
358
- filePath: ref,
359
- fileContent: result.content,
360
- userQuery,
361
- ctx,
362
- model: scoringModel,
363
- })
364
- : 0;
365
- newFiles.set(ref, {
366
- absolutePath,
367
- referencePath: ref,
368
- content: result.content,
369
- priority,
370
- contentHash: result.content ? simpleHash(result.content) : '',
371
- deleted: result.deleted,
372
- tokenCount: result.content ? estimateTokens(result.content) : 0,
373
- error: result.error,
374
- });
375
- hasChanges = true;
376
- }
377
- }
378
- // Transform references to anchor links
379
- if (refs.length > 0 && isInputMessage(item)) {
380
- const transformedContent = item.content.map((c) => {
381
- const part = {
382
- type: 'input_text',
383
- text: transformReferencesToAnchors(c.text),
384
- };
385
- return part;
386
- });
387
- const transformedItem = {
388
- ...item,
389
- content: transformedContent,
390
- };
391
- transformedItems.push(transformedItem);
392
- }
393
- else {
394
- transformedItems.push(item);
395
- }
396
- }
397
- // Check existing files for changes
398
- for (const [ref, tracked] of newFiles) {
399
- // Skip files with errors - they won't change
400
- if (tracked.error) {
401
- continue;
402
- }
403
- const result = await readFileContent(tracked.absolutePath, currentReadOpts, ctx.fs);
404
- if (result.deleted && !tracked.deleted) {
405
- // File was deleted
406
- newFiles.set(ref, {
407
- ...tracked,
408
- content: null,
409
- deleted: true,
410
- });
411
- hasChanges = true;
412
- }
413
- else if (result.error && !tracked.error) {
414
- // File now has error (e.g., permission changed)
415
- newFiles.set(ref, {
416
- ...tracked,
417
- content: null,
418
- error: result.error,
419
- });
420
- hasChanges = true;
421
- }
422
- else if (!result.deleted && result.content) {
423
- const newHash = simpleHash(result.content);
424
- if (newHash !== tracked.contentHash) {
425
- // File content changed
426
- const priority = userQuery
427
- ? await scoreFileRelevance({
428
- filePath: ref,
429
- fileContent: result.content,
430
- userQuery,
431
- ctx,
432
- model: scoringModel,
433
- })
434
- : tracked.priority;
435
- newFiles.set(ref, {
436
- ...tracked,
437
- content: result.content,
438
- contentHash: newHash,
439
- deleted: false,
440
- tokenCount: estimateTokens(result.content),
441
- priority,
442
- error: undefined,
443
- });
444
- hasChanges = true;
445
- }
446
- }
447
- }
448
- return {
449
- items: transformedItems,
450
- state: {
451
- ...state,
452
- files: newFiles,
453
- },
454
- rerender: hasChanges,
455
- timing: 'immediate',
456
- scope: 'self',
457
- };
458
- },
459
- async recall({ state, budget }) {
460
- if (state.files.size === 0) {
461
- return {
462
- items: [],
463
- tokenCount: 0,
464
- };
465
- }
466
- // Sort files by priority (highest first)
467
- const sortedFiles = [
468
- ...state.files.values(),
469
- ].sort((a, b) => b.priority - a.priority);
470
- // Build content blocks within budget
471
- const blocks = [];
472
- let totalTokens = 0;
473
- for (const file of sortedFiles) {
474
- // Handle deleted files
475
- if (file.deleted) {
476
- const block = `## ${file.referencePath}\n\n[FILE DELETED: ${file.absolutePath}]`;
477
- const tokens = estimateTokens(block);
478
- if (totalTokens + tokens <= budget) {
479
- blocks.push(block);
480
- totalTokens += tokens;
481
- }
482
- continue;
483
- }
484
- // Handle files with errors (security, size, permission)
485
- if (file.error) {
486
- const block = `## ${file.referencePath}\n\n[ERROR: ${file.error}]`;
487
- const tokens = estimateTokens(block);
488
- if (totalTokens + tokens <= budget) {
489
- blocks.push(block);
490
- totalTokens += tokens;
491
- }
492
- continue;
493
- }
494
- if (!file.content) {
495
- continue;
496
- }
497
- const header = `## ${file.referencePath}`;
498
- const headerTokens = estimateTokens(header);
499
- if (totalTokens + headerTokens >= budget) {
500
- // Can't fit even the header
501
- break;
502
- }
503
- const remainingBudget = budget - totalTokens - headerTokens - 10; // Buffer for formatting
504
- let contentToInclude = file.content;
505
- if (file.tokenCount > remainingBudget) {
506
- // Truncate content - show head + tail
507
- const lines = file.content.split('\n');
508
- const headLines = Math.floor(lines.length * 0.6);
509
- const tailLines = Math.floor(lines.length * 0.3);
510
- const head = lines.slice(0, headLines).join('\n');
511
- const tail = lines.slice(-tailLines).join('\n');
512
- contentToInclude = `${head}\n\n... [truncated ${lines.length - headLines - tailLines} lines] ...\n\n${tail}`;
513
- }
514
- const block = `${header}\n\n\`\`\`\n${contentToInclude}\n\`\`\``;
515
- const blockTokens = estimateTokens(block);
516
- if (totalTokens + blockTokens <= budget) {
517
- blocks.push(block);
518
- totalTokens += blockTokens;
519
- }
520
- }
521
- if (blocks.length === 0) {
522
- return {
523
- items: [],
524
- tokenCount: 0,
525
- };
526
- }
527
- const content = `# Referenced Files\n\n${blocks.join('\n\n')}`;
528
- return {
529
- items: [
530
- createMessage(content, 'developer'),
531
- ],
532
- tokenCount: estimateTokens(content),
533
- };
534
- },
588
+ init: () => initFileReferenceState(runtime.baseDir),
589
+ onItemAppend: (args) => onFileReferenceItemAppend(args, runtime),
590
+ recall: recallFileReferences,
535
591
  },
536
592
  };
537
593
  }
@@ -12,7 +12,7 @@
12
12
  * 3. Drop any orphan `function_call` / `function_call_output` left at the
13
13
  * slice boundary via `stripUnresolvedToolCalls`.
14
14
  */
15
- import type { MemoryLayer } from '../../types/memory';
15
+ import type { MemoryLayer } from '../../types/memory.js';
16
16
  /** @public Configuration for the {@link historyWindow} layer. */
17
17
  export interface HistoryWindowConfig {
18
18
  /** Cap on the number of trailing items projected to the LLM. Defaults to 40. */
@@ -12,10 +12,10 @@
12
12
  * 3. Drop any orphan `function_call` / `function_call_output` left at the
13
13
  * slice boundary via `stripUnresolvedToolCalls`.
14
14
  */
15
- import { NoeticConfigError } from '../../errors/noetic-config-error';
16
- import { isAssistantMessage, isUserMessage } from '../../interpreter/typeguards';
17
- import { Slot } from '../../types/memory';
18
- import { stripUnresolvedToolCalls } from '../strip-unresolved';
15
+ import { NoeticConfigError } from '../../errors/noetic-config-error.js';
16
+ import { Slot } from '../../types/memory.js';
17
+ import { isAssistantMessage, isUserMessage } from '../../util/message-helpers.js';
18
+ import { stripUnresolvedToolCalls } from '../strip-unresolved.js';
19
19
  //#region Constants
20
20
  const DEFAULT_MAX_ITEMS = 40;
21
21
  const MIN_MAX_ITEMS = 2;
@@ -31,14 +31,14 @@ export declare function observationalMemory(config?: ObservationalMemoryConfig):
31
31
  store: number;
32
32
  };
33
33
  hooks: {
34
- init({ storage }: import("../..").InitParams): Promise<{
34
+ init({ storage }: import("../../index.js").InitParams): Promise<{
35
35
  state: ObservationalState;
36
36
  }>;
37
- recall({ state }: import("../..").RecallParams<ObservationalState>): Promise<{
38
- items: import("../..").InputMessageItem[];
37
+ recall({ state }: import("../../index.js").RecallParams<ObservationalState>): Promise<{
38
+ items: import("../../index.js").InputMessageItem[];
39
39
  tokenCount: number;
40
40
  } | null>;
41
- store({ newItems, state }: import("../..").StoreParams<ObservationalState>): Promise<{
41
+ store({ newItems, state }: import("../../index.js").StoreParams<ObservationalState>): Promise<{
42
42
  state: {
43
43
  buffer: string[];
44
44
  bufferTokens: number;
@@ -46,7 +46,7 @@ export declare function observationalMemory(config?: ObservationalMemoryConfig):
46
46
  version: number;
47
47
  };
48
48
  }>;
49
- onSpawn({ parentState }: import("../..").SpawnParams<ObservationalState>): Promise<{
49
+ onSpawn({ parentState }: import("../../index.js").SpawnParams<ObservationalState>): Promise<{
50
50
  childState: ObservationalState;
51
51
  }>;
52
52
  };
@@ -1,6 +1,5 @@
1
- import { createMessage, estimateTokens } from '../../interpreter/message-helpers';
2
- import { isOutputText } from '../../interpreter/typeguards';
3
- import { Slot } from '../../types/memory';
1
+ import { Slot } from '../../types/memory.js';
2
+ import { createMessage, estimateTokens, isOutputText } from '../../util/message-helpers.js';
4
3
  const DEFAULT_BUFFER_THRESHOLD_TOKENS = 2_000;
5
4
  const DEFAULT_MAX_OBSERVATIONS = 50;
6
5
  /**
@@ -1,5 +1,5 @@
1
- import type { FlowNode } from '../../patterns/flow';
2
- import type { MemoryLayer, MemoryScope } from '../../types/memory';
1
+ import type { MemoryLayer, MemoryScope } from '../../types/memory.js';
2
+ import type { FlowNode } from '../flow-schema.js';
3
3
  export declare const PlanPhase: {
4
4
  readonly Idle: "idle";
5
5
  readonly Planning: "planning";