agentfootprint 3.1.0 → 3.1.2

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 (239) hide show
  1. package/AGENTS.md +66 -47
  2. package/CLAUDE.md +47 -37
  3. package/README.md +27 -12
  4. package/dist/adapters/llm/createProvider.js +3 -2
  5. package/dist/adapters/llm/createProvider.js.map +1 -1
  6. package/dist/adapters/observability/agentcore.js +1 -0
  7. package/dist/adapters/observability/agentcore.js.map +1 -1
  8. package/dist/core/flowchartAsTool.js +5 -5
  9. package/dist/esm/adapters/llm/createProvider.js +3 -2
  10. package/dist/esm/adapters/llm/createProvider.js.map +1 -1
  11. package/dist/esm/adapters/observability/agentcore.js +1 -0
  12. package/dist/esm/adapters/observability/agentcore.js.map +1 -1
  13. package/dist/esm/core/flowchartAsTool.js +5 -5
  14. package/dist/esm/lib/lazyRequire.js +3 -31
  15. package/dist/esm/lib/lazyRequire.js.map +1 -1
  16. package/dist/esm/lib/rag/defineRAG.js +4 -2
  17. package/dist/esm/lib/rag/defineRAG.js.map +1 -1
  18. package/dist/esm/lib/rag/indexDocuments.js +2 -2
  19. package/dist/esm/locales/index.js +3 -3
  20. package/dist/esm/memory/beats/heuristicExtractor.js +1 -1
  21. package/dist/esm/memory/beats/heuristicExtractor.js.map +1 -1
  22. package/dist/esm/memory/beats/index.js +6 -6
  23. package/dist/esm/memory/beats/index.js.map +1 -1
  24. package/dist/esm/memory/beats/llmExtractor.js +1 -1
  25. package/dist/esm/memory/beats/llmExtractor.js.map +1 -1
  26. package/dist/esm/memory/embedding/index.js +4 -4
  27. package/dist/esm/memory/embedding/index.js.map +1 -1
  28. package/dist/esm/memory/entry/index.js +1 -1
  29. package/dist/esm/memory/entry/index.js.map +1 -1
  30. package/dist/esm/memory/facts/extractFacts.js +1 -1
  31. package/dist/esm/memory/facts/extractFacts.js.map +1 -1
  32. package/dist/esm/memory/facts/index.js +7 -7
  33. package/dist/esm/memory/facts/index.js.map +1 -1
  34. package/dist/esm/memory/facts/llmFactExtractor.js +1 -1
  35. package/dist/esm/memory/facts/llmFactExtractor.js.map +1 -1
  36. package/dist/esm/memory/facts/loadFacts.js +1 -1
  37. package/dist/esm/memory/facts/loadFacts.js.map +1 -1
  38. package/dist/esm/memory/facts/patternFactExtractor.js +1 -1
  39. package/dist/esm/memory/facts/patternFactExtractor.js.map +1 -1
  40. package/dist/esm/memory/identity/index.js +1 -1
  41. package/dist/esm/memory/identity/index.js.map +1 -1
  42. package/dist/esm/memory/pipeline/auto.js +2 -2
  43. package/dist/esm/memory/pipeline/auto.js.map +1 -1
  44. package/dist/esm/memory/pipeline/default.js +4 -4
  45. package/dist/esm/memory/pipeline/default.js.map +1 -1
  46. package/dist/esm/memory/pipeline/ephemeral.js +3 -3
  47. package/dist/esm/memory/pipeline/ephemeral.js.map +1 -1
  48. package/dist/esm/memory/pipeline/fact.js +5 -5
  49. package/dist/esm/memory/pipeline/fact.js.map +1 -1
  50. package/dist/esm/memory/pipeline/index.js +6 -6
  51. package/dist/esm/memory/pipeline/index.js.map +1 -1
  52. package/dist/esm/memory/pipeline/narrative.js +6 -6
  53. package/dist/esm/memory/pipeline/narrative.js.map +1 -1
  54. package/dist/esm/memory/pipeline/semantic.js +5 -5
  55. package/dist/esm/memory/pipeline/semantic.js.map +1 -1
  56. package/dist/esm/memory/stages/index.js +6 -6
  57. package/dist/esm/memory/stages/index.js.map +1 -1
  58. package/dist/esm/memory/stages/pickByBudget.js +1 -1
  59. package/dist/esm/memory/stages/pickByBudget.js.map +1 -1
  60. package/dist/esm/memory/store/InMemoryStore.js +2 -2
  61. package/dist/esm/memory/store/InMemoryStore.js.map +1 -1
  62. package/dist/esm/memory/store/index.js +1 -1
  63. package/dist/esm/memory/store/index.js.map +1 -1
  64. package/dist/esm/memory/wire/index.js +1 -1
  65. package/dist/esm/memory/wire/index.js.map +1 -1
  66. package/dist/esm/package.json +1 -0
  67. package/dist/esm/recorders/core/typedEmit.js +1 -1
  68. package/dist/esm/recorders/observability/BoundaryRecorder.js +3 -2
  69. package/dist/esm/recorders/observability/BoundaryRecorder.js.map +1 -1
  70. package/dist/esm/resilience/withRetry.js +3 -0
  71. package/dist/esm/resilience/withRetry.js.map +1 -1
  72. package/dist/esm/strategies/compose.js +2 -2
  73. package/dist/esm/tool-providers/skillScopedTools.js +3 -3
  74. package/dist/lib/lazyRequire.js +6 -0
  75. package/dist/lib/lazyRequire.js.map +1 -1
  76. package/dist/lib/rag/defineRAG.js +4 -2
  77. package/dist/lib/rag/defineRAG.js.map +1 -1
  78. package/dist/lib/rag/indexDocuments.js +2 -2
  79. package/dist/locales/index.js +3 -3
  80. package/dist/memory/beats/heuristicExtractor.js +4 -4
  81. package/dist/memory/beats/heuristicExtractor.js.map +1 -1
  82. package/dist/memory/beats/index.js +13 -13
  83. package/dist/memory/beats/index.js.map +1 -1
  84. package/dist/memory/beats/llmExtractor.js +2 -2
  85. package/dist/memory/beats/llmExtractor.js.map +1 -1
  86. package/dist/memory/embedding/index.js +8 -8
  87. package/dist/memory/embedding/index.js.map +1 -1
  88. package/dist/memory/entry/index.js +3 -3
  89. package/dist/memory/entry/index.js.map +1 -1
  90. package/dist/memory/facts/extractFacts.js +2 -2
  91. package/dist/memory/facts/extractFacts.js.map +1 -1
  92. package/dist/memory/facts/index.js +17 -17
  93. package/dist/memory/facts/index.js.map +1 -1
  94. package/dist/memory/facts/llmFactExtractor.js +2 -2
  95. package/dist/memory/facts/llmFactExtractor.js.map +1 -1
  96. package/dist/memory/facts/loadFacts.js +2 -2
  97. package/dist/memory/facts/loadFacts.js.map +1 -1
  98. package/dist/memory/facts/patternFactExtractor.js +2 -2
  99. package/dist/memory/facts/patternFactExtractor.js.map +1 -1
  100. package/dist/memory/identity/index.js +2 -2
  101. package/dist/memory/identity/index.js.map +1 -1
  102. package/dist/memory/pipeline/auto.js +11 -11
  103. package/dist/memory/pipeline/auto.js.map +1 -1
  104. package/dist/memory/pipeline/default.js +8 -8
  105. package/dist/memory/pipeline/default.js.map +1 -1
  106. package/dist/memory/pipeline/ephemeral.js +6 -6
  107. package/dist/memory/pipeline/ephemeral.js.map +1 -1
  108. package/dist/memory/pipeline/fact.js +11 -11
  109. package/dist/memory/pipeline/fact.js.map +1 -1
  110. package/dist/memory/pipeline/index.js +12 -12
  111. package/dist/memory/pipeline/index.js.map +1 -1
  112. package/dist/memory/pipeline/narrative.js +12 -12
  113. package/dist/memory/pipeline/narrative.js.map +1 -1
  114. package/dist/memory/pipeline/semantic.js +10 -10
  115. package/dist/memory/pipeline/semantic.js.map +1 -1
  116. package/dist/memory/stages/index.js +13 -13
  117. package/dist/memory/stages/index.js.map +1 -1
  118. package/dist/memory/stages/pickByBudget.js +3 -3
  119. package/dist/memory/stages/pickByBudget.js.map +1 -1
  120. package/dist/memory/store/InMemoryStore.js +5 -5
  121. package/dist/memory/store/InMemoryStore.js.map +1 -1
  122. package/dist/memory/store/index.js +2 -2
  123. package/dist/memory/store/index.js.map +1 -1
  124. package/dist/memory/wire/index.js +4 -4
  125. package/dist/memory/wire/index.js.map +1 -1
  126. package/dist/recorders/core/typedEmit.js +1 -1
  127. package/dist/recorders/observability/BoundaryRecorder.js +3 -2
  128. package/dist/recorders/observability/BoundaryRecorder.js.map +1 -1
  129. package/dist/resilience/withRetry.js +3 -0
  130. package/dist/resilience/withRetry.js.map +1 -1
  131. package/dist/strategies/compose.js +2 -2
  132. package/dist/tool-providers/skillScopedTools.js +3 -3
  133. package/dist/types/adapters/llm/createProvider.d.ts +3 -2
  134. package/dist/types/adapters/llm/createProvider.d.ts.map +1 -1
  135. package/dist/types/adapters/observability/agentcore.d.ts +1 -0
  136. package/dist/types/adapters/observability/agentcore.d.ts.map +1 -1
  137. package/dist/types/core/flowchartAsTool.d.ts +5 -5
  138. package/dist/types/lib/lazyRequire.d.ts.map +1 -1
  139. package/dist/types/lib/rag/defineRAG.d.ts +4 -2
  140. package/dist/types/lib/rag/defineRAG.d.ts.map +1 -1
  141. package/dist/types/lib/rag/indexDocuments.d.ts +2 -2
  142. package/dist/types/locales/index.d.ts +3 -3
  143. package/dist/types/memory/beats/extractBeats.d.ts +4 -4
  144. package/dist/types/memory/beats/extractBeats.d.ts.map +1 -1
  145. package/dist/types/memory/beats/extractor.d.ts +2 -2
  146. package/dist/types/memory/beats/extractor.d.ts.map +1 -1
  147. package/dist/types/memory/beats/formatAsNarrative.d.ts +1 -1
  148. package/dist/types/memory/beats/formatAsNarrative.d.ts.map +1 -1
  149. package/dist/types/memory/beats/heuristicExtractor.d.ts +1 -1
  150. package/dist/types/memory/beats/heuristicExtractor.d.ts.map +1 -1
  151. package/dist/types/memory/beats/index.d.ts +12 -12
  152. package/dist/types/memory/beats/index.d.ts.map +1 -1
  153. package/dist/types/memory/beats/llmExtractor.d.ts +2 -2
  154. package/dist/types/memory/beats/llmExtractor.d.ts.map +1 -1
  155. package/dist/types/memory/beats/writeBeats.d.ts +2 -2
  156. package/dist/types/memory/beats/writeBeats.d.ts.map +1 -1
  157. package/dist/types/memory/embedding/embedMessages.d.ts +3 -3
  158. package/dist/types/memory/embedding/embedMessages.d.ts.map +1 -1
  159. package/dist/types/memory/embedding/index.d.ts +8 -8
  160. package/dist/types/memory/embedding/index.d.ts.map +1 -1
  161. package/dist/types/memory/embedding/loadRelevant.d.ts +3 -3
  162. package/dist/types/memory/embedding/loadRelevant.d.ts.map +1 -1
  163. package/dist/types/memory/embedding/mockEmbedder.d.ts +1 -1
  164. package/dist/types/memory/embedding/mockEmbedder.d.ts.map +1 -1
  165. package/dist/types/memory/entry/decay.d.ts +1 -1
  166. package/dist/types/memory/entry/decay.d.ts.map +1 -1
  167. package/dist/types/memory/entry/index.d.ts +2 -2
  168. package/dist/types/memory/entry/index.d.ts.map +1 -1
  169. package/dist/types/memory/entry/types.d.ts +1 -1
  170. package/dist/types/memory/entry/types.d.ts.map +1 -1
  171. package/dist/types/memory/facts/extractFacts.d.ts +4 -4
  172. package/dist/types/memory/facts/extractFacts.d.ts.map +1 -1
  173. package/dist/types/memory/facts/extractor.d.ts +2 -2
  174. package/dist/types/memory/facts/extractor.d.ts.map +1 -1
  175. package/dist/types/memory/facts/formatFacts.d.ts +3 -3
  176. package/dist/types/memory/facts/formatFacts.d.ts.map +1 -1
  177. package/dist/types/memory/facts/index.d.ts +14 -14
  178. package/dist/types/memory/facts/index.d.ts.map +1 -1
  179. package/dist/types/memory/facts/llmFactExtractor.d.ts +2 -2
  180. package/dist/types/memory/facts/llmFactExtractor.d.ts.map +1 -1
  181. package/dist/types/memory/facts/loadFacts.d.ts +2 -2
  182. package/dist/types/memory/facts/loadFacts.d.ts.map +1 -1
  183. package/dist/types/memory/facts/patternFactExtractor.d.ts +1 -1
  184. package/dist/types/memory/facts/patternFactExtractor.d.ts.map +1 -1
  185. package/dist/types/memory/facts/writeFacts.d.ts +2 -2
  186. package/dist/types/memory/facts/writeFacts.d.ts.map +1 -1
  187. package/dist/types/memory/identity/index.d.ts +2 -2
  188. package/dist/types/memory/identity/index.d.ts.map +1 -1
  189. package/dist/types/memory/pipeline/auto.d.ts +6 -6
  190. package/dist/types/memory/pipeline/auto.d.ts.map +1 -1
  191. package/dist/types/memory/pipeline/default.d.ts +2 -2
  192. package/dist/types/memory/pipeline/default.d.ts.map +1 -1
  193. package/dist/types/memory/pipeline/ephemeral.d.ts +2 -2
  194. package/dist/types/memory/pipeline/ephemeral.d.ts.map +1 -1
  195. package/dist/types/memory/pipeline/fact.d.ts +3 -3
  196. package/dist/types/memory/pipeline/fact.d.ts.map +1 -1
  197. package/dist/types/memory/pipeline/index.d.ts +13 -13
  198. package/dist/types/memory/pipeline/index.d.ts.map +1 -1
  199. package/dist/types/memory/pipeline/narrative.d.ts +3 -3
  200. package/dist/types/memory/pipeline/narrative.d.ts.map +1 -1
  201. package/dist/types/memory/pipeline/semantic.d.ts +3 -3
  202. package/dist/types/memory/pipeline/semantic.d.ts.map +1 -1
  203. package/dist/types/memory/pipeline/types.d.ts +1 -1
  204. package/dist/types/memory/pipeline/types.d.ts.map +1 -1
  205. package/dist/types/memory/stages/formatDefault.d.ts +3 -3
  206. package/dist/types/memory/stages/formatDefault.d.ts.map +1 -1
  207. package/dist/types/memory/stages/index.d.ts +13 -13
  208. package/dist/types/memory/stages/index.d.ts.map +1 -1
  209. package/dist/types/memory/stages/loadRecent.d.ts +2 -2
  210. package/dist/types/memory/stages/loadRecent.d.ts.map +1 -1
  211. package/dist/types/memory/stages/pickByBudget.d.ts +2 -2
  212. package/dist/types/memory/stages/pickByBudget.d.ts.map +1 -1
  213. package/dist/types/memory/stages/summarize.d.ts +2 -2
  214. package/dist/types/memory/stages/summarize.d.ts.map +1 -1
  215. package/dist/types/memory/stages/tokenize.d.ts +1 -1
  216. package/dist/types/memory/stages/tokenize.d.ts.map +1 -1
  217. package/dist/types/memory/stages/types.d.ts +3 -3
  218. package/dist/types/memory/stages/types.d.ts.map +1 -1
  219. package/dist/types/memory/stages/writeMessages.d.ts +3 -3
  220. package/dist/types/memory/stages/writeMessages.d.ts.map +1 -1
  221. package/dist/types/memory/store/InMemoryStore.d.ts +3 -3
  222. package/dist/types/memory/store/InMemoryStore.d.ts.map +1 -1
  223. package/dist/types/memory/store/index.d.ts +2 -2
  224. package/dist/types/memory/store/index.d.ts.map +1 -1
  225. package/dist/types/memory/store/types.d.ts +2 -2
  226. package/dist/types/memory/store/types.d.ts.map +1 -1
  227. package/dist/types/memory/wire/index.d.ts +2 -2
  228. package/dist/types/memory/wire/index.d.ts.map +1 -1
  229. package/dist/types/memory/wire/mountMemoryPipeline.d.ts +1 -1
  230. package/dist/types/memory/wire/mountMemoryPipeline.d.ts.map +1 -1
  231. package/dist/types/recorders/core/typedEmit.d.ts +1 -1
  232. package/dist/types/recorders/observability/BoundaryRecorder.d.ts +3 -2
  233. package/dist/types/recorders/observability/BoundaryRecorder.d.ts.map +1 -1
  234. package/dist/types/recorders/observability/observeRunId.d.ts +1 -1
  235. package/dist/types/resilience/withRetry.d.ts +3 -0
  236. package/dist/types/resilience/withRetry.d.ts.map +1 -1
  237. package/dist/types/strategies/compose.d.ts +2 -2
  238. package/dist/types/tool-providers/skillScopedTools.d.ts +3 -3
  239. package/package.json +7 -3
