agentfootprint 8.7.0 → 8.8.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 (166) hide show
  1. package/AGENTS.md +12 -4
  2. package/CLAUDE.md +4 -2
  3. package/ai-instructions/claude-code/SKILL.md +1 -1
  4. package/ai-instructions/setup.sh +0 -0
  5. package/bin/agentfootprint-lint-tools.mjs +0 -0
  6. package/dist/core/agent/buildAgentChart.js +12 -1
  7. package/dist/core/agent/buildAgentChart.js.map +1 -1
  8. package/dist/core/agent/buildDynamicAgentChart.js +12 -1
  9. package/dist/core/agent/buildDynamicAgentChart.js.map +1 -1
  10. package/dist/core/agent/memoryRecallInjections.js +100 -10
  11. package/dist/core/agent/memoryRecallInjections.js.map +1 -1
  12. package/dist/core/agent/stages/deliver.js.map +1 -1
  13. package/dist/core/slots/buildSystemPromptSlot.js +10 -0
  14. package/dist/core/slots/buildSystemPromptSlot.js.map +1 -1
  15. package/dist/core/slots/helpers.js +12 -10
  16. package/dist/core/slots/helpers.js.map +1 -1
  17. package/dist/esm/core/agent/buildAgentChart.js +13 -2
  18. package/dist/esm/core/agent/buildAgentChart.js.map +1 -1
  19. package/dist/esm/core/agent/buildDynamicAgentChart.js +13 -2
  20. package/dist/esm/core/agent/buildDynamicAgentChart.js.map +1 -1
  21. package/dist/esm/core/agent/memoryRecallInjections.d.ts +13 -3
  22. package/dist/esm/core/agent/memoryRecallInjections.js +101 -11
  23. package/dist/esm/core/agent/memoryRecallInjections.js.map +1 -1
  24. package/dist/esm/core/agent/stages/deliver.d.ts +6 -3
  25. package/dist/esm/core/agent/stages/deliver.js.map +1 -1
  26. package/dist/esm/core/slots/buildSystemPromptSlot.js +10 -0
  27. package/dist/esm/core/slots/buildSystemPromptSlot.js.map +1 -1
  28. package/dist/esm/core/slots/helpers.d.ts +11 -2
  29. package/dist/esm/core/slots/helpers.js +11 -9
  30. package/dist/esm/core/slots/helpers.js.map +1 -1
  31. package/dist/esm/events/payloads.d.ts +63 -0
  32. package/dist/esm/events/registry.d.ts +3 -1
  33. package/dist/esm/events/registry.js +2 -0
  34. package/dist/esm/events/registry.js.map +1 -1
  35. package/dist/esm/index.d.ts +2 -1
  36. package/dist/esm/index.js +6 -1
  37. package/dist/esm/index.js.map +1 -1
  38. package/dist/esm/lib/fnv1a.d.ts +16 -0
  39. package/dist/esm/lib/fnv1a.js +24 -0
  40. package/dist/esm/lib/fnv1a.js.map +1 -0
  41. package/dist/esm/lib/injection-engine/types.d.ts +17 -0
  42. package/dist/esm/lib/injection-engine/types.js.map +1 -1
  43. package/dist/esm/lib/rag/defineRAG.d.ts +126 -26
  44. package/dist/esm/lib/rag/defineRAG.js +112 -25
  45. package/dist/esm/lib/rag/defineRAG.js.map +1 -1
  46. package/dist/esm/lib/rag/index.d.ts +1 -1
  47. package/dist/esm/lib/rag/index.js +1 -1
  48. package/dist/esm/lib/rag/index.js.map +1 -1
  49. package/dist/esm/memory/define.js +38 -2
  50. package/dist/esm/memory/define.js.map +1 -1
  51. package/dist/esm/memory/define.types.d.ts +93 -1
  52. package/dist/esm/memory/define.types.js +18 -0
  53. package/dist/esm/memory/define.types.js.map +1 -1
  54. package/dist/esm/memory/embedding/loadRelevant.d.ts +49 -15
  55. package/dist/esm/memory/embedding/loadRelevant.js +136 -7
  56. package/dist/esm/memory/embedding/loadRelevant.js.map +1 -1
  57. package/dist/esm/memory/index.d.ts +2 -1
  58. package/dist/esm/memory/index.js +2 -1
  59. package/dist/esm/memory/index.js.map +1 -1
  60. package/dist/esm/memory/pipeline/semantic.d.ts +15 -0
  61. package/dist/esm/memory/pipeline/semantic.js +3 -1
  62. package/dist/esm/memory/pipeline/semantic.js.map +1 -1
  63. package/dist/esm/memory/retrieval/index.d.ts +10 -0
  64. package/dist/esm/memory/retrieval/index.js +3 -0
  65. package/dist/esm/memory/retrieval/index.js.map +1 -0
  66. package/dist/esm/memory/retrieval/provenance.d.ts +43 -0
  67. package/dist/esm/memory/retrieval/provenance.js +60 -0
  68. package/dist/esm/memory/retrieval/provenance.js.map +1 -0
  69. package/dist/esm/memory/retrieval/topK.d.ts +67 -0
  70. package/dist/esm/memory/retrieval/topK.js +53 -0
  71. package/dist/esm/memory/retrieval/topK.js.map +1 -0
  72. package/dist/esm/memory/retrieval/types.d.ts +189 -0
  73. package/dist/esm/memory/retrieval/types.js +2 -0
  74. package/dist/esm/memory/retrieval/types.js.map +1 -0
  75. package/dist/esm/memory/stages/formatDefault.d.ts +52 -26
  76. package/dist/esm/memory/stages/formatDefault.js +118 -28
  77. package/dist/esm/memory/stages/formatDefault.js.map +1 -1
  78. package/dist/esm/memory/stages/pickByBudget.js +53 -3
  79. package/dist/esm/memory/stages/pickByBudget.js.map +1 -1
  80. package/dist/esm/memory/stages/types.d.ts +15 -0
  81. package/dist/esm/memory/wire/mountMemoryPipeline.d.ts +25 -0
  82. package/dist/esm/memory/wire/mountMemoryPipeline.js +11 -2
  83. package/dist/esm/memory/wire/mountMemoryPipeline.js.map +1 -1
  84. package/dist/events/registry.js +2 -0
  85. package/dist/events/registry.js.map +1 -1
  86. package/dist/index.js +8 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/lib/fnv1a.js +28 -0
  89. package/dist/lib/fnv1a.js.map +1 -0
  90. package/dist/lib/injection-engine/types.js.map +1 -1
  91. package/dist/lib/rag/defineRAG.js +113 -26
  92. package/dist/lib/rag/defineRAG.js.map +1 -1
  93. package/dist/lib/rag/index.js +2 -1
  94. package/dist/lib/rag/index.js.map +1 -1
  95. package/dist/memory/define.js +38 -2
  96. package/dist/memory/define.js.map +1 -1
  97. package/dist/memory/define.types.js +21 -1
  98. package/dist/memory/define.types.js.map +1 -1
  99. package/dist/memory/embedding/loadRelevant.js +138 -8
  100. package/dist/memory/embedding/loadRelevant.js.map +1 -1
  101. package/dist/memory/index.js +5 -1
  102. package/dist/memory/index.js.map +1 -1
  103. package/dist/memory/pipeline/semantic.js +2 -0
  104. package/dist/memory/pipeline/semantic.js.map +1 -1
  105. package/dist/memory/retrieval/index.js +9 -0
  106. package/dist/memory/retrieval/index.js.map +1 -0
  107. package/dist/memory/retrieval/provenance.js +65 -0
  108. package/dist/memory/retrieval/provenance.js.map +1 -0
  109. package/dist/memory/retrieval/topK.js +57 -0
  110. package/dist/memory/retrieval/topK.js.map +1 -0
  111. package/dist/memory/retrieval/types.js +3 -0
  112. package/dist/memory/retrieval/types.js.map +1 -0
  113. package/dist/memory/stages/formatDefault.js +118 -28
  114. package/dist/memory/stages/formatDefault.js.map +1 -1
  115. package/dist/memory/stages/pickByBudget.js +53 -3
  116. package/dist/memory/stages/pickByBudget.js.map +1 -1
  117. package/dist/memory/wire/mountMemoryPipeline.js +11 -2
  118. package/dist/memory/wire/mountMemoryPipeline.js.map +1 -1
  119. package/dist/types/core/agent/buildAgentChart.d.ts.map +1 -1
  120. package/dist/types/core/agent/buildDynamicAgentChart.d.ts.map +1 -1
  121. package/dist/types/core/agent/memoryRecallInjections.d.ts +13 -3
  122. package/dist/types/core/agent/memoryRecallInjections.d.ts.map +1 -1
  123. package/dist/types/core/agent/stages/deliver.d.ts +6 -3
  124. package/dist/types/core/agent/stages/deliver.d.ts.map +1 -1
  125. package/dist/types/core/slots/buildSystemPromptSlot.d.ts.map +1 -1
  126. package/dist/types/core/slots/helpers.d.ts +11 -2
  127. package/dist/types/core/slots/helpers.d.ts.map +1 -1
  128. package/dist/types/events/payloads.d.ts +63 -0
  129. package/dist/types/events/payloads.d.ts.map +1 -1
  130. package/dist/types/events/registry.d.ts +3 -1
  131. package/dist/types/events/registry.d.ts.map +1 -1
  132. package/dist/types/index.d.ts +2 -1
  133. package/dist/types/index.d.ts.map +1 -1
  134. package/dist/types/lib/fnv1a.d.ts +17 -0
  135. package/dist/types/lib/fnv1a.d.ts.map +1 -0
  136. package/dist/types/lib/injection-engine/types.d.ts +17 -0
  137. package/dist/types/lib/injection-engine/types.d.ts.map +1 -1
  138. package/dist/types/lib/rag/defineRAG.d.ts +126 -26
  139. package/dist/types/lib/rag/defineRAG.d.ts.map +1 -1
  140. package/dist/types/lib/rag/index.d.ts +1 -1
  141. package/dist/types/lib/rag/index.d.ts.map +1 -1
  142. package/dist/types/memory/define.d.ts.map +1 -1
  143. package/dist/types/memory/define.types.d.ts +93 -1
  144. package/dist/types/memory/define.types.d.ts.map +1 -1
  145. package/dist/types/memory/embedding/loadRelevant.d.ts +49 -15
  146. package/dist/types/memory/embedding/loadRelevant.d.ts.map +1 -1
  147. package/dist/types/memory/index.d.ts +2 -1
  148. package/dist/types/memory/index.d.ts.map +1 -1
  149. package/dist/types/memory/pipeline/semantic.d.ts +15 -0
  150. package/dist/types/memory/pipeline/semantic.d.ts.map +1 -1
  151. package/dist/types/memory/retrieval/index.d.ts +11 -0
  152. package/dist/types/memory/retrieval/index.d.ts.map +1 -0
  153. package/dist/types/memory/retrieval/provenance.d.ts +44 -0
  154. package/dist/types/memory/retrieval/provenance.d.ts.map +1 -0
  155. package/dist/types/memory/retrieval/topK.d.ts +68 -0
  156. package/dist/types/memory/retrieval/topK.d.ts.map +1 -0
  157. package/dist/types/memory/retrieval/types.d.ts +190 -0
  158. package/dist/types/memory/retrieval/types.d.ts.map +1 -0
  159. package/dist/types/memory/stages/formatDefault.d.ts +52 -26
  160. package/dist/types/memory/stages/formatDefault.d.ts.map +1 -1
  161. package/dist/types/memory/stages/pickByBudget.d.ts.map +1 -1
  162. package/dist/types/memory/stages/types.d.ts +15 -0
  163. package/dist/types/memory/stages/types.d.ts.map +1 -1
  164. package/dist/types/memory/wire/mountMemoryPipeline.d.ts +25 -0
  165. package/dist/types/memory/wire/mountMemoryPipeline.d.ts.map +1 -1
  166. package/package.json +1 -1
