@graphorin/memory 0.5.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 (287) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +401 -0
  4. package/dist/conflict/index.d.ts +3 -0
  5. package/dist/conflict/index.js +7 -0
  6. package/dist/conflict/locale-packs/en.js +262 -0
  7. package/dist/conflict/locale-packs/en.js.map +1 -0
  8. package/dist/conflict/locale-packs/index.js +4 -0
  9. package/dist/conflict/locale-packs/types.d.ts +103 -0
  10. package/dist/conflict/locale-packs/types.d.ts.map +1 -0
  11. package/dist/conflict/locale-packs/types.js +55 -0
  12. package/dist/conflict/locale-packs/types.js.map +1 -0
  13. package/dist/conflict/pipeline.d.ts +35 -0
  14. package/dist/conflict/pipeline.d.ts.map +1 -0
  15. package/dist/conflict/pipeline.js +257 -0
  16. package/dist/conflict/pipeline.js.map +1 -0
  17. package/dist/conflict/stages/helpers.js +66 -0
  18. package/dist/conflict/stages/helpers.js.map +1 -0
  19. package/dist/conflict/stages/stage1-exact-dedup.js +21 -0
  20. package/dist/conflict/stages/stage1-exact-dedup.js.map +1 -0
  21. package/dist/conflict/stages/stage2-embedding-three-zone.js +37 -0
  22. package/dist/conflict/stages/stage2-embedding-three-zone.js.map +1 -0
  23. package/dist/conflict/stages/stage3-heuristic-regex.js +45 -0
  24. package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -0
  25. package/dist/conflict/stages/stage4-subject-predicate.js +31 -0
  26. package/dist/conflict/stages/stage4-subject-predicate.js.map +1 -0
  27. package/dist/conflict/stages/stage5-defer-to-deep.js +22 -0
  28. package/dist/conflict/stages/stage5-defer-to-deep.js.map +1 -0
  29. package/dist/conflict/types.d.ts +222 -0
  30. package/dist/conflict/types.d.ts.map +1 -0
  31. package/dist/conflict/types.js +50 -0
  32. package/dist/conflict/types.js.map +1 -0
  33. package/dist/consolidator/budget.d.ts +1 -0
  34. package/dist/consolidator/budget.js +214 -0
  35. package/dist/consolidator/budget.js.map +1 -0
  36. package/dist/consolidator/decay.d.ts +35 -0
  37. package/dist/consolidator/decay.d.ts.map +1 -0
  38. package/dist/consolidator/decay.js +91 -0
  39. package/dist/consolidator/decay.js.map +1 -0
  40. package/dist/consolidator/dlq.js +58 -0
  41. package/dist/consolidator/dlq.js.map +1 -0
  42. package/dist/consolidator/errors.d.ts +54 -0
  43. package/dist/consolidator/errors.d.ts.map +1 -0
  44. package/dist/consolidator/errors.js +67 -0
  45. package/dist/consolidator/errors.js.map +1 -0
  46. package/dist/consolidator/idempotency.d.ts +1 -0
  47. package/dist/consolidator/idempotency.js +22 -0
  48. package/dist/consolidator/idempotency.js.map +1 -0
  49. package/dist/consolidator/index.d.ts +34 -0
  50. package/dist/consolidator/index.d.ts.map +1 -0
  51. package/dist/consolidator/index.js +163 -0
  52. package/dist/consolidator/index.js.map +1 -0
  53. package/dist/consolidator/lock.d.ts +2 -0
  54. package/dist/consolidator/lock.js +89 -0
  55. package/dist/consolidator/lock.js.map +1 -0
  56. package/dist/consolidator/noise-filter.d.ts +2 -0
  57. package/dist/consolidator/noise-filter.js +173 -0
  58. package/dist/consolidator/noise-filter.js.map +1 -0
  59. package/dist/consolidator/phases/deep.d.ts +4 -0
  60. package/dist/consolidator/phases/deep.js +198 -0
  61. package/dist/consolidator/phases/deep.js.map +1 -0
  62. package/dist/consolidator/phases/induce.d.ts +154 -0
  63. package/dist/consolidator/phases/induce.d.ts.map +1 -0
  64. package/dist/consolidator/phases/induce.js +253 -0
  65. package/dist/consolidator/phases/induce.js.map +1 -0
  66. package/dist/consolidator/phases/light.d.ts +4 -0
  67. package/dist/consolidator/phases/light.js +99 -0
  68. package/dist/consolidator/phases/light.js.map +1 -0
  69. package/dist/consolidator/phases/reflect.js +287 -0
  70. package/dist/consolidator/phases/reflect.js.map +1 -0
  71. package/dist/consolidator/phases/standard.d.ts +8 -0
  72. package/dist/consolidator/phases/standard.js +499 -0
  73. package/dist/consolidator/phases/standard.js.map +1 -0
  74. package/dist/consolidator/reconcile.js +192 -0
  75. package/dist/consolidator/reconcile.js.map +1 -0
  76. package/dist/consolidator/runtime.d.ts +62 -0
  77. package/dist/consolidator/runtime.d.ts.map +1 -0
  78. package/dist/consolidator/runtime.js +580 -0
  79. package/dist/consolidator/runtime.js.map +1 -0
  80. package/dist/consolidator/scheduler.d.ts +111 -0
  81. package/dist/consolidator/scheduler.d.ts.map +1 -0
  82. package/dist/consolidator/scheduler.js +71 -0
  83. package/dist/consolidator/scheduler.js.map +1 -0
  84. package/dist/consolidator/triggers.d.ts +40 -0
  85. package/dist/consolidator/triggers.d.ts.map +1 -0
  86. package/dist/consolidator/triggers.js +94 -0
  87. package/dist/consolidator/triggers.js.map +1 -0
  88. package/dist/consolidator/types.d.ts +397 -0
  89. package/dist/consolidator/types.d.ts.map +1 -0
  90. package/dist/consolidator/types.js +117 -0
  91. package/dist/consolidator/types.js.map +1 -0
  92. package/dist/context-engine/annotations.d.ts +118 -0
  93. package/dist/context-engine/annotations.d.ts.map +1 -0
  94. package/dist/context-engine/annotations.js +102 -0
  95. package/dist/context-engine/annotations.js.map +1 -0
  96. package/dist/context-engine/auto-recall.d.ts +52 -0
  97. package/dist/context-engine/auto-recall.d.ts.map +1 -0
  98. package/dist/context-engine/auto-recall.js +41 -0
  99. package/dist/context-engine/auto-recall.js.map +1 -0
  100. package/dist/context-engine/compaction/clear-tool-results.d.ts +55 -0
  101. package/dist/context-engine/compaction/clear-tool-results.d.ts.map +1 -0
  102. package/dist/context-engine/compaction/clear-tool-results.js +97 -0
  103. package/dist/context-engine/compaction/clear-tool-results.js.map +1 -0
  104. package/dist/context-engine/compaction/compactor.d.ts +52 -0
  105. package/dist/context-engine/compaction/compactor.d.ts.map +1 -0
  106. package/dist/context-engine/compaction/compactor.js +196 -0
  107. package/dist/context-engine/compaction/compactor.js.map +1 -0
  108. package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts +15 -0
  109. package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts.map +1 -0
  110. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js +27 -0
  111. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js.map +1 -0
  112. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts +18 -0
  113. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts.map +1 -0
  114. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js +47 -0
  115. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js.map +1 -0
  116. package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts +15 -0
  117. package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts.map +1 -0
  118. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js +32 -0
  119. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js.map +1 -0
  120. package/dist/context-engine/compaction/hooks/types.d.ts +42 -0
  121. package/dist/context-engine/compaction/hooks/types.d.ts.map +1 -0
  122. package/dist/context-engine/compaction/index.d.ts +9 -0
  123. package/dist/context-engine/compaction/index.js +9 -0
  124. package/dist/context-engine/compaction/templates/summary-9-section.d.ts +84 -0
  125. package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -0
  126. package/dist/context-engine/compaction/templates/summary-9-section.js +84 -0
  127. package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -0
  128. package/dist/context-engine/compaction/thresholds.d.ts +68 -0
  129. package/dist/context-engine/compaction/thresholds.d.ts.map +1 -0
  130. package/dist/context-engine/compaction/thresholds.js +63 -0
  131. package/dist/context-engine/compaction/thresholds.js.map +1 -0
  132. package/dist/context-engine/compaction/types.d.ts +202 -0
  133. package/dist/context-engine/compaction/types.d.ts.map +1 -0
  134. package/dist/context-engine/engine.d.ts +258 -0
  135. package/dist/context-engine/engine.d.ts.map +1 -0
  136. package/dist/context-engine/engine.js +424 -0
  137. package/dist/context-engine/engine.js.map +1 -0
  138. package/dist/context-engine/index.d.ts +68 -0
  139. package/dist/context-engine/index.d.ts.map +1 -0
  140. package/dist/context-engine/index.js +24 -0
  141. package/dist/context-engine/locale-packs/en.d.ts +14 -0
  142. package/dist/context-engine/locale-packs/en.d.ts.map +1 -0
  143. package/dist/context-engine/locale-packs/en.js +87 -0
  144. package/dist/context-engine/locale-packs/en.js.map +1 -0
  145. package/dist/context-engine/locale-packs/index.js +4 -0
  146. package/dist/context-engine/locale-packs/resolver.d.ts +41 -0
  147. package/dist/context-engine/locale-packs/resolver.d.ts.map +1 -0
  148. package/dist/context-engine/locale-packs/resolver.js +119 -0
  149. package/dist/context-engine/locale-packs/resolver.js.map +1 -0
  150. package/dist/context-engine/locale-packs/types.d.ts +119 -0
  151. package/dist/context-engine/locale-packs/types.d.ts.map +1 -0
  152. package/dist/context-engine/locale-packs/types.js +24 -0
  153. package/dist/context-engine/locale-packs/types.js.map +1 -0
  154. package/dist/context-engine/metadata.d.ts +40 -0
  155. package/dist/context-engine/metadata.d.ts.map +1 -0
  156. package/dist/context-engine/metadata.js +66 -0
  157. package/dist/context-engine/metadata.js.map +1 -0
  158. package/dist/context-engine/preambles/inbound-en.d.ts +20 -0
  159. package/dist/context-engine/preambles/inbound-en.d.ts.map +1 -0
  160. package/dist/context-engine/preambles/inbound-en.js +23 -0
  161. package/dist/context-engine/preambles/inbound-en.js.map +1 -0
  162. package/dist/context-engine/privacy-filter.d.ts +108 -0
  163. package/dist/context-engine/privacy-filter.d.ts.map +1 -0
  164. package/dist/context-engine/privacy-filter.js +113 -0
  165. package/dist/context-engine/privacy-filter.js.map +1 -0
  166. package/dist/context-engine/templates/base-en.d.ts +19 -0
  167. package/dist/context-engine/templates/base-en.d.ts.map +1 -0
  168. package/dist/context-engine/templates/base-en.js +22 -0
  169. package/dist/context-engine/templates/base-en.js.map +1 -0
  170. package/dist/context-engine/templates/composer.d.ts +55 -0
  171. package/dist/context-engine/templates/composer.d.ts.map +1 -0
  172. package/dist/context-engine/templates/composer.js +51 -0
  173. package/dist/context-engine/templates/composer.js.map +1 -0
  174. package/dist/context-engine/token-budget.d.ts +99 -0
  175. package/dist/context-engine/token-budget.d.ts.map +1 -0
  176. package/dist/context-engine/token-budget.js +211 -0
  177. package/dist/context-engine/token-budget.js.map +1 -0
  178. package/dist/context-engine/token-counter.d.ts +54 -0
  179. package/dist/context-engine/token-counter.d.ts.map +1 -0
  180. package/dist/context-engine/token-counter.js +81 -0
  181. package/dist/context-engine/token-counter.js.map +1 -0
  182. package/dist/errors/index.d.ts +171 -0
  183. package/dist/errors/index.d.ts.map +1 -0
  184. package/dist/errors/index.js +206 -0
  185. package/dist/errors/index.js.map +1 -0
  186. package/dist/facade.d.ts +278 -0
  187. package/dist/facade.d.ts.map +1 -0
  188. package/dist/facade.js +265 -0
  189. package/dist/facade.js.map +1 -0
  190. package/dist/graph/entity-resolver.d.ts +135 -0
  191. package/dist/graph/entity-resolver.d.ts.map +1 -0
  192. package/dist/graph/entity-resolver.js +202 -0
  193. package/dist/graph/entity-resolver.js.map +1 -0
  194. package/dist/graph/index.d.ts +1 -0
  195. package/dist/index.d.ts +103 -0
  196. package/dist/index.d.ts.map +1 -0
  197. package/dist/index.js +95 -0
  198. package/dist/index.js.map +1 -0
  199. package/dist/internal/contextualize.d.ts +14 -0
  200. package/dist/internal/contextualize.d.ts.map +1 -0
  201. package/dist/internal/contextualize.js +110 -0
  202. package/dist/internal/contextualize.js.map +1 -0
  203. package/dist/internal/embedder-binding.js +50 -0
  204. package/dist/internal/embedder-binding.js.map +1 -0
  205. package/dist/internal/id.js +24 -0
  206. package/dist/internal/id.js.map +1 -0
  207. package/dist/internal/injection-heuristics.js +64 -0
  208. package/dist/internal/injection-heuristics.js.map +1 -0
  209. package/dist/internal/spans.js +35 -0
  210. package/dist/internal/spans.js.map +1 -0
  211. package/dist/internal/storage-adapter.d.ts +742 -0
  212. package/dist/internal/storage-adapter.d.ts.map +1 -0
  213. package/dist/migration/embedder-migration.d.ts +124 -0
  214. package/dist/migration/embedder-migration.d.ts.map +1 -0
  215. package/dist/migration/embedder-migration.js +109 -0
  216. package/dist/migration/embedder-migration.js.map +1 -0
  217. package/dist/migration/index.d.ts +2 -0
  218. package/dist/migration/index.js +3 -0
  219. package/dist/search/explain.d.ts +74 -0
  220. package/dist/search/explain.d.ts.map +1 -0
  221. package/dist/search/explain.js +50 -0
  222. package/dist/search/explain.js.map +1 -0
  223. package/dist/search/index.d.ts +6 -0
  224. package/dist/search/index.js +6 -0
  225. package/dist/search/iterative.d.ts +223 -0
  226. package/dist/search/iterative.d.ts.map +1 -0
  227. package/dist/search/iterative.js +330 -0
  228. package/dist/search/iterative.js.map +1 -0
  229. package/dist/search/query-transform.d.ts +112 -0
  230. package/dist/search/query-transform.d.ts.map +1 -0
  231. package/dist/search/query-transform.js +184 -0
  232. package/dist/search/query-transform.js.map +1 -0
  233. package/dist/search/rrf.d.ts +95 -0
  234. package/dist/search/rrf.d.ts.map +1 -0
  235. package/dist/search/rrf.js +166 -0
  236. package/dist/search/rrf.js.map +1 -0
  237. package/dist/search/types.d.ts +50 -0
  238. package/dist/search/types.d.ts.map +1 -0
  239. package/dist/tiers/episodic-memory.d.ts +142 -0
  240. package/dist/tiers/episodic-memory.d.ts.map +1 -0
  241. package/dist/tiers/episodic-memory.js +254 -0
  242. package/dist/tiers/episodic-memory.js.map +1 -0
  243. package/dist/tiers/index.d.ts +8 -0
  244. package/dist/tiers/index.js +9 -0
  245. package/dist/tiers/insight-memory.d.ts +71 -0
  246. package/dist/tiers/insight-memory.d.ts.map +1 -0
  247. package/dist/tiers/insight-memory.js +133 -0
  248. package/dist/tiers/insight-memory.js.map +1 -0
  249. package/dist/tiers/procedural-memory.d.ts +147 -0
  250. package/dist/tiers/procedural-memory.d.ts.map +1 -0
  251. package/dist/tiers/procedural-memory.js +267 -0
  252. package/dist/tiers/procedural-memory.js.map +1 -0
  253. package/dist/tiers/semantic-memory.d.ts +461 -0
  254. package/dist/tiers/semantic-memory.d.ts.map +1 -0
  255. package/dist/tiers/semantic-memory.js +631 -0
  256. package/dist/tiers/semantic-memory.js.map +1 -0
  257. package/dist/tiers/session-memory.d.ts +117 -0
  258. package/dist/tiers/session-memory.d.ts.map +1 -0
  259. package/dist/tiers/session-memory.js +163 -0
  260. package/dist/tiers/session-memory.js.map +1 -0
  261. package/dist/tiers/shared-memory.d.ts +29 -0
  262. package/dist/tiers/shared-memory.d.ts.map +1 -0
  263. package/dist/tiers/shared-memory.js +58 -0
  264. package/dist/tiers/shared-memory.js.map +1 -0
  265. package/dist/tiers/working-memory.d.ts +129 -0
  266. package/dist/tiers/working-memory.d.ts.map +1 -0
  267. package/dist/tiers/working-memory.js +229 -0
  268. package/dist/tiers/working-memory.js.map +1 -0
  269. package/dist/tools/block-tools.d.ts +98 -0
  270. package/dist/tools/block-tools.d.ts.map +1 -0
  271. package/dist/tools/block-tools.js +114 -0
  272. package/dist/tools/block-tools.js.map +1 -0
  273. package/dist/tools/fact-tools.d.ts +294 -0
  274. package/dist/tools/fact-tools.d.ts.map +1 -0
  275. package/dist/tools/fact-tools.js +293 -0
  276. package/dist/tools/fact-tools.js.map +1 -0
  277. package/dist/tools/index.d.ts +35 -0
  278. package/dist/tools/index.d.ts.map +1 -0
  279. package/dist/tools/index.js +36 -0
  280. package/dist/tools/index.js.map +1 -0
  281. package/dist/tools/recall-tools.d.ts +209 -0
  282. package/dist/tools/recall-tools.d.ts.map +1 -0
  283. package/dist/tools/recall-tools.js +170 -0
  284. package/dist/tools/recall-tools.js.map +1 -0
  285. package/dist/tools/types.d.ts +40 -0
  286. package/dist/tools/types.d.ts.map +1 -0
  287. package/package.json +119 -0