package/AGENTS.md CHANGED
@@ -44,7 +44,9 @@ const provider = mock({ reply: 'Refunds take 3 business days.' });
44
44
 
45
45
  // Inline-mocked tool — no real backend yet.
46
46
  const lookup = defineTool({
47
- schema: { name: 'lookup', description: '...', inputSchema: {} },
47
+ name: 'lookup',
48
+ description: '...',
49
+ inputSchema: { type: 'object', properties: {} },
48
50
  execute: async () => 'mock data',
49
51
  });
50
52
 
@@ -148,7 +150,8 @@ agent.rag(docs);
148
150
  ### Agent (ReAct primitive)
149
151
 
150
152
  ```typescript
151
- import { Agent, defineTool, anthropic } from 'agentfootprint';
153
+ import { Agent, defineTool } from 'agentfootprint';
154
+ import { anthropic } from 'agentfootprint/llm-providers';
152
155
 
153
156
  const agent = Agent.create({
154
157
  provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }),
@@ -172,7 +175,8 @@ Builder methods:
172
175
  ### LLMCall (one-shot primitive)
173
176
 
174
177
  ```typescript
175
- import { LLMCall, anthropic } from 'agentfootprint';
178
+ import { LLMCall } from 'agentfootprint';
179
+ import { anthropic } from 'agentfootprint/llm-providers';
176
180
 