@@ -37,7 +37,7 @@
37
37
  */
38
38
  import { flowChart } from 'footprintjs';
39
39
  import { pickByBudget } from '../stages/pickByBudget.js';
40
- import { formatDefault } from '../stages/formatDefault.js';
40
+ import { formatDefault, } from '../stages/formatDefault.js';
41
41
  import { writeMessages } from '../stages/writeMessages.js';
42
42
  import { loadRelevant } from '../embedding/loadRelevant.js';
43
43
  import { embedMessages, } from '../embedding/embedMessages.js';
@@ -57,6 +57,7 @@ export function semanticPipeline(config) {
57
57
  ...(config.k !== undefined && { k: config.k }),
58
58
  ...(config.minScore !== undefined && { minScore: config.minScore }),
59
59
  ...(config.tiers && { tiers: config.tiers }),
60
+ ...(config.retrieval !== undefined && { retrieval: config.retrieval }),
60
61
  };
61
62
  const pickConfig = {
62
63
  ...(config.reserveTokens !== undefined && { reserveTokens: config.reserveTokens }),
@@ -66,6 +67,7 @@ export function semanticPipeline(config) {
66
67
  const formatConfig = {
67
68
  ...(config.formatHeader !== undefined && { header: config.formatHeader }),
68
69
  ...(config.formatFooter !== undefined && { footer: config.formatFooter }),
70
+ ...(config.flavor !== undefined && { flavor: config.flavor }),
69
71
  };
70
72
  const embedConfig = {
71
73
  embedder: config.embedder,
@@ -1 +1 @@
1
- {"version":3,"file":"semantic.js","sourceRoot":"","sources":["../../../../src/memory/pipeline/semantic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"semantic.js","sourceRoot":"","sources":["../../../../src/memory/pipeline/semantic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,YAAY,EAA2B,MAAM,2BAA2B,CAAC;AAClF,OAAO,EACL,aAAa,GAGd,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,aAAa,EAA4B,MAAM,4BAA4B,CAAC;AAKrF,OAAO,EAAE,YAAY,EAA2B,MAAM,8BAA8B,CAAC;AACrF,OAAO,EACL,aAAa,GAGd,MAAM,+BAA+B,CAAC;AAyDvC;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA8B;IAC7D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,sEAAsE;YACpE,yEAAyE,CAC5E,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAuB;QACrC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;QACzE,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,SAAS,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACnE,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QAC5C,GAAG,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;KACvE,CAAC;IACF,MAAM,UAAU,GAAuB;QACrC,GAAG,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC;QAClF,GAAG,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC;QAClF,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;KAC1E,CAAC;IACF,MAAM,YAAY,GAAwB;QACxC,GAAG,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;QACzE,GAAG,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;QACzE,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;KAC9D,CAAC;IACF,MAAM,WAAW,GAAwB;QACvC,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;KAC1E,CAAC;IACF,MAAM,WAAW,GAAwB;QACvC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;QACnD,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;KACrE,CAAC;IAEF,+DAA+D;IAC/D,IAAI,WAAW,GAAG,SAAS,CACzB,cAAc,EACd,YAAY,CAAC,UAAU,CAAC,EACxB,eAAe,EACf,EAAE,WAAW,EAAE,4DAA4D,EAAE,CAC9E,CAAC;IACF,WAAW,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,WAAW;SACrB,WAAW,CACV,QAAQ,EACR,aAAa,CAAC,YAAY,CAAC,EAC3B,gBAAgB,EAChB,6CAA6C,CAC9C;SACA,KAAK,EAAE,CAAC;IAEX,kDAAkD;IAClD,MAAM,KAAK,GAAG,SAAS,CACrB,eAAe,EACf,aAAa,CAAC,WAAW,CAAC,EAC1B,gBAAgB,EAChB,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAChF;SACE,WAAW,CACV,eAAe,EACf,aAAa,CAAC,WAAW,CAAC,EAC1B,gBAAgB,EAChB,0DAA0D,CAC3D;SACA,KAAK,EAAE,CAAC;IAEX,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * memory/retrieval — what a retrieval considered, and the seam that
3
+ * decides what it keeps.
4
+ *
5
+ * @see ./types.ts the record + the strategy interface
6
+ * @see ./topK.ts the strategy 8.7.0 had, now written down as one
7
+ */
8
+ export type { RetrievalEvidence, RetrievalRejectReason, RetrievalStrategy, RetrievalVerdict, RetrievedCandidate, ScoredCandidate, } from './types.js';
9
+ export { topK, type TopKOptions } from './topK.js';
10
+ export { chunkProvenance, chunkText, type ChunkProvenance } from './provenance.js';
@@ -0,0 +1,3 @@
1
+ export { topK } from './topK.js';
2
+ export { chunkProvenance, chunkText } from './provenance.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/memory/retrieval/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,IAAI,EAAoB,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,SAAS,EAAwB,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * provenance — read a stored entry's coordinates back out of it.
3
+ *
4
+ * Pattern: pure projection.
5
+ * Role: memory/ layer. One place that knows which metadata keys mean
6
+ * "which document", "which page", "which section", so the
7
+ * retrieval record and the citation the model sees can never
8
+ * disagree about a chunk's origin.
9
+ * Emits: N/A.
10
+ *
11
+ * The keys are the ones `indexDocuments` already accepts on
12
+ * `RagDocument.metadata`, and the ones a document splitter will write.
13
+ * Nothing is invented: an entry that carries no metadata simply has no
14
+ * coordinates, and the record says so by omitting the fields rather than
15
+ * by guessing a filename from an id.
16
+ */
17
+ /** The document coordinates a chunk can carry. All optional — absence is honest. */
18
+ export interface ChunkProvenance {
19
+ /** Which document this text came from. Read from `docUri`, else `source`. */
20
+ readonly docUri?: string;
21
+ /** Which page, for paginated formats. */
22
+ readonly page?: number;
23
+ /** Which section heading the splitter cut under. */
24
+ readonly heading?: string;
25
+ }
26
+ /**
27
+ * Pull the coordinates out of a stored value.
28
+ *
29
+ * Accepts metadata at the value's `metadata` key (where `indexDocuments`
30
+ * puts it) or directly on the value, so a hand-built entry works too.
31
+ */
32
+ export declare function chunkProvenance(value: unknown): ChunkProvenance;
33
+ /**
34
+ * The text a stored value carries.
35
+ *
36
+ * Two shapes reach the formatter through the same pipeline: a chat
37
+ * `Message` (`{ role, content }`) from conversation memory, and a
38
+ * document (`{ id, content, metadata }`) from `indexDocuments`. Both
39
+ * keep their text on `content`, which is why one accessor serves both —
40
+ * but only the message shape has a meaningful `role`, which is why the
41
+ * corpus formatter does not print one.
42
+ */
43
+ export declare function chunkText(value: unknown): string;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * provenance — read a stored entry's coordinates back out of it.
3
+ *
4
+ * Pattern: pure projection.
5
+ * Role: memory/ layer. One place that knows which metadata keys mean
6
+ * "which document", "which page", "which section", so the
7
+ * retrieval record and the citation the model sees can never
8
+ * disagree about a chunk's origin.
9
+ * Emits: N/A.
10
+ *
11
+ * The keys are the ones `indexDocuments` already accepts on
12
+ * `RagDocument.metadata`, and the ones a document splitter will write.
13
+ * Nothing is invented: an entry that carries no metadata simply has no
14
+ * coordinates, and the record says so by omitting the fields rather than
15
+ * by guessing a filename from an id.
16
+ */
17
+ function asRecord(value) {
18
+ return typeof value === 'object' && value !== null
19
+ ? value
20
+ : undefined;
21
+ }
22
+ /**
23
+ * Pull the coordinates out of a stored value.
24
+ *
25
+ * Accepts metadata at the value's `metadata` key (where `indexDocuments`
26
+ * puts it) or directly on the value, so a hand-built entry works too.
27
+ */
28
+ export function chunkProvenance(value) {
29
+ const root = asRecord(value);
30
+ if (!root)
31
+ return {};
32
+ const meta = asRecord(root['metadata']) ?? root;
33
+ const rawDoc = meta['docUri'] ?? meta['source'];
34
+ const docUri = typeof rawDoc === 'string' && rawDoc.length > 0 ? rawDoc : undefined;
35
+ const rawPage = meta['page'];
36
+ const page = typeof rawPage === 'number' && Number.isFinite(rawPage) ? rawPage : undefined;
37
+ const rawHeading = meta['heading'];
38
+ const heading = typeof rawHeading === 'string' && rawHeading.length > 0 ? rawHeading : undefined;
39
+ return {
40
+ ...(docUri !== undefined && { docUri }),
41
+ ...(page !== undefined && { page }),
42
+ ...(heading !== undefined && { heading }),
43
+ };
44
+ }
45
+ /**
46
+ * The text a stored value carries.
47
+ *
48
+ * Two shapes reach the formatter through the same pipeline: a chat
49
+ * `Message` (`{ role, content }`) from conversation memory, and a
50
+ * document (`{ id, content, metadata }`) from `indexDocuments`. Both
51
+ * keep their text on `content`, which is why one accessor serves both —
52
+ * but only the message shape has a meaningful `role`, which is why the
53
+ * corpus formatter does not print one.
54
+ */
55
+ export function chunkText(value) {
56
+ const root = asRecord(value);
57
+ const content = root?.['content'];
58
+ return typeof content === 'string' ? content : '';
59
+ }
60
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.js","sourceRoot":"","sources":["../../../../src/memory/retrieval/provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAYH,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAChD,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,IAAI,CAAC;IAEhD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpF,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3F,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAEjG,OAAO;QACL,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC;QACnC,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * topK — the retrieval strategy agentfootprint has always used, now
3
+ * written down as one.
4
+ *
5
+ * Pattern: Strategy (one of {@link RetrievalStrategy}).
6
+ * Role: memory/ layer. Extracted in 8.8.0 from the two numbers that
7
+ * used to live loose on `defineRAG` (`topK`, `threshold`), so
8
+ * that a different rule can be written without touching a stage.
9
+ * Emits: N/A — the stage that calls it does the emitting.
10
+ *
11
+ * Behaviour is byte-for-byte what 8.7.0 did: take the highest-scoring
12
+ * candidates that clear `threshold`, at most `k` of them, and inject
13
+ * nothing at all when none clear it.
14
+ *
15
+ * **The threshold is strict, and strict means silent-by-design becomes
16
+ * loud-by-record.** When nothing clears the floor, no context is
17
+ * injected — a weak match in the prompt makes a confident wrong answer
18
+ * more likely, not less. What 8.8.0 changes is that the near-misses are
19
+ * now IN the record with their scores, so "the agent answered from
20
+ * nothing" is a readable outcome instead of an absence.
21
+ */
22
+ import type { RetrievalStrategy } from './types.js';
23
+ export interface TopKOptions {
24
+ /**
25
+ * How many chunks may reach the prompt. Default 3 — enough for more
26
+ * than one perspective, few enough that the middle of a long context
27
+ * does not swallow the answer.
28
+ */
29
+ readonly k?: number;
30
+ /**
31
+ * Minimum similarity to admit, in the store's score space ([-1, 1]
32
+ * cosine for every shipped store). Default 0.7.
33
+ *
34
+ * 0.7 is a high bar for some embedders. Sentence-transformer relatives
35
+ * (`all-MiniLM-L6-v2` and family, which `localEmbedder` uses by
36
+ * default) often score 0.4–0.6 on genuinely relevant chunks; OpenAI
37
+ * `text-embedding-3-*` sits comfortably at 0.7. If retrievals come back
38
+ * empty, read the `agentfootprint.memory.retrieved` event: it now
39
+ * carries the rejected candidates and their scores, so the right
40
+ * threshold is a number you can see rather than one you guess.
41
+ *
42
+ * Pass `null` for no floor — every candidate up to `k` is admitted.
43
+ */
44
+ readonly threshold?: number | null;
45
+ /**
46
+ * How many extra candidates to pull past `k` so that rejected ones can
47
+ * be reported. Default 10. Raising it costs one larger read and shows
48
+ * more near-misses; it can never change which candidates are admitted.
49
+ */
50
+ readonly rejectWindow?: number;
51
+ }
52
+ /**
53
+ * Build the top-K strategy.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * import { defineRAG } from 'agentfootprint';
58
+ * import { topK } from 'agentfootprint/memory';
59
+ *
60
+ * const docs = defineRAG({
61
+ * id: 'product-docs',
62
+ * store, embedder,
63
+ * retrieval: topK({ k: 5, threshold: 0.55 }),
64
+ * });
65
+ * ```
66
+ */
67
+ export declare function topK(options?: TopKOptions): RetrievalStrategy;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Build the top-K strategy.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { defineRAG } from 'agentfootprint';
7
+ * import { topK } from 'agentfootprint/memory';
8
+ *
9
+ * const docs = defineRAG({
10
+ * id: 'product-docs',
11
+ * store, embedder,
12
+ * retrieval: topK({ k: 5, threshold: 0.55 }),
13
+ * });
14
+ * ```
15
+ */
16
+ export function topK(options = {}) {
17
+ const k = options.k ?? 3;
18
+ if (!Number.isInteger(k) || k < 1) {
19
+ throw new Error(`topK: \`k\` must be a positive integer — received ${String(options.k)}.`);
20
+ }
21
+ const rejectWindow = options.rejectWindow ?? 10;
22
+ if (!Number.isInteger(rejectWindow) || rejectWindow < 0) {
23
+ throw new Error(`topK: \`rejectWindow\` must be a non-negative integer — received ${String(options.rejectWindow)}.`);
24
+ }
25
+ const threshold = options.threshold === null ? undefined : options.threshold ?? 0.7;
26
+ if (threshold !== undefined && !Number.isFinite(threshold)) {
27
+ throw new Error(`topK: \`threshold\` must be a finite number or null — received ${String(options.threshold)}.`);
28
+ }
29
+ return {
30
+ name: 'topK',
31
+ k,
32
+ ...(threshold !== undefined && { threshold }),
33
+ rejectWindow,
34
+ select(pool) {
35
+ const verdicts = [];
36
+ let admitted = 0;
37
+ for (const candidate of pool) {
38
+ if (threshold !== undefined && candidate.score < threshold) {
39
+ verdicts.push({ admitted: false, reason: 'below-threshold' });
40
+ continue;
41
+ }
42
+ if (admitted >= k) {
43
+ verdicts.push({ admitted: false, reason: 'over-max-entries' });
44
+ continue;
45
+ }
46
+ admitted += 1;
47
+ verdicts.push({ admitted: true });
48
+ }
49
+ return verdicts;
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=topK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topK.js","sourceRoot":"","sources":["../../../../src/memory/retrieval/topK.ts"],"names":[],"mappings":"AAqDA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,IAAI,CAAC,UAAuB,EAAE;IAC5C,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,qDAAqD,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CACb,oEAAoE,MAAM,CACxE,OAAO,CAAC,YAAY,CACrB,GAAG,CACL,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC;IACpF,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CACb,kEAAkE,MAAM,CACtE,OAAO,CAAC,SAAS,CAClB,GAAG,CACL,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,CAAC;QACD,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC;QAC7C,YAAY;QACZ,MAAM,CAAC,IAAgC;YACrC,MAAM,QAAQ,GAAuB,EAAE,CAAC;YACxC,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;gBAC7B,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,KAAK,GAAG,SAAS,EAAE,CAAC;oBAC3D,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;oBAC9D,SAAS;gBACX,CAAC;gBACD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;oBAClB,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC/D,SAAS;gBACX,CAAC;gBACD,QAAQ,IAAI,CAAC,CAAC;gBACd,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * retrieval/types — the record a retrieval leaves behind, and the seam
3
+ * that decides which candidates reach the prompt.
4
+ *
5
+ * Pattern: Strategy (the seam) + Value objects (the record).
6
+ * Role: memory/ layer. The law this folder exists to keep is stated
7
+ * once, here, because every stage downstream implements a piece
8
+ * of it: **a retrieval must be able to say what it considered,
9
+ * not only what it used.**
10
+ * Emits: N/A (types only). `loadRelevant` emits
11
+ * `agentfootprint.memory.retrieved` from the record below;
12
+ * `formatDefault` emits `agentfootprint.memory.attached` per
13
+ * admitted chunk.
14
+ *
15
+ * Before 8.8.0 a retrieval computed a cosine score for every candidate
16
+ * and then threw all of them away one line later (`results.map(r =>
17
+ * r.entry)`). The prompt carried the passages; nothing carried the
18
+ * reason. "Why did the agent read this passage" had no answer in the
19
+ * recording, and "why did it NOT read that one" had no answer anywhere —
20
+ * a below-threshold candidate was filtered inside the store and never
21
+ * came back. {@link RetrievalEvidence} is what that answer is made of.
22
+ */
23
+ import type { MemoryEntry } from '../entry/index.js';
24
+ /**
25
+ * Why a candidate did not reach the prompt. Every rejected candidate
26
+ * names one of these — a rejection without a reason is the silence this
27
+ * whole record exists to remove.
28
+ */
29
+ export type RetrievalRejectReason =
30
+ /** Scored below the retriever's `threshold`. The quality floor refused it. */
31
+ 'below-threshold'
32
+ /** Cleared the threshold, but the context-token budget had no room left. */
33
+ | 'over-budget'
34
+ /** Cleared the threshold and the budget, but the picker's `maxEntries` cap was full. */
35
+ | 'over-max-entries';
36
+ /**
37
+ * One candidate the retrieval considered — admitted or not.
38
+ *
39
+ * `rank` is the candidate's position by SCORE (1-based, descending),
40
+ * which is not necessarily the order it appears in the prompt: the
41
+ * budget picker admits by recency (see {@link RetrievalEvidence.selectionOrder}).
42
+ * Recording both is the point — a reader can see that the best-scoring
43
+ * chunk was admitted third, and know that was the picker's doing.
44
+ */
45
+ export interface RetrievedCandidate {
46
+ /** The store entry's id. For an indexed corpus this is the chunk id. */
47
+ readonly id: string;
48
+ /** Similarity as the store reported it. Cosine ([-1, 1]) for every shipped store. */
49
+ readonly score: number;
50
+ /** 1-based position by score, descending, across the whole candidate pool. */
51
+ readonly rank: number;
52
+ /** Did this candidate's text reach the prompt? */
53
+ readonly admitted: boolean;
54
+ /** Present exactly when `admitted` is false. */
55
+ readonly reason?: RetrievalRejectReason;
56
+ /** Source document, when the indexed value carried one in its metadata. */
57
+ readonly docUri?: string;
58
+ /** Page number, when the loader knew one (PDFs). */
59
+ readonly page?: number;
60
+ /** Section heading, when the splitter knew one. */
61
+ readonly heading?: string;
62
+ /**
63
+ * The exact prompt bytes this chunk contributed, set by the formatter
64
+ * for admitted candidates. Joining every admitted candidate's fragment
65
+ * **in {@link promptPosition} order** with `\n\n` reproduces the
66
+ * injected message exactly — which is what lets one retrieval become
67
+ * one `InjectionRecord` PER CHUNK without changing a single byte the
68
+ * model sees.
69
+ */
70
+ readonly promptFragment?: string;
71
+ /**
72
+ * Where this chunk sat in the injected message, 0-based.
73
+ *
74
+ * NOT the same as {@link rank}, and the difference is the honest part:
75
+ * `rank` is how well the chunk scored, `promptPosition` is where the
76
+ * budget picker put it. Under the default recency ordering the
77
+ * best-scoring chunk can land last — which is exactly the kind of thing
78
+ * a lost-in-the-middle investigation needs to be able to see, and which
79
+ * a record that only kept one of the two orders could not show.
80
+ */
81
+ readonly promptPosition?: number;
82
+ }
83
+ /**
84
+ * Everything one retrieval knows about itself. Written to the memory
85
+ * subflow's scope by `loadRelevant`, refined by `pickByBudget` and
86
+ * `formatDefault`, and lifted to the PARENT scope by the read mount so
87
+ * it lands in the root commit log where a slice can reach it.
88
+ */
89
+ export interface RetrievalEvidence {
90
+ /** The retriever's id (`defineRAG({ id })`). Stamped by the read mount. */
91
+ readonly memoryId?: string;
92
+ /**
93
+ * A stable hash of the query text — NOT the text. The query is already
94
+ * in the recording once (as `userMessage`); copying it into a second
95
+ * key would widen the exposure surface for no new information, and any
96
+ * redaction policy the host configured for the first copy would not
97
+ * know about the second.
98
+ */
99
+ readonly queryHash: string;
100
+ /** How many chunks the retriever was willing to admit. */
101
+ readonly k: number;
102
+ /** The quality floor. Absent when the retriever set none. */
103
+ readonly threshold?: number;
104
+ /** The embedder id the query was produced with, when the caller declared one. */
105
+ readonly embedderId?: string;
106
+ /** Length of the query vector. Mixing two lengths in one store is a config bug. */
107
+ readonly dimensions?: number;
108
+ /** How the budget picker ordered the admitted set. See the note on `rank`. */
109
+ readonly selectionOrder: 'recency' | 'relevance';
110
+ /** How many candidates came back from the store. */
111
+ readonly consideredCount: number;
112
+ /** How many reached the prompt. */
113
+ readonly admittedCount: number;
114
+ /** `consideredCount - admittedCount`. */
115
+ readonly rejectedCount: number;
116
+ /**
117
+ * The candidates themselves, best-scoring first.
118
+ *
119
+ * `undefined` means this store could not tell us — see
120
+ * {@link candidatesOmittedReason}. It never means "there were none";
121
+ * that case is `[]` with `consideredCount: 0`.
122
+ */
123
+ readonly candidates?: readonly RetrievedCandidate[];
124
+ /**
125
+ * Whether {@link candidates} is the complete set of candidates that
126
+ * existed, or only as far as the pool we asked for reached.
127
+ *
128
+ * `false` does NOT weaken the admitted set — see the proof in
129
+ * `loadRelevant`. It only means the REJECTED list is a sample: there
130
+ * may be further below-threshold entries we never saw.
131
+ */
132
+ readonly candidatesComplete: boolean;
133
+ /** Present exactly when `candidates` is undefined. */
134
+ readonly candidatesOmittedReason?: string;
135
+ /**
136
+ * The store returned nothing at all for this namespace. Distinct from
137
+ * "everything scored below threshold" (`consideredCount > 0`), and the
138
+ * distinction is the whole diagnosis: an empty namespace almost always
139
+ * means the corpus was indexed somewhere else.
140
+ */
141
+ readonly corpusEmpty: boolean;
142
+ /** The namespace that was searched, as a plain string, for the diagnosis above. */
143
+ readonly namespace?: string;
144
+ }
145
+ /**
146
+ * The retrieval seam: given the candidates a store returned, decide
147
+ * which of them the prompt may have — and say why about each one.
148
+ *
149
+ * A strategy NEVER talks to the store and never embeds anything. It is
150
+ * handed a scored, score-descending pool and returns a verdict per
151
+ * candidate. That narrowness is what makes it composable: a re-ranker or
152
+ * a diversity selector is the same shape with a different body.
153
+ *
154
+ * Shipped: {@link topK}. Deliberately NOT shipped in 8.8.0, and named
155
+ * here so the destination is on record rather than implied — a
156
+ * cross-encoder `rerank(...)` and a maximal-marginal-relevance `mmr(...)`
157
+ * are additional adapters behind this same interface. Neither needs an
158
+ * engine change, a new stage, or a new event; both were left out because
159
+ * a re-ranker without a shipped re-ranking model is a config with nothing
160
+ * to configure.
161
+ */
162
+ export interface RetrievalStrategy {
163
+ /** Stable name — appears in the recording and in refusal messages. */
164
+ readonly name: string;
165
+ /** How many candidates this strategy is willing to admit. */
166
+ readonly k: number;
167
+ /** The quality floor, when the strategy has one. */
168
+ readonly threshold?: number;
169
+ /**
170
+ * How many EXTRA candidates to pull past `k` purely so that rejected
171
+ * ones can be shown. Never affects which candidates are admitted.
172
+ */
173
+ readonly rejectWindow: number;
174
+ /**
175
+ * Rule on a score-descending pool. Return one verdict per input, in
176
+ * the same order. Implementations must not reorder.
177
+ */
178
+ select(pool: readonly ScoredCandidate[]): readonly RetrievalVerdict[];
179
+ }
180
+ /** One store result, as a strategy sees it. */
181
+ export interface ScoredCandidate {
182
+ readonly entry: MemoryEntry<unknown>;
183
+ readonly score: number;
184
+ }
185
+ /** A strategy's ruling on one candidate. */
186
+ export interface RetrievalVerdict {
187
+ readonly admitted: boolean;
188
+ readonly reason?: RetrievalRejectReason;
189
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/memory/retrieval/types.ts"],"names":[],"mappings":""}
@@ -1,44 +1,64 @@
1
1
  /**
2
2
  * formatDefault — render picked entries into injection-ready messages.
3
3
  *
4
- * Reads from scope: `selected`
5
- * Writes to scope: `formatted` (messages to inject into the LLM prompt)
4
+ * Reads from scope: `selected`, `retrieved`
5
+ * Writes to scope: `formatted` (messages to inject into the LLM prompt),
6
+ * `retrieved` (each admitted chunk's prompt fragment)
6
7
  *
7
8
  * Why a separate stage from the picker?
8
- * Retrieval and presentation are orthogonal concerns (MemGPT-reviewer
9
- * ask). A picker decides WHICH memories survive the budget; a formatter
10
- * decides HOW they appear to the LLM. Consumers can swap either without
11
- * touching the other. In research settings, format variations ("JSON
12
- * envelope" vs "XML tags" vs "natural paragraphs") are worth ablating.
9
+ * Retrieval and presentation are orthogonal concerns. A picker decides
10
+ * WHICH memories survive the budget; a formatter decides HOW they appear
11
+ * to the LLM. Consumers can swap either without touching the other. In
12
+ * research settings, format variations ("JSON envelope" vs "XML tags" vs
13
+ * "natural paragraphs") are worth ablating.
13
14
  *
14
- * Default format:
15
- * One `system` message containing a citation-tagged block per entry:
15
+ * ─── Two flavors, because they are two different claims ────────────────
16
16
  *
17
- * <memory source="turn:5" updated="2026-04-18T06:00:00Z">
18
- * User said: I live in San Francisco.
17
+ * `'memory'` (default, unchanged since 7.20.0) — recall from THIS
18
+ * conversation. Each entry is a turn, so it is rendered with the turn's
19
+ * role and number:
20
+ *
21
+ * Relevant context from prior conversations. Use when it helps answer the current turn.
22
+ *
23
+ * <memory role="user" turn="5" updated="2026-04-18T06:00:00Z">
24
+ * I live in San Francisco.
19
25
  * </memory>
20
26
  *
21
- * Citation tags let the LLM reference sources in its response; the
22
- * Anthropic-reviewer ask ("recall should carry source").
27
+ * `'rag'` (8.8.0) — retrieval from a document corpus. An indexed chunk
28
+ * has no role and no turn; it has a document, a position in it, and a
29
+ * similarity score. Printing `role="unknown" turn="0"` on a page of a
30
+ * PDF, which is what the memory shape did, told the model three things
31
+ * that were not true and withheld the one thing it needed to cite:
32
+ *
33
+ * Relevant passages retrieved from the document corpus. Cite the source id when you use one.
23
34
  *
24
- * Role chosen: `system`. Reasoning: this is NOT the ongoing dialogue,
25
- * it's context we're adding. A `user` role would confuse turn-taking;
26
- * `assistant` would be a false claim. `system` matches the semantic
27
- * of "context injected by the application, not part of the conversation."
35
+ * <source id="refunds.md#3" doc="refunds.md" heading="Refund timing" score="0.81">
36
+ * Refunds are processed within 3 business days of approval.
37
+ * </source>
28
38
  *
29
- * Wrapping: entries are grouped into ONE system message rather than N
30
- * separate messages. One message is easier for LLMs to reason about
31
- * and avoids breaking up the conversational flow.
39
+ * Role chosen (both flavors): `system`. This is NOT the ongoing dialogue,
40
+ * it is context the application is adding. A `user` role would confuse
41
+ * turn-taking; `assistant` would be a false claim.
42
+ *
43
+ * Wrapping: entries are grouped into ONE system message rather than N.
44
+ * One message is easier for a model to reason about — and the record
45
+ * still resolves to one `InjectionRecord` PER chunk, because each chunk's
46
+ * exact bytes are kept on the retrieval record (`promptFragment`) and
47
+ * joining them with `\n\n` reproduces this message exactly.
32
48
  */
33
49
  import type { TypedScope } from 'footprintjs';
34
50
  import type { MemoryEntry } from '../entry/index.js';
35
51
  import type { LLMMessage as Message } from '../../adapters/types.js';
36
52
  import type { MemoryState } from './types.js';
53
+ import type { RetrievedCandidate } from '../retrieval/types.js';
54
+ /** Which claim the injected block is making about its entries. */
55
+ export type MemoryFormatFlavor = 'memory' | 'rag';
37
56
  export interface FormatDefaultConfig {
38
57
  /**
39
58
  * Header prepended to the injected message. Explains to the LLM what
40
59
  * follows and what it's for. Override if your app has specific phrasing
41
- * guidance ("long-term memory" vs "user preferences", etc.).
60
+ * guidance ("long-term memory" vs "user preferences", etc.). Defaults
61
+ * per `flavor`.
42
62
  */
43
63
  readonly header?: string;
44
64
  /**
@@ -48,12 +68,18 @@ export interface FormatDefaultConfig {
48
68
  */
49
69
  readonly footer?: string;
50
70
  /**
51
- * Custom per-entry renderer. Receives the entry; returns the block
52
- * string (without outer tags — the default wrapper adds those). Use
53
- * for app-specific formatting: custom source attributions, hiding
54
- * tier info, etc.
71
+ * Which rendering the entries get. Default `'memory'` — conversation
72
+ * recall, byte-identical to every release before 8.8.0. `defineRAG`
73
+ * sets `'rag'`.
74
+ */
75
+ readonly flavor?: MemoryFormatFlavor;
76
+ /**
77
+ * Custom per-entry renderer. Receives the entry (and, for retrieval
78
+ * pipelines, its candidate record); returns the block string. Use for
79
+ * app-specific formatting: custom source attributions, hiding tier
80
+ * info, etc. Overrides `flavor`.
55
81
  */
56
- readonly renderEntry?: (entry: MemoryEntry<Message>) => string;
82
+ readonly renderEntry?: (entry: MemoryEntry<Message>, candidate?: RetrievedCandidate) => string;
57
83
  /**
58
84
  * When `true`, inject even if `selected` is empty (emits only header
59
85
  * and footer). Usually NOT desired — an empty memory block is noise.