@@ -0,0 +1,55 @@
1
+ import { ContextLocalePack } from "../locale-packs/types.js";
2
+
3
+ //#region src/context-engine/templates/composer.d.ts
4
+
5
+ /**
6
+ * Layer 1 mode discriminator. `'full'` ships the full educational
7
+ * template (~250-350 tok); `'minimal'` ships the shortened version
8
+ * (~80-120 tok) for top-tier models.
9
+ *
10
+ * @stable
11
+ */
12
+ type MemoryBaseMode = 'full' | 'minimal';
13
+ /**
14
+ * Compose Layer 1 from the resolved locale pack.
15
+ *
16
+ * @stable
17
+ */
18
+ declare function composeLayer1(pack: ContextLocalePack, mode: MemoryBaseMode): string;
19
+ /**
20
+ * Render the optional Layer 2 (`agent_instructions`) wrapper.
21
+ * Returns the empty string when `instructions` is empty so the
22
+ * layer is dropped from the assembled prompt.
23
+ *
24
+ * @stable
25
+ */
26
+ declare function composeLayer2(instructions: string | undefined): string;
27
+ /**
28
+ * Render Layer 4 skills metadata cards. Each entry is a
29
+ * `<skill ... />` self-closing tag so the model sees only the
30
+ * progressive-disclosure metadata — the full SKILL.md body is
31
+ * loaded on-demand via `activate_skill(name)` per RB-04.
32
+ *
33
+ * @stable
34
+ */
35
+ interface SkillMetadataCard {
36
+ readonly name: string;
37
+ readonly description: string;
38
+ readonly location?: string;
39
+ /** When `true`, the card is excluded from the assembled prompt. */
40
+ readonly disableModelInvocation?: boolean;
41
+ }
42
+ declare function composeLayer4Skills(cards: ReadonlyArray<SkillMetadataCard>): string;
43
+ /**
44
+ * Render the inbound-sanitization preamble fragment. Emitted
45
+ * AFTER the cache breakpoint so the Layer 1-4 cache prefix is
46
+ * unaffected. Caller threads the fragment into the system content
47
+ * post-Layer 5; the fragment is returned as a string so the
48
+ * caller does not need to know the cache-breakpoint policy.
49
+ *
50
+ * @stable
51
+ */
52
+ declare function composeInboundPreamble(pack: ContextLocalePack): string;
53
+ //#endregion
54
+ export { MemoryBaseMode, SkillMetadataCard, composeInboundPreamble, composeLayer1, composeLayer2, composeLayer4Skills };
55
+ //# sourceMappingURL=composer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.d.ts","names":[],"sources":["../../../src/context-engine/templates/composer.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;KAkBY,cAAA;;;;;;iBAOI,aAAA,OAAoB,yBAAyB;;;;;;;;iBAW7C,aAAA;;;;;;;;;UAaC,iBAAA;;;;;;;iBAQD,mBAAA,QAA2B,cAAc;;;;;;;;;;iBAuBzC,sBAAA,OAA6B"}
@@ -0,0 +1,51 @@
1
+ //#region src/context-engine/templates/composer.ts
2
+ /**
3
+ * Compose Layer 1 from the resolved locale pack.
4
+ *
5
+ * @stable
6
+ */
7
+ function composeLayer1(pack, mode) {
8
+ return mode === "minimal" ? pack.baseTemplate.minimal : pack.baseTemplate.full;
9
+ }
10
+ /**
11
+ * Render the optional Layer 2 (`agent_instructions`) wrapper.
12
+ * Returns the empty string when `instructions` is empty so the
13
+ * layer is dropped from the assembled prompt.
14
+ *
15
+ * @stable
16
+ */
17
+ function composeLayer2(instructions) {
18
+ if (instructions === void 0 || instructions.trim().length === 0) return "";
19
+ return `<agent_instructions>\n${instructions}\n</agent_instructions>`;
20
+ }
21
+ function composeLayer4Skills(cards) {
22
+ const visible = cards.filter((c) => c.disableModelInvocation !== true);
23
+ if (visible.length === 0) return "";
24
+ const lines = ["<skills_available>"];
25
+ for (const card of visible) {
26
+ const description = ` description="${escapeXmlAttr(card.description)}"`;
27
+ const location = card.location !== void 0 ? ` location="${escapeXmlAttr(card.location)}"` : "";
28
+ lines.push(` <skill name="${escapeXmlAttr(card.name)}"${description}${location} />`);
29
+ }
30
+ lines.push("</skills_available>");
31
+ return lines.join("\n");
32
+ }
33
+ /**
34
+ * Render the inbound-sanitization preamble fragment. Emitted
35
+ * AFTER the cache breakpoint so the Layer 1-4 cache prefix is
36
+ * unaffected. Caller threads the fragment into the system content
37
+ * post-Layer 5; the fragment is returned as a string so the
38
+ * caller does not need to know the cache-breakpoint policy.
39
+ *
40
+ * @stable
41
+ */
42
+ function composeInboundPreamble(pack) {
43
+ return pack.inboundSanitizationPreamble.text;
44
+ }
45
+ function escapeXmlAttr(value) {
46
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
47
+ }
48
+
49
+ //#endregion
50
+ export { composeInboundPreamble, composeLayer1, composeLayer2, composeLayer4Skills };
51
+ //# sourceMappingURL=composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.js","names":[],"sources":["../../../src/context-engine/templates/composer.ts"],"sourcesContent":["/**\n * Layer 1 base-template composer. Resolves the locale pack, picks\n * the right base fragment based on `memoryBaseMode`, and exposes\n * the per-step inbound preamble as an additive Layer 5/6 fragment\n * (so the Layer 1-4 cache prefix stays bytes-equal across the run).\n *\n * @packageDocumentation\n */\n\nimport type { ContextLocalePack } from '../locale-packs/index.js';\n\n/**\n * Layer 1 mode discriminator. `'full'` ships the full educational\n * template (~250-350 tok); `'minimal'` ships the shortened version\n * (~80-120 tok) for top-tier models.\n *\n * @stable\n */\nexport type MemoryBaseMode = 'full' | 'minimal';\n\n/**\n * Compose Layer 1 from the resolved locale pack.\n *\n * @stable\n */\nexport function composeLayer1(pack: ContextLocalePack, mode: MemoryBaseMode): string {\n return mode === 'minimal' ? pack.baseTemplate.minimal : pack.baseTemplate.full;\n}\n\n/**\n * Render the optional Layer 2 (`agent_instructions`) wrapper.\n * Returns the empty string when `instructions` is empty so the\n * layer is dropped from the assembled prompt.\n *\n * @stable\n */\nexport function composeLayer2(instructions: string | undefined): string {\n if (instructions === undefined || instructions.trim().length === 0) return '';\n return `<agent_instructions>\\n${instructions}\\n</agent_instructions>`;\n}\n\n/**\n * Render Layer 4 skills metadata cards. Each entry is a\n * `<skill ... />` self-closing tag so the model sees only the\n * progressive-disclosure metadata — the full SKILL.md body is\n * loaded on-demand via `activate_skill(name)` per RB-04.\n *\n * @stable\n */\nexport interface SkillMetadataCard {\n readonly name: string;\n readonly description: string;\n readonly location?: string;\n /** When `true`, the card is excluded from the assembled prompt. */\n readonly disableModelInvocation?: boolean;\n}\n\nexport function composeLayer4Skills(cards: ReadonlyArray<SkillMetadataCard>): string {\n const visible = cards.filter((c) => c.disableModelInvocation !== true);\n if (visible.length === 0) return '';\n const lines = ['<skills_available>'];\n for (const card of visible) {\n const description = ` description=\"${escapeXmlAttr(card.description)}\"`;\n const location =\n card.location !== undefined ? ` location=\"${escapeXmlAttr(card.location)}\"` : '';\n lines.push(` <skill name=\"${escapeXmlAttr(card.name)}\"${description}${location} />`);\n }\n lines.push('</skills_available>');\n return lines.join('\\n');\n}\n\n/**\n * Render the inbound-sanitization preamble fragment. Emitted\n * AFTER the cache breakpoint so the Layer 1-4 cache prefix is\n * unaffected. Caller threads the fragment into the system content\n * post-Layer 5; the fragment is returned as a string so the\n * caller does not need to know the cache-breakpoint policy.\n *\n * @stable\n */\nexport function composeInboundPreamble(pack: ContextLocalePack): string {\n return pack.inboundSanitizationPreamble.text;\n}\n\nfunction escapeXmlAttr(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n"],"mappings":";;;;;;AAyBA,SAAgB,cAAc,MAAyB,MAA8B;AACnF,QAAO,SAAS,YAAY,KAAK,aAAa,UAAU,KAAK,aAAa;;;;;;;;;AAU5E,SAAgB,cAAc,cAA0C;AACtE,KAAI,iBAAiB,UAAa,aAAa,MAAM,CAAC,WAAW,EAAG,QAAO;AAC3E,QAAO,yBAAyB,aAAa;;AAmB/C,SAAgB,oBAAoB,OAAiD;CACnF,MAAM,UAAU,MAAM,QAAQ,MAAM,EAAE,2BAA2B,KAAK;AACtE,KAAI,QAAQ,WAAW,EAAG,QAAO;CACjC,MAAM,QAAQ,CAAC,qBAAqB;AACpC,MAAK,MAAM,QAAQ,SAAS;EAC1B,MAAM,cAAc,iBAAiB,cAAc,KAAK,YAAY,CAAC;EACrE,MAAM,WACJ,KAAK,aAAa,SAAY,cAAc,cAAc,KAAK,SAAS,CAAC,KAAK;AAChF,QAAM,KAAK,kBAAkB,cAAc,KAAK,KAAK,CAAC,GAAG,cAAc,SAAS,KAAK;;AAEvF,OAAM,KAAK,sBAAsB;AACjC,QAAO,MAAM,KAAK,KAAK;;;;;;;;;;;AAYzB,SAAgB,uBAAuB,MAAiC;AACtE,QAAO,KAAK,4BAA4B;;AAG1C,SAAS,cAAc,OAAuB;AAC5C,QAAO,MACJ,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,SAAS"}
@@ -0,0 +1,99 @@
1
+ import { ContextTokenCounter } from "./token-counter.js";
2
+
3
+ //#region src/context-engine/token-budget.d.ts
4
+
5
+ /**
6
+ * Layer-id discriminator. Mirrors the documented Layer 1-6
7
+ * structure of the layered system prompt.
8
+ *
9
+ * @stable
10
+ */
11
+ type LayerId = 'identity' | 'memoryMetadata' | 'activeRules' | 'workingBlocks' | 'activeSkills' | 'autoRecall';
12
+ /**
13
+ * Default priority ladder. Higher priority means the layer is
14
+ * preserved longer when the budget is tight.
15
+ *
16
+ * @stable
17
+ */
18
+ declare const DEFAULT_LAYER_PRIORITY: ReadonlyArray<LayerId>;
19
+ /**
20
+ * Single layer candidate fed to {@link allocate}. The `text` field
21
+ * carries the rendered fragment; `cap` is the optional per-layer
22
+ * upper bound (in tokens); `priority` overrides the default ladder
23
+ * for advanced use cases.
24
+ *
25
+ * @stable
26
+ */
27
+ interface LayerCandidate {
28
+ readonly id: LayerId;
29
+ readonly text: string;
30
+ readonly cap?: number;
31
+ readonly priority?: number;
32
+ }
33
+ /**
34
+ * Per-layer truncation mode used when the layer overflows its cap
35
+ * or the global budget. `truncate` is the default; `drop` removes
36
+ * the layer entirely when it would otherwise overflow.
37
+ *
38
+ * @stable
39
+ */
40
+ type OverflowMode = 'truncate' | 'drop';
41
+ /**
42
+ * Output of {@link allocate} — one entry per surviving layer in
43
+ * priority order, plus a `truncated` flag for observability.
44
+ *
45
+ * @stable
46
+ */
47
+ interface LayerAllocation {
48
+ readonly id: LayerId;
49
+ readonly text: string;
50
+ readonly tokens: number;
51
+ readonly truncated: boolean;
52
+ readonly droppedTokens: number;
53
+ }
54
+ /**
55
+ * Result of an allocator run.
56
+ *
57
+ * @stable
58
+ */
59
+ interface AllocationResult {
60
+ readonly layers: ReadonlyArray<LayerAllocation>;
61
+ readonly totalTokens: number;
62
+ readonly budgetTokens: number;
63
+ readonly overflowDropped: ReadonlyArray<LayerId>;
64
+ }
65
+ /**
66
+ * Truncate `text` to fit `maxTokens`, preserving the leading
67
+ * portion and replacing the trailing portion with the literal
68
+ * `[...truncated]` marker. The token estimate is computed via the
69
+ * supplied `counter`; truncation falls back to character-based
70
+ * trimming when the estimate is non-monotonic.
71
+ *
72
+ * Structure-aware (CE-16e): the cut never splits a tag, and block
73
+ * tags the cut leaves open are re-closed after the marker, so a
74
+ * capped layer of XML-ish markup (e.g. `<memory_blocks>`) stays
75
+ * well-formed in the assembled prompt. Plain strings are unaffected.
76
+ *
77
+ * @stable
78
+ */
79
+ declare function truncateToTokens(text: string, maxTokens: number, counter: ContextTokenCounter): Promise<{
80
+ readonly text: string;
81
+ readonly tokens: number;
82
+ readonly truncated: boolean;
83
+ }>;
84
+ /**
85
+ * Run the allocator. Layers are sorted by priority ascending (the
86
+ * first layer is the highest priority); when the running total
87
+ * exceeds the budget, lower-priority layers are first capped at
88
+ * their per-layer `cap` (when set) and finally truncated to
89
+ * whatever fits.
90
+ *
91
+ * @stable
92
+ */
93
+ declare function allocate(layers: ReadonlyArray<LayerCandidate>, budgetTokens: number, counter: ContextTokenCounter, options?: {
94
+ readonly priority?: ReadonlyArray<LayerId>;
95
+ readonly overflowMode?: OverflowMode;
96
+ }): Promise<AllocationResult>;
97
+ //#endregion
98
+ export { AllocationResult, DEFAULT_LAYER_PRIORITY, LayerAllocation, LayerCandidate, LayerId, OverflowMode, allocate, truncateToTokens };
99
+ //# sourceMappingURL=token-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.d.ts","names":[],"sources":["../../src/context-engine/token-budget.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;KAiCY,OAAA;;;;;;;cAcC,wBAAwB,cAAc;;;;;;;;;UAiBlC,cAAA;eACF;;;;;;;;;;;;KAaH,YAAA;;;;;;;UAQK,eAAA;eACF;;;;;;;;;;;UAYE,gBAAA;mBACE,cAAc;;;4BAGL,cAAc;;;;;;;;;;;;;;;;iBAmDpB,gBAAA,2CAGX,sBACR;;;;;;;;;;;;;;iBAuDmB,QAAA,SACZ,cAAc,gDAEb;sBAEa,cAAc;0BACV;IAEzB,QAAQ"}
@@ -0,0 +1,211 @@
1
+ //#region src/context-engine/token-budget.ts
2
+ /**
3
+ * Default priority ladder. Higher priority means the layer is
4
+ * preserved longer when the budget is tight.
5
+ *
6
+ * @stable
7
+ */
8
+ const DEFAULT_LAYER_PRIORITY = Object.freeze([
9
+ "identity",
10
+ "memoryMetadata",
11
+ "activeRules",
12
+ "workingBlocks",
13
+ "activeSkills",
14
+ "autoRecall"
15
+ ]);
16
+ /**
17
+ * Tags opened-but-not-closed in `text`, innermost-first. A heuristic
18
+ * scanner sized for the engine's own XML-ish layer markup
19
+ * (`<memory_blocks>`, `<memory_rules>`, ...) — self-closing tags are
20
+ * skipped and an unmatched closer is ignored.
21
+ */
22
+ function unclosedTags(text) {
23
+ const stack = [];
24
+ const re = /<(\/?)([A-Za-z_][\w.-]*)(?:\s[^<>]*)?(\/?)>/g;
25
+ let match = re.exec(text);
26
+ while (match !== null) {
27
+ const [, closing, name, selfClosing] = match;
28
+ if (selfClosing !== "/" && name !== void 0) if (closing === "/") {
29
+ const idx = stack.lastIndexOf(name);
30
+ if (idx !== -1) stack.splice(idx, 1);
31
+ } else stack.push(name);
32
+ match = re.exec(text);
33
+ }
34
+ return stack.reverse();
35
+ }
36
+ /**
37
+ * Snap a prefix cut to a structure-safe point (CE-16e): never end
38
+ * inside a partially-emitted tag (`...<memory_blo`).
39
+ */
40
+ function snapCut(kept) {
41
+ const lastOpen = kept.lastIndexOf("<");
42
+ return lastOpen > kept.lastIndexOf(">") ? kept.slice(0, lastOpen) : kept;
43
+ }
44
+ /**
45
+ * Truncate `text` to fit `maxTokens`, preserving the leading
46
+ * portion and replacing the trailing portion with the literal
47
+ * `[...truncated]` marker. The token estimate is computed via the
48
+ * supplied `counter`; truncation falls back to character-based
49
+ * trimming when the estimate is non-monotonic.
50
+ *
51
+ * Structure-aware (CE-16e): the cut never splits a tag, and block
52
+ * tags the cut leaves open are re-closed after the marker, so a
53
+ * capped layer of XML-ish markup (e.g. `<memory_blocks>`) stays
54
+ * well-formed in the assembled prompt. Plain strings are unaffected.
55
+ *
56
+ * @stable
57
+ */
58
+ async function truncateToTokens(text, maxTokens, counter) {
59
+ if (maxTokens <= 0) return {
60
+ text: "",
61
+ tokens: 0,
62
+ truncated: text.length > 0
63
+ };
64
+ const initial = await counter.countText(text);
65
+ if (initial <= maxTokens) return {
66
+ text,
67
+ tokens: initial,
68
+ truncated: false
69
+ };
70
+ const marker = "\n[...truncated]";
71
+ const markerTokens = await counter.countText(marker);
72
+ const targetTokens = Math.max(0, maxTokens - markerTokens);
73
+ if (targetTokens <= 0) return {
74
+ text: marker,
75
+ tokens: markerTokens,
76
+ truncated: true
77
+ };
78
+ let lo = 0;
79
+ let hi = text.length;
80
+ let bestCut = 0;
81
+ while (lo <= hi) {
82
+ const mid = lo + hi >>> 1;
83
+ const slice = text.slice(0, mid);
84
+ if (await counter.countText(slice) <= targetTokens) {
85
+ bestCut = mid;
86
+ lo = mid + 1;
87
+ } else hi = mid - 1;
88
+ }
89
+ let kept = snapCut(text.slice(0, bestCut));
90
+ let closers = unclosedTags(kept).map((t) => `</${t}>`).join("");
91
+ let truncated = `${kept}${marker}${closers.length > 0 ? `\n${closers}` : ""}`;
92
+ let tokens = await counter.countText(truncated);
93
+ while (tokens > maxTokens && kept.length > 0) {
94
+ kept = snapCut(kept.slice(0, kept.length - Math.ceil(kept.length * .1)));
95
+ closers = unclosedTags(kept).map((t) => `</${t}>`).join("");
96
+ truncated = `${kept}${marker}${closers.length > 0 ? `\n${closers}` : ""}`;
97
+ tokens = await counter.countText(truncated);
98
+ }
99
+ return {
100
+ text: truncated,
101
+ tokens,
102
+ truncated: true
103
+ };
104
+ }
105
+ /**
106
+ * Run the allocator. Layers are sorted by priority ascending (the
107
+ * first layer is the highest priority); when the running total
108
+ * exceeds the budget, lower-priority layers are first capped at
109
+ * their per-layer `cap` (when set) and finally truncated to
110
+ * whatever fits.
111
+ *
112
+ * @stable
113
+ */
114
+ async function allocate(layers, budgetTokens, counter, options = {}) {
115
+ const priorityOrder = options.priority ?? DEFAULT_LAYER_PRIORITY;
116
+ const overflowMode = options.overflowMode ?? "truncate";
117
+ const orderIndex = /* @__PURE__ */ new Map();
118
+ priorityOrder.forEach((id, idx) => {
119
+ orderIndex.set(id, idx);
120
+ });
121
+ const sorted = [...layers].sort((a, b) => {
122
+ return (a.priority ?? orderIndex.get(a.id) ?? 999) - (b.priority ?? orderIndex.get(b.id) ?? 999);
123
+ });
124
+ const capped = [];
125
+ for (const candidate of sorted) {
126
+ if (candidate.text.length === 0) {
127
+ capped.push({
128
+ candidate,
129
+ text: "",
130
+ tokens: 0,
131
+ truncated: false
132
+ });
133
+ continue;
134
+ }
135
+ if (candidate.cap !== void 0 && candidate.cap >= 0) {
136
+ const trimmed = await truncateToTokens(candidate.text, candidate.cap, counter);
137
+ capped.push({
138
+ candidate,
139
+ text: trimmed.text,
140
+ tokens: trimmed.tokens,
141
+ truncated: trimmed.truncated
142
+ });
143
+ } else {
144
+ const tokens = await counter.countText(candidate.text);
145
+ capped.push({
146
+ candidate,
147
+ text: candidate.text,
148
+ tokens,
149
+ truncated: false
150
+ });
151
+ }
152
+ }
153
+ const budget = Math.max(0, budgetTokens);
154
+ const layersOut = [];
155
+ const overflowDropped = [];
156
+ let runningTotal = 0;
157
+ for (const entry of capped) {
158
+ if (entry.text.length === 0) {
159
+ layersOut.push({
160
+ id: entry.candidate.id,
161
+ text: "",
162
+ tokens: 0,
163
+ truncated: false,
164
+ droppedTokens: 0
165
+ });
166
+ continue;
167
+ }
168
+ if (runningTotal + entry.tokens <= budget) {
169
+ runningTotal += entry.tokens;
170
+ layersOut.push({
171
+ id: entry.candidate.id,
172
+ text: entry.text,
173
+ tokens: entry.tokens,
174
+ truncated: entry.truncated,
175
+ droppedTokens: 0
176
+ });
177
+ continue;
178
+ }
179
+ const remaining = Math.max(0, budget - runningTotal);
180
+ if (overflowMode === "drop" || remaining === 0) {
181
+ overflowDropped.push(entry.candidate.id);
182
+ layersOut.push({
183
+ id: entry.candidate.id,
184
+ text: "",
185
+ tokens: 0,
186
+ truncated: false,
187
+ droppedTokens: entry.tokens
188
+ });
189
+ continue;
190
+ }
191
+ const trimmed = await truncateToTokens(entry.text, remaining, counter);
192
+ runningTotal += trimmed.tokens;
193
+ layersOut.push({
194
+ id: entry.candidate.id,
195
+ text: trimmed.text,
196
+ tokens: trimmed.tokens,
197
+ truncated: true,
198
+ droppedTokens: entry.tokens - trimmed.tokens
199
+ });
200
+ }
201
+ return Object.freeze({
202
+ layers: Object.freeze(layersOut),
203
+ totalTokens: runningTotal,
204
+ budgetTokens: budget,
205
+ overflowDropped: Object.freeze(overflowDropped)
206
+ });
207
+ }
208
+
209
+ //#endregion
210
+ export { DEFAULT_LAYER_PRIORITY, allocate, truncateToTokens };
211
+ //# sourceMappingURL=token-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-budget.js","names":["DEFAULT_LAYER_PRIORITY: ReadonlyArray<LayerId>","stack: string[]","capped: Array<{\n readonly candidate: LayerCandidate;\n text: string;\n tokens: number;\n truncated: boolean;\n }>","layersOut: LayerAllocation[]","overflowDropped: LayerId[]"],"sources":["../../src/context-engine/token-budget.ts"],"sourcesContent":["/**\n * Priority-ordered token-budget allocator. The allocator runs over the six\n * layers of the {@link ContextEngine}'s system prompt and trims the\n * lowest-priority layers first when the combined cost exceeds the configured\n * budget.\n *\n * This **truncation priority** is deliberately separate from the **emission\n * order** (CE-9): the engine emits the surviving layers in a stable order\n * (identity → rules → blocks → skills → [KV-cache breakpoint] → metadata →\n * auto-recall) regardless of which were trimmed, so the cacheable prefix stays\n * byte-stable across turns. This ladder governs only *what gets dropped first*.\n *\n * Priority (configurable), highest preserved → first truncated:\n *\n * 1. **identity** — Layer 1 (`graphorin_memory_base` + agent\n * instructions). Always preserved; truncated last.\n * 2. **memoryMetadata** — Layer 5. Small (~100-300 tokens).\n * 3. **activeRules** — Layer 4 procedural rules.\n * 4. **workingBlocks** — Layer 3.\n * 5. **activeSkills** — Layer 4 skills metadata cards.\n * 6. **autoRecall** — Layer 6. Lowest priority; truncated first.\n *\n * @packageDocumentation\n */\n\nimport type { ContextTokenCounter } from './token-counter.js';\n\n/**\n * Layer-id discriminator. Mirrors the documented Layer 1-6\n * structure of the layered system prompt.\n *\n * @stable\n */\nexport type LayerId =\n | 'identity'\n | 'memoryMetadata'\n | 'activeRules'\n | 'workingBlocks'\n | 'activeSkills'\n | 'autoRecall';\n\n/**\n * Default priority ladder. Higher priority means the layer is\n * preserved longer when the budget is tight.\n *\n * @stable\n */\nexport const DEFAULT_LAYER_PRIORITY: ReadonlyArray<LayerId> = Object.freeze([\n 'identity',\n 'memoryMetadata',\n 'activeRules',\n 'workingBlocks',\n 'activeSkills',\n 'autoRecall',\n]);\n\n/**\n * Single layer candidate fed to {@link allocate}. The `text` field\n * carries the rendered fragment; `cap` is the optional per-layer\n * upper bound (in tokens); `priority` overrides the default ladder\n * for advanced use cases.\n *\n * @stable\n */\nexport interface LayerCandidate {\n readonly id: LayerId;\n readonly text: string;\n readonly cap?: number;\n readonly priority?: number;\n}\n\n/**\n * Per-layer truncation mode used when the layer overflows its cap\n * or the global budget. `truncate` is the default; `drop` removes\n * the layer entirely when it would otherwise overflow.\n *\n * @stable\n */\nexport type OverflowMode = 'truncate' | 'drop';\n\n/**\n * Output of {@link allocate} — one entry per surviving layer in\n * priority order, plus a `truncated` flag for observability.\n *\n * @stable\n */\nexport interface LayerAllocation {\n readonly id: LayerId;\n readonly text: string;\n readonly tokens: number;\n readonly truncated: boolean;\n readonly droppedTokens: number;\n}\n\n/**\n * Result of an allocator run.\n *\n * @stable\n */\nexport interface AllocationResult {\n readonly layers: ReadonlyArray<LayerAllocation>;\n readonly totalTokens: number;\n readonly budgetTokens: number;\n readonly overflowDropped: ReadonlyArray<LayerId>;\n}\n\n/**\n * Tags opened-but-not-closed in `text`, innermost-first. A heuristic\n * scanner sized for the engine's own XML-ish layer markup\n * (`<memory_blocks>`, `<memory_rules>`, ...) — self-closing tags are\n * skipped and an unmatched closer is ignored.\n */\nfunction unclosedTags(text: string): string[] {\n const stack: string[] = [];\n const re = /<(\\/?)([A-Za-z_][\\w.-]*)(?:\\s[^<>]*)?(\\/?)>/g;\n let match = re.exec(text);\n while (match !== null) {\n const [, closing, name, selfClosing] = match;\n if (selfClosing !== '/' && name !== undefined) {\n if (closing === '/') {\n const idx = stack.lastIndexOf(name);\n if (idx !== -1) stack.splice(idx, 1);\n } else {\n stack.push(name);\n }\n }\n match = re.exec(text);\n }\n return stack.reverse();\n}\n\n/**\n * Snap a prefix cut to a structure-safe point (CE-16e): never end\n * inside a partially-emitted tag (`...<memory_blo`).\n */\nfunction snapCut(kept: string): string {\n const lastOpen = kept.lastIndexOf('<');\n return lastOpen > kept.lastIndexOf('>') ? kept.slice(0, lastOpen) : kept;\n}\n\n/**\n * Truncate `text` to fit `maxTokens`, preserving the leading\n * portion and replacing the trailing portion with the literal\n * `[...truncated]` marker. The token estimate is computed via the\n * supplied `counter`; truncation falls back to character-based\n * trimming when the estimate is non-monotonic.\n *\n * Structure-aware (CE-16e): the cut never splits a tag, and block\n * tags the cut leaves open are re-closed after the marker, so a\n * capped layer of XML-ish markup (e.g. `<memory_blocks>`) stays\n * well-formed in the assembled prompt. Plain strings are unaffected.\n *\n * @stable\n */\nexport async function truncateToTokens(\n text: string,\n maxTokens: number,\n counter: ContextTokenCounter,\n): Promise<{ readonly text: string; readonly tokens: number; readonly truncated: boolean }> {\n if (maxTokens <= 0) return { text: '', tokens: 0, truncated: text.length > 0 };\n const initial = await counter.countText(text);\n if (initial <= maxTokens) return { text, tokens: initial, truncated: false };\n const marker = '\\n[...truncated]';\n const markerTokens = await counter.countText(marker);\n const targetTokens = Math.max(0, maxTokens - markerTokens);\n if (targetTokens <= 0) return { text: marker, tokens: markerTokens, truncated: true };\n // Approximate the cut point via the inverse of the heuristic\n // chars-per-token ratio. Falls back to repeated halving when the\n // ratio is non-monotonic for the supplied counter.\n let lo = 0;\n let hi = text.length;\n let bestCut = 0;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n const slice = text.slice(0, mid);\n const tokens = await counter.countText(slice);\n if (tokens <= targetTokens) {\n bestCut = mid;\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n let kept = snapCut(text.slice(0, bestCut));\n let closers = unclosedTags(kept)\n .map((t) => `</${t}>`)\n .join('');\n let truncated = `${kept}${marker}${closers.length > 0 ? `\\n${closers}` : ''}`;\n let tokens = await counter.countText(truncated);\n // Re-closing may push past the cap — shave the kept body until the\n // whole thing (body + marker + closers) fits. Terminates: `kept`\n // shrinks by ≥1 char per pass, and the empty body fits by the\n // `targetTokens > 0` guard above.\n while (tokens > maxTokens && kept.length > 0) {\n kept = snapCut(kept.slice(0, kept.length - Math.ceil(kept.length * 0.1)));\n closers = unclosedTags(kept)\n .map((t) => `</${t}>`)\n .join('');\n truncated = `${kept}${marker}${closers.length > 0 ? `\\n${closers}` : ''}`;\n tokens = await counter.countText(truncated);\n }\n return { text: truncated, tokens, truncated: true };\n}\n\n/**\n * Run the allocator. Layers are sorted by priority ascending (the\n * first layer is the highest priority); when the running total\n * exceeds the budget, lower-priority layers are first capped at\n * their per-layer `cap` (when set) and finally truncated to\n * whatever fits.\n *\n * @stable\n */\nexport async function allocate(\n layers: ReadonlyArray<LayerCandidate>,\n budgetTokens: number,\n counter: ContextTokenCounter,\n options: {\n readonly priority?: ReadonlyArray<LayerId>;\n readonly overflowMode?: OverflowMode;\n } = {},\n): Promise<AllocationResult> {\n const priorityOrder = options.priority ?? DEFAULT_LAYER_PRIORITY;\n const overflowMode = options.overflowMode ?? 'truncate';\n const orderIndex = new Map<LayerId, number>();\n priorityOrder.forEach((id, idx) => {\n orderIndex.set(id, idx);\n });\n const sorted = [...layers].sort((a, b) => {\n const aPriority = a.priority ?? orderIndex.get(a.id) ?? 999;\n const bPriority = b.priority ?? orderIndex.get(b.id) ?? 999;\n return aPriority - bPriority;\n });\n\n // First pass: enforce per-layer caps (regardless of total budget).\n const capped: Array<{\n readonly candidate: LayerCandidate;\n text: string;\n tokens: number;\n truncated: boolean;\n }> = [];\n for (const candidate of sorted) {\n if (candidate.text.length === 0) {\n capped.push({ candidate, text: '', tokens: 0, truncated: false });\n continue;\n }\n if (candidate.cap !== undefined && candidate.cap >= 0) {\n const trimmed = await truncateToTokens(candidate.text, candidate.cap, counter);\n capped.push({\n candidate,\n text: trimmed.text,\n tokens: trimmed.tokens,\n truncated: trimmed.truncated,\n });\n } else {\n const tokens = await counter.countText(candidate.text);\n capped.push({ candidate, text: candidate.text, tokens, truncated: false });\n }\n }\n\n // Second pass: enforce the global budget. Walk the layers in\n // priority order, accumulating tokens; when the running total\n // exceeds the budget, the rest are dropped or truncated to fit.\n const budget = Math.max(0, budgetTokens);\n const layersOut: LayerAllocation[] = [];\n const overflowDropped: LayerId[] = [];\n let runningTotal = 0;\n for (const entry of capped) {\n if (entry.text.length === 0) {\n layersOut.push({\n id: entry.candidate.id,\n text: '',\n tokens: 0,\n truncated: false,\n droppedTokens: 0,\n });\n continue;\n }\n if (runningTotal + entry.tokens <= budget) {\n runningTotal += entry.tokens;\n layersOut.push({\n id: entry.candidate.id,\n text: entry.text,\n tokens: entry.tokens,\n truncated: entry.truncated,\n droppedTokens: 0,\n });\n continue;\n }\n const remaining = Math.max(0, budget - runningTotal);\n if (overflowMode === 'drop' || remaining === 0) {\n overflowDropped.push(entry.candidate.id);\n layersOut.push({\n id: entry.candidate.id,\n text: '',\n tokens: 0,\n truncated: false,\n droppedTokens: entry.tokens,\n });\n continue;\n }\n const trimmed = await truncateToTokens(entry.text, remaining, counter);\n runningTotal += trimmed.tokens;\n layersOut.push({\n id: entry.candidate.id,\n text: trimmed.text,\n tokens: trimmed.tokens,\n truncated: true,\n droppedTokens: entry.tokens - trimmed.tokens,\n });\n }\n\n return Object.freeze({\n layers: Object.freeze(layersOut),\n totalTokens: runningTotal,\n budgetTokens: budget,\n overflowDropped: Object.freeze(overflowDropped),\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { Message, TokenCounter } from "@graphorin/core";
2
+
3
+ //#region src/context-engine/token-counter.d.ts
4
+
5
+ /**
6
+ * Pluggable text-token counter used inside the ContextEngine. The
7
+ * surface is narrower than {@link TokenCounter} because the engine
8
+ * never assembles a message list during the budget-allocation
9
+ * phase — it operates on rendered text fragments.
10
+ *
11
+ * @stable
12
+ */
13
+ interface ContextTokenCounter {
14
+ readonly id: string;
15
+ countText(text: string): Promise<number>;
16
+ }
17
+ /**
18
+ * Built-in heuristic counter — chars/4 for Latin-ish text plus one
19
+ * token per dense-script (CJK/kana/hangul) character (CE-13). Stable
20
+ * default when the operator does not pass a real {@link TokenCounter}.
21
+ *
22
+ * @stable
23
+ */
24
+ declare const HEURISTIC_TOKEN_COUNTER: ContextTokenCounter;
25
+ /**
26
+ * Wrap a real {@link TokenCounter} into the narrower
27
+ * {@link ContextTokenCounter} surface. Calls `countText(text)`
28
+ * directly for max precision; falls back to the synthetic
29
+ * single-message bridge when only `count(messages)` is supported.
30
+ *
31
+ * @stable
32
+ */
33
+ declare function adaptTokenCounter(counter: TokenCounter): ContextTokenCounter;
34
+ /**
35
+ * Count tokens across a message list using a {@link TokenCounter}
36
+ * (or the heuristic fallback). Used by the trigger-evaluation hot
37
+ * path of the auto-compaction subsystem (RB-46) at the top of every
38
+ * step.
39
+ *
40
+ * @stable
41
+ */
42
+ declare function countMessageTokens(messages: ReadonlyArray<Message>, counter: TokenCounter | ContextTokenCounter): Promise<number>;
43
+ /**
44
+ * Render a `Message` into a single textual approximation suitable
45
+ * for token counting. Multimodal parts other than `'text'` /
46
+ * `'reasoning'` contribute a constant approximation so the counter
47
+ * does not silently under-count.
48
+ *
49
+ * @stable
50
+ */
51
+ declare function renderMessageText(message: Message): string;
52
+ //#endregion
53
+ export { ContextTokenCounter, HEURISTIC_TOKEN_COUNTER, adaptTokenCounter, countMessageTokens, renderMessageText };
54
+ //# sourceMappingURL=token-counter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-counter.d.ts","names":[],"sources":["../../src/context-engine/token-counter.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;UA0BiB,mBAAA;;2BAEU;;;;;;;;;cAyBd,yBAAyB;;;;;;;;;iBAkBtB,iBAAA,UAA2B,eAAe;;;;;;;;;iBAkBpC,kBAAA,WACV,cAAc,mBACf,eAAe,sBACvB;;;;;;;;;iBAmBa,iBAAA,UAA2B"}
@@ -0,0 +1,81 @@
1
+ //#region src/context-engine/token-counter.ts
2
+ /**
3
+ * Script-aware heuristic (CE-13). The flat chars/4 rule undercounts
4
+ * non-Latin scripts ~4x — real tokenizers emit roughly one token per
5
+ * CJK character (and per kana / hangul syllable), so a 200k-window
6
+ * threshold expressed in "heuristic tokens" let CJK conversations blow
7
+ * past the provider limit before compaction ever fired. Characters
8
+ * outside the Latin-ish range count at one token each; Latin text keeps
9
+ * the classic chars/4. Still a heuristic — wire a real `TokenCounter`
10
+ * (e.g. the provider package's JsTiktokenCounter) for production
11
+ * accuracy.
12
+ */
13
+ const DENSE_SCRIPT_RE = /[ᄀ-ᇿ⺀-〿぀-ヿ㄰-㆏ㇰ-䶿一-鿿ꥠ-꥿가-퟿豈-﫿＀-￯]/g;
14
+ /**
15
+ * Built-in heuristic counter — chars/4 for Latin-ish text plus one
16
+ * token per dense-script (CJK/kana/hangul) character (CE-13). Stable
17
+ * default when the operator does not pass a real {@link TokenCounter}.
18
+ *
19
+ * @stable
20
+ */
21
+ const HEURISTIC_TOKEN_COUNTER = Object.freeze({
22
+ id: "graphorin/heuristic@chars-per-4+dense-script",
23
+ async countText(text) {
24
+ if (typeof text !== "string" || text.length === 0) return 0;
25
+ const dense = text.match(DENSE_SCRIPT_RE)?.length ?? 0;
26
+ const latin = text.length - dense;
27
+ return Math.ceil(latin / 4) + dense;
28
+ }
29
+ });
30
+ /**
31
+ * Wrap a real {@link TokenCounter} into the narrower
32
+ * {@link ContextTokenCounter} surface. Calls `countText(text)`
33
+ * directly for max precision; falls back to the synthetic
34
+ * single-message bridge when only `count(messages)` is supported.
35
+ *
36
+ * @stable
37
+ */
38
+ function adaptTokenCounter(counter) {
39
+ return Object.freeze({
40
+ id: counter.id,
41
+ async countText(text) {
42
+ if (typeof text !== "string" || text.length === 0) return 0;
43
+ return counter.countText(text);
44
+ }
45
+ });
46
+ }
47
+ /**
48
+ * Count tokens across a message list using a {@link TokenCounter}
49
+ * (or the heuristic fallback). Used by the trigger-evaluation hot
50
+ * path of the auto-compaction subsystem (RB-46) at the top of every
51
+ * step.
52
+ *
53
+ * @stable
54
+ */
55
+ async function countMessageTokens(messages, counter) {
56
+ if ("count" in counter && typeof counter.count === "function") return counter.count(messages);
57
+ let total = 0;
58
+ for (const message of messages) total += await counter.countText(renderMessageText(message));
59
+ return total;
60
+ }
61
+ /**
62
+ * Render a `Message` into a single textual approximation suitable
63
+ * for token counting. Multimodal parts other than `'text'` /
64
+ * `'reasoning'` contribute a constant approximation so the counter
65
+ * does not silently under-count.
66
+ *
67
+ * @stable
68
+ */
69
+ function renderMessageText(message) {
70
+ if (message.role === "system") return message.content;
71
+ const content = message.content;
72
+ if (typeof content === "string") return content;
73
+ let out = "";
74
+ for (const part of content) if (part.type === "text" || part.type === "reasoning") out += `${part.text}\n`;
75
+ else out += "[non-text-part]\n";
76
+ return out;
77
+ }
78
+
79
+ //#endregion
80
+ export { HEURISTIC_TOKEN_COUNTER, adaptTokenCounter, countMessageTokens, renderMessageText };
81
+ //# sourceMappingURL=token-counter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-counter.js","names":["HEURISTIC_TOKEN_COUNTER: ContextTokenCounter"],"sources":["../../src/context-engine/token-counter.ts"],"sourcesContent":["/**\n * Token-counting helpers used by the {@link ContextEngine}. The\n * production path accepts a {@link TokenCounter} from\n * `@graphorin/core` (the default `JsTiktokenCounter` lives in\n * `@graphorin/provider`); the fallback path is a deterministic\n * heuristic that keeps unit tests dependency-free.\n *\n * The fallback counts ~1 token per 4 characters — close enough for\n * budgeting decisions, intentionally less precise than a real BPE\n * tokenizer. Production deployments wire a real\n * {@link TokenCounter} through `createMemory({ contextEngine: {\n * tokenCounter: ... } })`.\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\n\n/**\n * Pluggable text-token counter used inside the ContextEngine. The\n * surface is narrower than {@link TokenCounter} because the engine\n * never assembles a message list during the budget-allocation\n * phase — it operates on rendered text fragments.\n *\n * @stable\n */\nexport interface ContextTokenCounter {\n readonly id: string;\n countText(text: string): Promise<number>;\n}\n\n/**\n * Script-aware heuristic (CE-13). The flat chars/4 rule undercounts\n * non-Latin scripts ~4x — real tokenizers emit roughly one token per\n * CJK character (and per kana / hangul syllable), so a 200k-window\n * threshold expressed in \"heuristic tokens\" let CJK conversations blow\n * past the provider limit before compaction ever fired. Characters\n * outside the Latin-ish range count at one token each; Latin text keeps\n * the classic chars/4. Still a heuristic — wire a real `TokenCounter`\n * (e.g. the provider package's JsTiktokenCounter) for production\n * accuracy.\n */\nconst DENSE_SCRIPT_RE =\n // CJK unified + extensions, kana, hangul, CJK punctuation, full-width forms.\n /[ᄀ-ᇿ⺀-〿぀-ヿ㄰-㆏ㇰ-䶿一-鿿ꥠ-꥿가-퟿豈-﫿＀-￯]/g;\n\n/**\n * Built-in heuristic counter — chars/4 for Latin-ish text plus one\n * token per dense-script (CJK/kana/hangul) character (CE-13). Stable\n * default when the operator does not pass a real {@link TokenCounter}.\n *\n * @stable\n */\nexport const HEURISTIC_TOKEN_COUNTER: ContextTokenCounter = Object.freeze({\n id: 'graphorin/heuristic@chars-per-4+dense-script',\n async countText(text: string): Promise<number> {\n if (typeof text !== 'string' || text.length === 0) return 0;\n const dense = text.match(DENSE_SCRIPT_RE)?.length ?? 0;\n const latin = text.length - dense;\n return Math.ceil(latin / 4) + dense;\n },\n});\n\n/**\n * Wrap a real {@link TokenCounter} into the narrower\n * {@link ContextTokenCounter} surface. Calls `countText(text)`\n * directly for max precision; falls back to the synthetic\n * single-message bridge when only `count(messages)` is supported.\n *\n * @stable\n */\nexport function adaptTokenCounter(counter: TokenCounter): ContextTokenCounter {\n return Object.freeze({\n id: counter.id,\n async countText(text: string): Promise<number> {\n if (typeof text !== 'string' || text.length === 0) return 0;\n return counter.countText(text);\n },\n });\n}\n\n/**\n * Count tokens across a message list using a {@link TokenCounter}\n * (or the heuristic fallback). Used by the trigger-evaluation hot\n * path of the auto-compaction subsystem (RB-46) at the top of every\n * step.\n *\n * @stable\n */\nexport async function countMessageTokens(\n messages: ReadonlyArray<Message>,\n counter: TokenCounter | ContextTokenCounter,\n): Promise<number> {\n if ('count' in counter && typeof (counter as TokenCounter).count === 'function') {\n return (counter as TokenCounter).count(messages);\n }\n let total = 0;\n for (const message of messages) {\n total += await counter.countText(renderMessageText(message));\n }\n return total;\n}\n\n/**\n * Render a `Message` into a single textual approximation suitable\n * for token counting. Multimodal parts other than `'text'` /\n * `'reasoning'` contribute a constant approximation so the counter\n * does not silently under-count.\n *\n * @stable\n */\nexport function renderMessageText(message: Message): string {\n if (message.role === 'system') return message.content;\n const content = message.content;\n if (typeof content === 'string') return content;\n let out = '';\n for (const part of content) {\n if (part.type === 'text' || part.type === 'reasoning') {\n out += `${part.text}\\n`;\n } else {\n // Approximate non-text parts (image / audio / file) as a small\n // fixed cost so the counter does not silently under-count.\n out += '[non-text-part]\\n';\n }\n }\n return out;\n}\n"],"mappings":";;;;;;;;;;;;AA0CA,MAAM,kBAEJ;;;;;;;;AASF,MAAaA,0BAA+C,OAAO,OAAO;CACxE,IAAI;CACJ,MAAM,UAAU,MAA+B;AAC7C,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG,QAAO;EAC1D,MAAM,QAAQ,KAAK,MAAM,gBAAgB,EAAE,UAAU;EACrD,MAAM,QAAQ,KAAK,SAAS;AAC5B,SAAO,KAAK,KAAK,QAAQ,EAAE,GAAG;;CAEjC,CAAC;;;;;;;;;AAUF,SAAgB,kBAAkB,SAA4C;AAC5E,QAAO,OAAO,OAAO;EACnB,IAAI,QAAQ;EACZ,MAAM,UAAU,MAA+B;AAC7C,OAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAAG,QAAO;AAC1D,UAAO,QAAQ,UAAU,KAAK;;EAEjC,CAAC;;;;;;;;;;AAWJ,eAAsB,mBACpB,UACA,SACiB;AACjB,KAAI,WAAW,WAAW,OAAQ,QAAyB,UAAU,WACnE,QAAQ,QAAyB,MAAM,SAAS;CAElD,IAAI,QAAQ;AACZ,MAAK,MAAM,WAAW,SACpB,UAAS,MAAM,QAAQ,UAAU,kBAAkB,QAAQ,CAAC;AAE9D,QAAO;;;;;;;;;;AAWT,SAAgB,kBAAkB,SAA0B;AAC1D,KAAI,QAAQ,SAAS,SAAU,QAAO,QAAQ;CAC9C,MAAM,UAAU,QAAQ;AACxB,KAAI,OAAO,YAAY,SAAU,QAAO;CACxC,IAAI,MAAM;AACV,MAAK,MAAM,QAAQ,QACjB,KAAI,KAAK,SAAS,UAAU,KAAK,SAAS,YACxC,QAAO,GAAG,KAAK,KAAK;KAIpB,QAAO;AAGX,QAAO"}