177
181
  const call = LLMCall.create({ provider: anthropic(...), model: 'claude-sonnet-4-5-20250929' })
178
182
  .system('You are a terse assistant.')
@@ -187,14 +191,12 @@ const answer = await call.run({ message: 'Summarize: ...' });
187
191
  import { defineTool } from 'agentfootprint';
188
192
 
189
193
  const weather = defineTool({
190
- schema: {
191
- name: 'weather',
192
- description: 'Current weather for a city.',
193
- inputSchema: {
194
- type: 'object',
195
- properties: { city: { type: 'string' } },
196
- required: ['city'],
197
- },
194
+ name: 'weather',
195
+ description: 'Current weather for a city.',
196
+ inputSchema: {
197
+ type: 'object',
198
+ properties: { city: { type: 'string' } },
199
+ required: ['city'],
198
200
  },
199
201
  execute: async (args) => `${(args as { city: string }).city}: 72°F`,
200
202
  });
@@ -330,33 +332,34 @@ There is **no** `MultiAgentSystem` class. Multi-agent = compositions of single A
330
332
  ```typescript
331
333
  import { Sequence, Parallel, Conditional, Loop } from 'agentfootprint';
332
334
 
333
- // Output flows downstream
335
+ // Output flows downstream — every step needs an id + a runner
334
336
  const pipeline = Sequence.create()
335
- .step(researcher) // each step is itself an Agent / LLMCall / runner
336
- .step(writer)
337
- .step(editor)
337
+ .step('research', researcher) // each runner is an Agent / LLMCall / composition
338
+ .step('write', writer)
339
+ .step('edit', editor)
338
340
  .build();
339
341
 
340
- // Multi-perspective with merge
342
+ // Multi-perspective with LLM merge — branches need ids; rank via mergeWithLLM
341
343
  const tot = Parallel.create()
342
- .branch(thoughtAgent)
343
- .branch(thoughtAgent)
344
- .branch(thoughtAgent)
345
- .merge(rankerLLM)
344
+ .branch('a', thoughtAgent)
345
+ .branch('b', thoughtAgent)
346
+ .branch('c', thoughtAgent)
347
+ .mergeWithLLM({ provider, model: 'mock', prompt: 'Pick the best answer.' })
346
348
  .build();
349
+ // (or .mergeWithFn((results) => ...) to merge in code)
347
350
 
348
- // Predicate-based routing
351
+ // Predicate-based routing — .when(id, predicate, runner); .otherwise is mandatory
349
352
  const triage = Conditional.create()
350
- .when((ctx) => ctx.intent === 'billing', billingAgent)
351
- .when((ctx) => ctx.intent === 'tech', techAgent)
352
- .otherwise(generalAgent)
353
+ .when('billing', (input) => /refund|invoice/i.test(input.message), billingAgent)
354
+ .when('tech', (input) => /error|bug/i.test(input.message), techAgent)
355
+ .otherwise('general', generalAgent)
353
356
  .build();
354
357
 
355
- // Iterate with budget
358
+ // Iterate with a REQUIRED budget — .repeat(body) + .times(n) / .forAtMost(ms) / .until(guard)
356
359
  const refine = Loop.create()
357
- .body(critiqueAgent)
358
- .untilGuard((ctx) => ctx.qualityScore > 0.9)
359
- .maxIterations(5)
360
+ .repeat(critiqueAgent)
361
+ .until(({ iteration, latestOutput }) => latestOutput.includes('DONE'))
362
+ .times(5)
360
363
  .build();
361
364
  ```
362
365
 
@@ -377,7 +380,9 @@ Browse [`examples/patterns/`](examples/patterns/) — every pattern is a runnabl
377
380
  ### Providers
378
381
 
379
382
  ```typescript
380
- import { anthropic, openai, bedrock, ollama, mock } from 'agentfootprint';
383
+ import { mock } from 'agentfootprint';
384
+ // Vendor-SDK providers (lazy peer-deps) live on the dedicated subpath:
385
+ import { anthropic, openai, bedrock, ollama } from 'agentfootprint/llm-providers';
381
386
 
382
387
  // Adapter-swap testing: same agent, different provider, $0 in CI
383
388
  const provider = process.env.NODE_ENV === 'production'
@@ -385,7 +390,12 @@ const provider = process.env.NODE_ENV === 'production'
385
390
  : mock({ reply: 'test response' });
386
391
  ```
387
392
 
388
- Every provider implements the same `LLMProvider` interface. Browser variants exist for client-side use.
393
+ Every provider implements the same `LLMProvider` interface. `mock`,
394
+ `browserAnthropic`, `browserOpenai`, and `createProvider` ship on the main
395
+ barrel; the vendor-SDK-backed providers (`anthropic` · `openai` · `bedrock`
396
+ · `ollama`) live ONLY at `agentfootprint/llm-providers` (legacy alias:
397
+ `agentfootprint/providers`) so bundlers never walk their lazy peer-dep
398
+ requires. Browser variants exist for client-side use.
389
399
 
390
400
  ### Pause / Resume (Human-in-the-Loop)
391
401
 
@@ -393,8 +403,13 @@ Every provider implements the same `LLMProvider` interface. Browser variants exi
393
403
  import { askHuman, pauseHere, isPaused } from 'agentfootprint';
394
404
 
395
405
  const approveTool = defineTool({
396
- schema: { name: 'approve', description: 'Ask a human.', inputSchema: { ... } },
397
- execute: askHuman({ severity: 'high' }),
406
+ name: 'approve',
407
+ description: 'Ask a human.',
408
+ inputSchema: { type: 'object', properties: { amount: { type: 'number' } } },
409
+ // askHuman() / pauseHere() throw a PauseRequest — CALL them inside
410
+ // execute, don't pass them as the execute function.
411
+ execute: async (args) =>
412
+ askHuman({ question: `Approve $${(args as { amount: number }).amount}?` }),
398
413
  });
399
414
 
400
415
  const result = await agent.run({ message: 'Refund $500?' });
@@ -408,14 +423,20 @@ if (isPaused(result)) {
408
423
  ### Resilience
409
424
 
410
425
  ```typescript
411
- import { withRetry, withFallback, resilientProvider } from 'agentfootprint';
426
+ import { withRetry, withFallback, fallbackProvider, withCircuitBreaker } from 'agentfootprint/resilience';
427
+ import { anthropic, openai, ollama } from 'agentfootprint/llm-providers';
412
428
 
413
- const reliable = withRetry(provider, { maxRetries: 3 });
429
+ const reliable = withRetry(provider, { maxAttempts: 3 });
414
430
  const resilient = withFallback(primary, fallback);
415
- const chain = resilientProvider([anthropic({...}), openai({...}), ollama({...})]);
431
+ const chain = fallbackProvider(anthropic({...}), openai({...}), ollama({...}));
432
+ const guarded = withCircuitBreaker(provider);
416
433
  ```
417
434
 
418
- ### Observability 47 typed events × 13 domains
435
+ Resilience decorators live on the `agentfootprint/resilience` subpath
436
+ (not the main barrel). Each preserves the `LLMProvider` interface and
437
+ stacks freely.
438
+
439
+ ### Observability — 59 typed events across 16 domains
419
440
 
420
441
  ```typescript
421
442
  agent.on('agentfootprint.context.injected', (e) =>
@@ -440,7 +461,7 @@ Recorders (auto-attached when relevant builder method is called):
440
461
 
441
462
  - ❌ **Don't ship a `ReflexionAgent` class.** Compose `Sequence(Agent, critique-LLM, Agent)`.
442
463
  - ❌ **Don't use `agent.run('string')`** — use `agent.run({ message: '...', identity? })`.
443
- - ❌ **Don't import from stale subpaths** like `'agentfootprint/instructions'`, `'agentfootprint/observe'`, `'agentfootprint/security'`. Top-level barrel covers it: `from 'agentfootprint'`.
464
+ - ❌ **Don't import from non-existent subpaths** like `'agentfootprint/instructions'` — the injection factories live on the main barrel (or `'agentfootprint/injection-engine'`). NOTE: `'agentfootprint/observe'`, `'agentfootprint/security'`, `'agentfootprint/resilience'`, `'agentfootprint/llm-providers'`, `'agentfootprint/memory'`, `'agentfootprint/tool-providers'`, `'agentfootprint/locales'` ARE real subpaths — some surfaces (vendor providers, resilience decorators) live ONLY there, not on the main barrel.
444
465
  - ❌ **Don't use `.memoryPipeline(pipeline)`** — that's the v1 API. Use `.memory(defineMemory({...}))`.
445
466
  - ❌ **Don't fall back when TopK threshold returns nothing.** Strict semantics: garbage past context > none is wrong.
446
467
  - ❌ **Don't store closures or class instances in scope** — TransactionBuffer can't clone functions. Memory-store entries serialize to JSON.
@@ -471,10 +492,10 @@ Recorders (auto-attached when relevant builder method is called):
471
492
  ## Build & Test
472
493
 
473
494
  ```bash
474
- npm install agentfootprint footprintjs
475
- npm test # vitest run — 1100+ tests
495
+ npm install agentfootprint footprintjs # footprintjs ^6 is a peer dependency
496
+ npm test # vitest run
476
497
  npm run example examples/... # run a single example end-to-end
477
- npm run examples:run-all # run every example (33 of them)
498
+ npm run test:examples # typecheck + run every example
478
499
  ```
479
500
 
480
501
  ## Package layout
@@ -489,10 +510,10 @@ src/
489
510
  ├── memory/ — defineMemory + 4 types × 7 strategies + InMemoryStore + Causal
490
511
  ├── adapters/llm/ — Anthropic, OpenAI, Bedrock, Ollama, Browser variants, Mock
491
512
  ├── recorders/ — context, stream, agent, cost, skill, permission, eval, memory
492
- ├── resilience/ — withRetry, withFallback, resilientProvider
513
+ ├── resilience/ — withRetry, withFallback, fallbackProvider, withCircuitBreaker
493
514
  └── stream.ts — SSE formatter
494
515
 
495
- examples/ — 33 runnable end-to-end tests organized by DNA layer
516
+ examples/ — runnable end-to-end tests organized by DNA layer
496
517
  ├── core/ — primitives
497
518
  ├── core-flow/ — compositions
498
519
  ├── patterns/ — canonical recipes
@@ -503,10 +524,8 @@ examples/ — 33 runnable end-to-end tests organized by DNA layer
503
524
 
504
525
  ## Roadmap (informs what to defer)
505
526
 
506
- - **v2.0 (current)** — primitives + compositions + InjectionEngine + Memory (incl. Causal) + 6 providers + 33 examples
507
- - **v2.1** — RAG flavor (`defineRAG`) · Redis memory adapter · MCP integration · CircuitBreaker · 3-tier output fallback
508
- - **v2.2** — Governance (Policy + BudgetTracker) · DynamoDB / Postgres / Pinecone adapters
509
- - **v2.3** — Causal training-data exports (SFT / DPO / process-RL)
510
- - **v2.4+** — Deep Agents · A2A protocol · Lens UI integration
527
+ - **v3.1 (current)** — primitives + compositions + InjectionEngine + Memory (incl. Causal) + providers + RAG (`defineRAG`) + MCP (`mcpClient`) + Redis/AgentCore memory adapters + resilience (retry / fallback / circuit breaker) + Permission policy + observability subsystem
528
+ - **Shipped since v2.0** — RAG flavor · Redis memory adapter · MCP integration · CircuitBreaker · governance (PermissionPolicy)
529
+ - **Planned** — Causal training-data exports (SFT / DPO / process-RL) · DynamoDB / Postgres / Pinecone adapters · Deep Agents · A2A protocol · Lens UI integration
511
530
 
512
531
  When in doubt — read [`examples/`](examples/), every file is a runnable spec.
package/CLAUDE.md CHANGED
@@ -54,7 +54,7 @@ await agent.run({ message: 'How long does a refund take?' });
54
54
 
55
55
  | Boundary | Mock for development | Production swap |
56
56
  |---|---|---|
57
- | LLM provider | `mock({ reply })` · `mock({ replies })` for scripted ReAct | `anthropic()` · `openai()` · `bedrock()` · `ollama()` |
57
+ | LLM provider | `mock({ reply })` · `mock({ replies })` for scripted ReAct | `anthropic()` · `openai()` · `bedrock()` · `ollama()` (from `agentfootprint/llm-providers`) |
58
58
  | Embedder | `mockEmbedder()` | OpenAI / Cohere / Bedrock embedder factory |
59
59
  | Memory store | `InMemoryStore` | `RedisStore` (`agentfootprint/memory-redis`) · `AgentCoreStore` (`agentfootprint/memory-agentcore`) · DynamoDB / Postgres / Pinecone (planned) |
60
60
  | MCP server | `mockMcpClient({ tools })` — in-memory, no SDK | `mcpClient({ transport })` to a real server |
@@ -148,7 +148,8 @@ agent.rag(docs);
148
148
  ### Agent (ReAct primitive)
149
149
 
150
150
  ```typescript
151
- import { Agent, defineTool, anthropic } from 'agentfootprint';
151
+ import { Agent, defineTool } from 'agentfootprint';
152
+ import { anthropic } from 'agentfootprint/llm-providers';
152
153
 
153
154
  const agent = Agent.create({
154
155
  provider: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }),
@@ -172,7 +173,8 @@ Builder methods:
172
173
  ### LLMCall (one-shot primitive)
173
174
 
174
175
  ```typescript
175
- import { LLMCall, anthropic } from 'agentfootprint';
176
+ import { LLMCall } from 'agentfootprint';
177
+ import { anthropic } from 'agentfootprint/llm-providers';
176
178
 
177
179
  const call = LLMCall.create({ provider: anthropic(...), model: 'claude-sonnet-4-5-20250929' })
178
180
  .system('You are a terse assistant.')
@@ -330,33 +332,35 @@ There is **no** `MultiAgentSystem` class. Multi-agent = compositions of single A
330
332
  ```typescript
331
333
  import { Sequence, Parallel, Conditional, Loop } from 'agentfootprint';
332
334
 
333
- // Output flows downstream
335
+ // Output flows downstream — each step takes an id + a runner (Agent / LLMCall / runner)
334
336
  const pipeline = Sequence.create()
335
- .step(researcher) // each step is itself an Agent / LLMCall / runner
336
- .step(writer)
337
- .step(editor)
337
+ .step('research', researcher)
338
+ .step('write', writer)
339
+ .step('edit', editor)
338
340
  .build();
339
341
 
340
- // Multi-perspective with merge
342
+ // Multi-perspective with LLM merge — each branch takes an id + a runner
341
343
  const tot = Parallel.create()
342
- .branch(thoughtAgent)
343
- .branch(thoughtAgent)
344
- .branch(thoughtAgent)
345
- .merge(rankerLLM)
344
+ .branch('t1', thoughtAgent)
345
+ .branch('t2', thoughtAgent)
346
+ .branch('t3', thoughtAgent)
347
+ .mergeWithLLM({ provider, model: 'claude-sonnet-4-5-20250929', prompt: 'Rank and synthesize.' })
346
348
  .build();
347
349
 
348
- // Predicate-based routing
350
+ // Predicate-based routing — .when(id, predicate, runner); exactly one .otherwise(id, runner).
351
+ // predicate receives ConditionalInput { message }.
349
352
  const triage = Conditional.create()
350
- .when((ctx) => ctx.intent === 'billing', billingAgent)
351
- .when((ctx) => ctx.intent === 'tech', techAgent)
352
- .otherwise(generalAgent)
353
+ .when('billing', (input) => /refund|invoice/i.test(input.message), billingAgent)
354
+ .when('tech', (input) => /error|bug/i.test(input.message), techAgent)
355
+ .otherwise('general', generalAgent)
353
356
  .build();
354
357
 
355
- // Iterate with budget
358
+ // Iterate with budget — .repeat(runner) sets the body, .times(n) caps iterations.
359
+ // until() guard receives { iteration, latestOutput, startMs }.
356
360
  const refine = Loop.create()
357
- .body(critiqueAgent)
358
- .untilGuard((ctx) => ctx.qualityScore > 0.9)
359
- .maxIterations(5)
361
+ .repeat(critiqueAgent)
362
+ .until((ctx) => /done/i.test(ctx.latestOutput))
363
+ .times(5)
360
364
  .build();
361
365
  ```
362
366
 
@@ -377,7 +381,8 @@ Browse [`examples/patterns/`](examples/patterns/) — every pattern is a runnabl
377
381
  ### Providers
378
382
 
379
383
  ```typescript
380
- import { anthropic, openai, bedrock, ollama, mock } from 'agentfootprint';
384
+ import { mock } from 'agentfootprint'; // zero-peer-dep, on the main barrel
385
+ import { anthropic, openai, bedrock, ollama } from 'agentfootprint/llm-providers'; // vendor-SDK-backed
381
386
 
382
387
  // Adapter-swap testing: same agent, different provider, $0 in CI
383
388
  const provider = process.env.NODE_ENV === 'production'
@@ -385,7 +390,7 @@ const provider = process.env.NODE_ENV === 'production'
385
390
  : mock({ reply: 'test response' });
386
391
  ```
387
392
 
388
- Every provider implements the same `LLMProvider` interface. Browser variants exist for client-side use.
393
+ Every provider implements the same `LLMProvider` interface. The vendor-SDK providers (`anthropic`, `openai`, `bedrock`, `ollama`, plus their `*Provider` classes) live ONLY at `agentfootprint/llm-providers` (legacy alias `agentfootprint/providers`) — kept off the main barrel so bundlers never walk the lazy peer-dep requires. The main barrel exports the zero-peer-dep providers: `mock`, `browserAnthropic`, `browserOpenai`, and `createProvider`.
389
394
 
390
395
  ### Pause / Resume (Human-in-the-Loop)
391
396
 
@@ -394,7 +399,12 @@ import { askHuman, pauseHere, isPaused } from 'agentfootprint';
394
399
 
395
400
  const approveTool = defineTool({
396
401
  schema: { name: 'approve', description: 'Ask a human.', inputSchema: { ... } },
397
- execute: askHuman({ severity: 'high' }),
402
+ // askHuman()/pauseHere() THROW a PauseRequest — call them INSIDE execute,
403
+ // do not assign them as the execute function.
404
+ execute: async (args) => {
405
+ askHuman({ question: `Approve this action?`, risk: 'high' });
406
+ return ''; // unreachable — askHuman always throws
407
+ },
398
408
  });
399
409
 
400
410
  const result = await agent.run({ message: 'Refund $500?' });
@@ -408,14 +418,17 @@ if (isPaused(result)) {
408
418
  ### Resilience
409
419
 
410
420
  ```typescript
411
- import { withRetry, withFallback, resilientProvider } from 'agentfootprint';
421
+ import { withRetry, withFallback, fallbackProvider } from 'agentfootprint/resilience';
422
+ import { anthropic, openai, ollama } from 'agentfootprint/llm-providers';
412
423
 
413
- const reliable = withRetry(provider, { maxRetries: 3 });
424
+ const reliable = withRetry(provider, { maxAttempts: 3 });
414
425
  const resilient = withFallback(primary, fallback);
415
- const chain = resilientProvider([anthropic({...}), openai({...}), ollama({...})]);
426
+ const chain = fallbackProvider(anthropic({...}), openai({...}), ollama({...}));
416
427
  ```
417
428
 
418
- ### Observability 47 typed events × 13 domains
429
+ Also available: `withCircuitBreaker(provider, opts?)`. The resilience decorators live ONLY at `agentfootprint/resilience` (not the main barrel).
430
+
431
+ ### Observability — 59 typed events × 16 domains
419
432
 
420
433
  ```typescript
421
434
  agent.on('agentfootprint.context.injected', (e) =>
@@ -440,7 +453,7 @@ Recorders (auto-attached when relevant builder method is called):
440
453
 
441
454
  - ❌ **Don't ship a `ReflexionAgent` class.** Compose `Sequence(Agent, critique-LLM, Agent)`.
442
455
  - ❌ **Don't use `agent.run('string')`** — use `agent.run({ message: '...', identity? })`.
443
- - ❌ **Don't import from stale subpaths** like `'agentfootprint/instructions'`, `'agentfootprint/observe'`, `'agentfootprint/security'`. Top-level barrel covers it: `from 'agentfootprint'`.
456
+ - ❌ **Don't import from removed subpaths** like `'agentfootprint/instructions'` — the injection factories are on the main barrel (or `'agentfootprint/injection-engine'`). NOTE: `'agentfootprint/observe'`, `'agentfootprint/security'`, `'agentfootprint/resilience'`, `'agentfootprint/llm-providers'` ARE real, current subpaths — some symbols (e.g. `buildStepGraph`, `extractSequence`, the resilience decorators, the vendor-SDK providers) are intentionally subpath-only, NOT on the main barrel.
444
457
  - ❌ **Don't use `.memoryPipeline(pipeline)`** — that's the v1 API. Use `.memory(defineMemory({...}))`.
445
458
  - ❌ **Don't fall back when TopK threshold returns nothing.** Strict semantics: garbage past context > none is wrong.
446
459
  - ❌ **Don't store closures or class instances in scope** — TransactionBuffer can't clone functions. Memory-store entries serialize to JSON.
@@ -472,9 +485,9 @@ Recorders (auto-attached when relevant builder method is called):
472
485
 
473
486
  ```bash
474
487
  npm install agentfootprint footprintjs
475
- npm test # vitest run — 1100+ tests
488
+ npm test # vitest run — 2000+ tests
476
489
  npm run example examples/... # run a single example end-to-end
477
- npm run examples:run-all # run every example (33 of them)
490
+ npm run test:examples # typecheck + run every example end-to-end
478
491
  ```
479
492
 
480
493
  ## Package layout
@@ -489,10 +502,10 @@ src/
489
502
  ├── memory/ — defineMemory + 4 types × 7 strategies + InMemoryStore + Causal
490
503
  ├── adapters/llm/ — Anthropic, OpenAI, Bedrock, Ollama, Browser variants, Mock
491
504
  ├── recorders/ — context, stream, agent, cost, skill, permission, eval, memory
492
- ├── resilience/ — withRetry, withFallback, resilientProvider
505
+ ├── resilience/ — withRetry, withFallback, fallbackProvider, withCircuitBreaker
493
506
  └── stream.ts — SSE formatter
494
507
 
495
- examples/ — 33 runnable end-to-end tests organized by DNA layer
508
+ examples/ — 47 runnable end-to-end tests organized by DNA layer
496
509
  ├── core/ — primitives
497
510
  ├── core-flow/ — compositions
498
511
  ├── patterns/ — canonical recipes
@@ -503,10 +516,7 @@ examples/ — 33 runnable end-to-end tests organized by DNA layer
503
516
 
504
517
  ## Roadmap (informs what to defer)
505
518
 
506
- - **v2.0 (current)** — primitives + compositions + InjectionEngine + Memory (incl. Causal) + 6 providers + 33 examples
507
- - **v2.1** — RAG flavor (`defineRAG`) · Redis memory adapter · MCP integration · CircuitBreaker · 3-tier output fallback
508
- - **v2.2** — Governance (Policy + BudgetTracker) · DynamoDB / Postgres / Pinecone adapters
509
- - **v2.3** — Causal training-data exports (SFT / DPO / process-RL)
510
- - **v2.4+** — Deep Agents · A2A protocol · Lens UI integration
519
+ - **Shipped (v3.x current)** — primitives + compositions + InjectionEngine + Memory (incl. Causal) + providers (vendor-SDK + browser + mock) + RAG (`defineRAG`) + MCP (`mcpClient`) + Redis/AgentCore memory adapters + resilience (`withRetry` / `withFallback` / `fallbackProvider` / `withCircuitBreaker`) + reliability subsystem + governance (`PermissionPolicy`) + observability subpaths (`/observe`, `/thinking`, `/locales`, `/status`)
520
+ - **Planned** — BudgetTracker · DynamoDB / Postgres / Pinecone memory adapters · Causal training-data exports (SFT / DPO / process-RL) · Deep Agents · A2A protocol · Lens UI integration
511
521
 
512
522
  When in doubt — read [`examples/`](examples/), every file is a runnable spec.
package/README.md CHANGED
@@ -18,6 +18,8 @@
18
18
  <a href="https://github.com/footprintjs/agentfootprint/actions"><img src="https://github.com/footprintjs/agentfootprint/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
19
19
  <!-- coverage-badge --><img src="https://img.shields.io/badge/coverage-87%25-green.svg" alt="coverage: 87%"><!-- /coverage-badge -->
20
20
  <a href="https://www.npmjs.com/package/agentfootprint"><img src="https://img.shields.io/npm/v/agentfootprint.svg?style=flat" alt="npm version"></a>
21
+ <a href="https://bundlephobia.com/package/agentfootprint"><img src="https://img.shields.io/bundlephobia/minzip/agentfootprint?label=minzipped" alt="minzipped size"></a>
22
+ <a href="#tree-shakeable--esm-first"><img src="https://img.shields.io/badge/tree--shakeable-%E2%9C%93-success?style=flat" alt="tree-shakeable"></a>
21
23
  <a href="https://www.npmjs.com/package/agentfootprint"><img src="https://img.shields.io/npm/dm/agentfootprint.svg" alt="Downloads"></a>
22
24
  <a href="https://github.com/footprintjs/agentfootprint/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT"></a>
23
25
  </p>
@@ -54,15 +56,15 @@ That's the whole model: `Injection = slot × trigger × cache`.
54
56
 
55
57
  ### The 4 triggers
56
58
 
57
- | Trigger | Flavor | Fires when | Builder example | Default slot |
59
+ | Trigger | Flavor | Fires when | Illustration | Default slot |
58
60
  |---|---|---|---|---|
59
- | `always` | static | Every iteration | `.steering('You are a triage agent…')` | `system` |
60
- | `rule` | runtime — predicate | Your rule returns true | `.rag({ when: s => /price\|refund/.test(s.userQuery) })` | `messages` |
61
- | `on-tool-return` | runtime — lifecycle | After a specific tool returns | `.instruction({ after: 'search_db', text: 'Cite source IDs.' })` | `messages` |
62
- | `llm-activated` | runtime — agent-driven | LLM calls `read_skill('id')` | `.skill({ id: 'refund-policy', activatedBy: 'read_skill' })` | `messages` (body) |
61
+ | `always` | static | Every iteration | `.steering(defineSteering({ id, prompt: 'You are a triage agent…' }))` | `system` |
62
+ | `rule` | runtime — predicate | Your rule returns true | `.instruction(defineInstruction({ id, activeWhen: s => /price\|refund/.test(s.userQuery), prompt }))` | `system` |
63
+ | `on-tool-return` | runtime — lifecycle | After a specific tool returns | `.instruction(defineInstruction({ id, slot: 'messages', activeWhen, prompt: 'Cite source IDs.' }))` | `messages` |
64
+ | `llm-activated` | runtime — agent-driven | LLM calls `read_skill('id')` | `.skill(defineSkill({ id: 'refund-policy', description, body, viaToolName: 'read_skill' }))` | `messages` (body) |
63
65
 
64
66
  > [!NOTE]
65
- > Slot is a default, not a coupling — the same `Skill` can live in `tools` (schema only, discovered via `read_skill`), `messages` (body injected on activation), or `system` (baked into the prompt as steering).
67
+ > The "Illustration" column shows the shape of each flavor — the typed builder methods (`.steering` / `.instruction` / `.skill` / `.fact` / `.rag`) take an `Injection` (or `MemoryDefinition` for `.rag`) produced by the matching `defineSteering` / `defineInstruction` / `defineSkill` / `defineFact` / `defineRAG` factory. Slot is a default, not a coupling — the same `Skill` can live in `tools` (schema only, discovered via `read_skill`), `messages` (body injected on activation), or `system` (baked into the prompt as steering).
66
68
 
67
69
  **3 slots × 4 triggers × N flavors = the entire context-engineering surface.**
68
70
 
@@ -204,8 +206,8 @@ const fan = Parallel.create()
204
206
  import { Conditional } from 'agentfootprint';
205
207
 
206
208
  const router = Conditional.create()
207
- .when('billing', s => s.intent === 'billing', billingAgent)
208
- .when('tech', s => s.intent === 'tech', techAgent)
209
+ .when('billing', s => /bill|invoice|refund/.test(s.message), billingAgent)
210
+ .when('tech', s => /error|bug|crash/.test(s.message), techAgent)
209
211
  .otherwise('default', defaultAgent)
210
212
  .build();
211
213
  ```
@@ -227,7 +229,7 @@ import { Loop } from 'agentfootprint';
227
229
 
228
230
  const reflexion = Loop.create()
229
231
  .repeat(thinkAgent)
230
- .until(s => s.satisfied)
232
+ .until(({ latestOutput }) => latestOutput.includes('DONE'))
231
233
  .build();
232
234
  ```
233
235
 
@@ -270,7 +272,8 @@ Pick the flows that match your problem. Chain them. **That's your Agentic Applic
270
272
  ```typescript
271
273
  const research = Loop.create()
272
274
  .repeat(Sequence.create().step('plan', plan).step('search', searchAll).build())
273
- .until(s => s.satisfied).build();
275
+ .until(({ iteration, latestOutput }) => iteration >= 3 || latestOutput.includes('DONE'))
276
+ .build();
274
277
  ```
275
278
 
276
279
  Same `.create().method().build()` shape as the four rows above — just composed.
@@ -367,7 +370,7 @@ const result = await agent.run({ message: 'Weather in Paris?' });
367
370
  console.log(result); // → "I checked: it is 72°F and sunny."
368
371
  ```
369
372
 
370
- Swap `mock(...)` for `anthropic(...)` / `openai(...)` / `bedrock(...)` / `ollama(...)` for production. Nothing else changes.
373
+ For production, import a real provider from `agentfootprint/llm-providers` and swap it in — `anthropic(...)` / `openai(...)` / `bedrock(...)` / `ollama(...)`. Only the import line changes; the agent code stays the same. (The vendor-SDK providers live on the `agentfootprint/llm-providers` subpath so the main `agentfootprint` barrel stays free of optional peer-dep requires; `mock`, `browserAnthropic`, and `browserOpenai` are on the main barrel.)
371
374
 
372
375
  ---
373
376
 
@@ -393,7 +396,7 @@ The flowchart, recorders, and tests don't change between dev and prod.
393
396
  - 4 control flows — `Sequence`, `Parallel`, `Conditional`, `Loop`
394
397
  - 1 Injection primitive — `defineSkill` / `defineSteering` / `defineInstruction` / `defineFact`
395
398
  - 1 reliability gate — `.reliability({ preCheck, postDecide, providers, circuitBreaker, fallback })`
396
- - 1 tool dispatch primitive — `ToolProvider` (sync OR async) — `staticTools` · `gatedTools` · `skillScopedTools` · custom `discoveryProvider` over hubs / MCP / per-tenant catalogs
399
+ - 1 tool dispatch primitive — `ToolProvider` (sync OR async) — `staticTools` · `gatedTools` · `skillScopedTools` · or a custom `ToolProvider` that discovers over hubs / MCP / per-tenant catalogs
397
400
 
398
401
  **LLM providers** (7)
399
402
 
@@ -441,6 +444,18 @@ Or jump into the [examples gallery](https://github.com/footprintjs/agentfootprin
441
444
 
442
445
  ---
443
446
 
447
+ ## Tree-shakeable & ESM-first
448
+
449
+ Import one thing, ship one thing. agentfootprint is built so your bundle grows only with what you actually use:
450
+
451
+ - **Dual build, true ESM.** Ships CommonJS (`require`) **and** real ECMAScript Modules (`import`) with TypeScript types. The ESM build is `type:module` with explicit `.js` import extensions, so it loads as true ESM under Node, Vite, Next, Deno, and Bun — no shims.
452
+ - **Per-file modules + honest `sideEffects`.** The dist is emitted file-by-file (never pre-bundled), so bundlers drop every export you don't touch. A small `import { defineTool }` doesn't pull in the Agent runtime, injection engine, memory stores, or LLM providers.
453
+ - **Subpath exports + lazy peer-deps.** Heavyweight integrations live behind their own subpaths and load their SDK **only when you instantiate them** — importing agentfootprint never bundles `@anthropic-ai/sdk`, `ioredis`, the AWS SDKs, or the MCP SDK unless you actually use that adapter.
454
+
455
+ **Proven, not promised.** A CI smoke test bundles a minimal `import { defineTool }` and asserts the Agent runtime, injection engine, memory stores, and providers are pruned; a second test loads the main barrel and every subpath as true ESM and verifies the lazy-adapter loader works under ESM (`createRequire`, not a bare `require`). See [`test/esm-packaging.test.ts`](test/esm-packaging.test.ts).
456
+
457
+ ---
458
+
444
459
  ## Built on
445
460
 
446
461
  [footprintjs](https://github.com/footprintjs/footPrint) — the flowchart pattern for backend code. agentfootprint's decision-evidence capture, narrative recording, and time-travel checkpointing are footprintjs primitives at the runtime layer.
@@ -9,11 +9,12 @@
9
9
  * Emits: N/A.
10
10
  *
11
11
  * @example
12
+ * const kind = (process.env.LLM_PROVIDER ?? 'anthropic') as ProviderKind;
12
13
  * const provider = createProvider({
13
- * kind: process.env.LLM_PROVIDER ?? 'mock',
14
+ * kind,
14
15
  * apiKey: process.env.LLM_API_KEY,
15
16
  * defaultModel: process.env.LLM_MODEL,
16
- * });
17
+ * } as CreateProviderOptions);
17
18
  *
18
19
  * For provider-specific options (Bedrock region, Ollama host, Browser
19
20
  * apiUrl, etc.) construct the underlying factory directly — this
@@ -1 +1 @@
1
- {"version":3,"file":"createProvider.js","sourceRoot":"","sources":["../../../src/adapters/llm/createProvider.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAGH,uDAAmE;AACnE,iEAAkF;AAClF,2DAAiF;AACjF,6DAA4E;AAC5E,+EAGuC;AACvC,yEAA8F;AA2B9F;;GAEG;AACH,SAAgB,cAAc,CAAC,OAA8B;IAC3D,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,IAAA,sBAAI,EAAC,OAAO,CAAC,CAAC;QACvB,KAAK,WAAW;YACd,OAAO,IAAA,gCAAS,EAAC,OAAO,CAAC,CAAC;QAC5B,KAAK,QAAQ;YACX,OAAO,IAAA,0BAAM,EAAC,OAAO,CAAC,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,IAAA,0BAAM,EAAC,OAAO,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,IAAA,4BAAO,EAAC,OAAO,CAAC,CAAC;QAC1B,KAAK,mBAAmB;YACtB,OAAO,IAAA,8CAAgB,EAAC,OAAO,CAAC,CAAC;QACnC,KAAK,gBAAgB;YACnB,OAAO,IAAA,wCAAa,EAAC,OAAO,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,CAAC;YACR,sEAAsE;YACtE,MAAM,WAAW,GAAU,OAAO,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,CAAC,SAAS,CAAE,WAAgC,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAxBD,wCAwBC"}
1
+ {"version":3,"file":"createProvider.js","sourceRoot":"","sources":["../../../src/adapters/llm/createProvider.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAGH,uDAAmE;AACnE,iEAAkF;AAClF,2DAAiF;AACjF,6DAA4E;AAC5E,+EAGuC;AACvC,yEAA8F;AA2B9F;;GAEG;AACH,SAAgB,cAAc,CAAC,OAA8B;IAC3D,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,IAAA,sBAAI,EAAC,OAAO,CAAC,CAAC;QACvB,KAAK,WAAW;YACd,OAAO,IAAA,gCAAS,EAAC,OAAO,CAAC,CAAC;QAC5B,KAAK,QAAQ;YACX,OAAO,IAAA,0BAAM,EAAC,OAAO,CAAC,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,IAAA,0BAAM,EAAC,OAAO,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,IAAA,4BAAO,EAAC,OAAO,CAAC,CAAC;QAC1B,KAAK,mBAAmB;YACtB,OAAO,IAAA,8CAAgB,EAAC,OAAO,CAAC,CAAC;QACnC,KAAK,gBAAgB;YACnB,OAAO,IAAA,wCAAa,EAAC,OAAO,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,CAAC;YACR,sEAAsE;YACtE,MAAM,WAAW,GAAU,OAAO,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,CAAC,SAAS,CAAE,WAAgC,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAxBD,wCAwBC"}
@@ -42,6 +42,7 @@
42
42
  * @example Test injection (skip SDK require entirely)
43
43
  * ```ts
44
44
  * agentcoreObservability({
45
+ * logGroupName: '/agentfootprint/test',
45
46
  * _client: {
46
47
  * putLogEvents: async (input) => { capturedBatches.push(input); },
47
48
  * },
@@ -1 +1 @@
1
- {"version":3,"file":"agentcore.js","sourceRoot":"","sources":["../../../src/adapters/observability/agentcore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;;;AAIH,mDAGyB;AAWzB;;;;;GAKG;AACH,SAAgB,sBAAsB,CAAC,IAAmC;IACxE,OAAO,IAAA,6CAA6B,EAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAC1D,CAAC;AAFD,wDAEC"}
1
+ {"version":3,"file":"agentcore.js","sourceRoot":"","sources":["../../../src/adapters/observability/agentcore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;;;AAIH,mDAGyB;AAWzB;;;;;GAKG;AACH,SAAgB,sBAAsB,CAAC,IAAmC;IACxE,OAAO,IAAA,6CAA6B,EAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAC1D,CAAC;AAFD,wDAEC"}
@@ -38,13 +38,13 @@
38
38
  *
39
39
  * @example Single-stage flowchart as a tool
40
40
  * import { flowChart } from 'footprintjs';
41
- * import { flowchartAsTool } from 'agentfootprint';
41
+ * import { Agent, flowchartAsTool } from 'agentfootprint';
42
42
  *
43
- * const refundChart = flowChart<{ orderId: string; reason: string }>(
43
+ * const refundChart = flowChart<{ refundId: string }>(
44
44
  * 'RefundFlow',
45
45
  * async (scope) => {
46
- * const refundId = await refundService.process(scope.$getArgs().orderId);
47
- * scope.refundId = refundId;
46
+ * const args = scope.$getArgs<{ orderId: string; reason: string }>();
47
+ * scope.refundId = await refundService.process(args.orderId);
48
48
  * },
49
49
  * 'refund-flow',
50
50
  * ).build();
@@ -65,7 +65,7 @@
65
65
  * JSON.stringify({ refundId: snapshot.values.refundId, status: 'processed' }),
66
66
  * });
67
67
  *
68
- * agent.tool(refundTool);
68
+ * const agent = Agent.create({ provider }).tool(refundTool).build();
69
69
  *
70
70
  * @example Multi-stage flowchart with decide() + recorders
71
71
  * const triageChart = flowChart<TriageState>('Triage', validateInput, 'validate')
@@ -8,11 +8,12 @@
8
8
  * Emits: N/A.
9
9
  *
10
10
  * @example
11
+ * const kind = (process.env.LLM_PROVIDER ?? 'anthropic') as ProviderKind;
11
12
  * const provider = createProvider({
12
- * kind: process.env.LLM_PROVIDER ?? 'mock',
13
+ * kind,
13
14
  * apiKey: process.env.LLM_API_KEY,
14
15
  * defaultModel: process.env.LLM_MODEL,
15
- * });
16
+ * } as CreateProviderOptions);
16
17
  *
17
18
  * For provider-specific options (Bedrock region, Ollama host, Browser
18
19
  * apiUrl, etc.) construct the underlying factory directly — this
@@ -1 +1 @@
1
- {"version":3,"file":"createProvider.js","sourceRoot":"","sources":["../../../../src/adapters/llm/createProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,IAAI,EAA4B,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,MAAM,EAAE,MAAM,EAA8B,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,OAAO,EAA+B,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EACL,gBAAgB,GAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,aAAa,EAAqC,MAAM,4BAA4B,CAAC;AA2B9F;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,WAAW;YACd,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC;QAC5B,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,KAAK,mBAAmB;YACtB,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACnC,KAAK,gBAAgB;YACnB,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,CAAC;YACR,sEAAsE;YACtE,MAAM,WAAW,GAAU,OAAO,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,CAAC,SAAS,CAAE,WAAgC,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"createProvider.js","sourceRoot":"","sources":["../../../../src/adapters/llm/createProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,IAAI,EAA4B,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,MAAM,EAAE,MAAM,EAA8B,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,OAAO,EAA+B,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EACL,gBAAgB,GAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,aAAa,EAAqC,MAAM,4BAA4B,CAAC;AA2B9F;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,KAAK,WAAW;YACd,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC;QAC5B,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,KAAK,mBAAmB;YACtB,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACnC,KAAK,gBAAgB;YACnB,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC,CAAC;YACR,sEAAsE;YACtE,MAAM,WAAW,GAAU,OAAO,CAAC;YACnC,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,CAAC,SAAS,CAAE,WAAgC,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -41,6 +41,7 @@
41
41
  * @example Test injection (skip SDK require entirely)
42
42
  * ```ts
43
43
  * agentcoreObservability({
44
+ * logGroupName: '/agentfootprint/test',
44
45
  * _client: {
45
46
  * putLogEvents: async (input) => { capturedBatches.push(input); },
46
47
  * },
@@ -1 +1 @@
1
- {"version":3,"file":"agentcore.js","sourceRoot":"","sources":["../../../../src/adapters/observability/agentcore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAIH,OAAO,EACL,6BAA6B,GAE9B,MAAM,iBAAiB,CAAC;AAWzB;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAmC;IACxE,OAAO,6BAA6B,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"agentcore.js","sourceRoot":"","sources":["../../../../src/adapters/observability/agentcore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAIH,OAAO,EACL,6BAA6B,GAE9B,MAAM,iBAAiB,CAAC;AAWzB;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAmC;IACxE,OAAO,6BAA6B,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAC1D,CAAC"}
@@ -37,13 +37,13 @@
37
37
  *
38
38
  * @example Single-stage flowchart as a tool
39
39
  * import { flowChart } from 'footprintjs';
40
- * import { flowchartAsTool } from 'agentfootprint';
40
+ * import { Agent, flowchartAsTool } from 'agentfootprint';
41
41
  *
42
- * const refundChart = flowChart<{ orderId: string; reason: string }>(
42
+ * const refundChart = flowChart<{ refundId: string }>(
43
43
  * 'RefundFlow',
44
44
  * async (scope) => {
45
- * const refundId = await refundService.process(scope.$getArgs().orderId);
46
- * scope.refundId = refundId;
45
+ * const args = scope.$getArgs<{ orderId: string; reason: string }>();
46
+ * scope.refundId = await refundService.process(args.orderId);
47
47
  * },
48
48
  * 'refund-flow',
49
49
  * ).build();
@@ -64,7 +64,7 @@
64
64
  * JSON.stringify({ refundId: snapshot.values.refundId, status: 'processed' }),
65
65
  * });
66
66
  *
67
- * agent.tool(refundTool);
67
+ * const agent = Agent.create({ provider }).tool(refundTool).build();
68
68
  *
69
69
  * @example Multi-stage flowchart with decide() + recorders
70
70
  * const triageChart = flowChart<TriageState>('Triage', validateInput, 'validate')