@jack200714/mafw 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (631) hide show
  1. package/.opencode/mafw/.backup-v61/parametric/base-skill-manifest.yaml +2 -0
  2. package/.opencode/mafw/STATUS.md +6 -0
  3. package/.opencode/mafw/ledger.md +20 -0
  4. package/.opencode/mafw/memory/.harmonic_index.json +28 -0
  5. package/.opencode/mafw/memory/.review_queue.json +1 -0
  6. package/.opencode/mafw/memory/tier3/parametric.json +13 -0
  7. package/.opencode/mafw/memory/tier3/test-goal.json +19 -0
  8. package/.opencode/mafw/memory-index.json +6 -0
  9. package/.opencode/mafw/parametric/base-skill-manifest.yaml +2 -0
  10. package/.opencode/mafw/waves.json +4 -0
  11. package/.opencode/opencode.json +3 -0
  12. package/.opencode/plugins/mafw-plugin.ts +8 -0
  13. package/.opencode/skills/banner-design/SKILL.md +196 -0
  14. package/.opencode/skills/banner-design/references/banner-sizes-and-styles.md +118 -0
  15. package/.opencode/skills/brand/SKILL.md +97 -0
  16. package/.opencode/skills/brand/references/approval-checklist.md +169 -0
  17. package/.opencode/skills/brand/references/asset-organization.md +157 -0
  18. package/.opencode/skills/brand/references/brand-guideline-template.md +140 -0
  19. package/.opencode/skills/brand/references/color-palette-management.md +186 -0
  20. package/.opencode/skills/brand/references/consistency-checklist.md +94 -0
  21. package/.opencode/skills/brand/references/logo-usage-rules.md +185 -0
  22. package/.opencode/skills/brand/references/messaging-framework.md +85 -0
  23. package/.opencode/skills/brand/references/typography-specifications.md +214 -0
  24. package/.opencode/skills/brand/references/update.md +118 -0
  25. package/.opencode/skills/brand/references/visual-identity.md +96 -0
  26. package/.opencode/skills/brand/references/voice-framework.md +88 -0
  27. package/.opencode/skills/brand/scripts/extract-colors.cjs +341 -0
  28. package/.opencode/skills/brand/scripts/inject-brand-context.cjs +349 -0
  29. package/.opencode/skills/brand/scripts/sync-brand-to-tokens.cjs +248 -0
  30. package/.opencode/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +52 -0
  31. package/.opencode/skills/brand/scripts/validate-asset.cjs +387 -0
  32. package/.opencode/skills/brand/templates/brand-guidelines-starter.md +275 -0
  33. package/.opencode/skills/design/SKILL.md +313 -0
  34. package/.opencode/skills/design/data/cip/deliverables.csv +51 -0
  35. package/.opencode/skills/design/data/cip/industries.csv +21 -0
  36. package/.opencode/skills/design/data/cip/mockup-contexts.csv +21 -0
  37. package/.opencode/skills/design/data/cip/styles.csv +21 -0
  38. package/.opencode/skills/design/data/icon/styles.csv +16 -0
  39. package/.opencode/skills/design/data/logo/colors.csv +56 -0
  40. package/.opencode/skills/design/data/logo/industries.csv +56 -0
  41. package/.opencode/skills/design/data/logo/styles.csv +56 -0
  42. package/.opencode/skills/design/references/banner-sizes-and-styles.md +118 -0
  43. package/.opencode/skills/design/references/cip-deliverable-guide.md +95 -0
  44. package/.opencode/skills/design/references/cip-design.md +121 -0
  45. package/.opencode/skills/design/references/cip-prompt-engineering.md +84 -0
  46. package/.opencode/skills/design/references/cip-style-guide.md +68 -0
  47. package/.opencode/skills/design/references/design-routing.md +207 -0
  48. package/.opencode/skills/design/references/icon-design.md +122 -0
  49. package/.opencode/skills/design/references/logo-color-psychology.md +101 -0
  50. package/.opencode/skills/design/references/logo-design.md +92 -0
  51. package/.opencode/skills/design/references/logo-prompt-engineering.md +158 -0
  52. package/.opencode/skills/design/references/logo-style-guide.md +109 -0
  53. package/.opencode/skills/design/references/slides-copywriting-formulas.md +84 -0
  54. package/.opencode/skills/design/references/slides-create.md +4 -0
  55. package/.opencode/skills/design/references/slides-html-template.md +295 -0
  56. package/.opencode/skills/design/references/slides-layout-patterns.md +137 -0
  57. package/.opencode/skills/design/references/slides-strategies.md +94 -0
  58. package/.opencode/skills/design/references/slides.md +42 -0
  59. package/.opencode/skills/design/references/social-photos-design.md +329 -0
  60. package/.opencode/skills/design/scripts/cip/core.py +215 -0
  61. package/.opencode/skills/design/scripts/cip/generate.py +484 -0
  62. package/.opencode/skills/design/scripts/cip/render-html.py +424 -0
  63. package/.opencode/skills/design/scripts/cip/search.py +127 -0
  64. package/.opencode/skills/design/scripts/icon/generate.py +487 -0
  65. package/.opencode/skills/design/scripts/logo/core.py +175 -0
  66. package/.opencode/skills/design/scripts/logo/generate.py +362 -0
  67. package/.opencode/skills/design/scripts/logo/search.py +114 -0
  68. package/.opencode/skills/design-system/SKILL.md +244 -0
  69. package/.opencode/skills/design-system/data/slide-backgrounds.csv +11 -0
  70. package/.opencode/skills/design-system/data/slide-charts.csv +26 -0
  71. package/.opencode/skills/design-system/data/slide-color-logic.csv +14 -0
  72. package/.opencode/skills/design-system/data/slide-copy.csv +26 -0
  73. package/.opencode/skills/design-system/data/slide-layout-logic.csv +16 -0
  74. package/.opencode/skills/design-system/data/slide-layouts.csv +26 -0
  75. package/.opencode/skills/design-system/data/slide-strategies.csv +16 -0
  76. package/.opencode/skills/design-system/data/slide-typography.csv +15 -0
  77. package/.opencode/skills/design-system/references/component-specs.md +236 -0
  78. package/.opencode/skills/design-system/references/component-tokens.md +214 -0
  79. package/.opencode/skills/design-system/references/primitive-tokens.md +203 -0
  80. package/.opencode/skills/design-system/references/semantic-tokens.md +215 -0
  81. package/.opencode/skills/design-system/references/states-and-variants.md +241 -0
  82. package/.opencode/skills/design-system/references/tailwind-integration.md +251 -0
  83. package/.opencode/skills/design-system/references/token-architecture.md +224 -0
  84. package/.opencode/skills/design-system/scripts/embed-tokens.cjs +99 -0
  85. package/.opencode/skills/design-system/scripts/fetch-background.py +317 -0
  86. package/.opencode/skills/design-system/scripts/generate-slide.py +770 -0
  87. package/.opencode/skills/design-system/scripts/generate-tokens.cjs +205 -0
  88. package/.opencode/skills/design-system/scripts/html-token-validator.py +327 -0
  89. package/.opencode/skills/design-system/scripts/search-slides.py +218 -0
  90. package/.opencode/skills/design-system/scripts/slide-token-validator.py +35 -0
  91. package/.opencode/skills/design-system/scripts/slide_search_core.py +453 -0
  92. package/.opencode/skills/design-system/scripts/tests/test_validate_tokens.py +48 -0
  93. package/.opencode/skills/design-system/scripts/validate-tokens.cjs +246 -0
  94. package/.opencode/skills/design-system/templates/design-tokens-starter.json +143 -0
  95. package/.opencode/skills/frontend-design/LICENSE.txt +21 -0
  96. package/.opencode/skills/frontend-design/SKILL.md +115 -0
  97. package/.opencode/skills/mafw-automation-interview/SKILL.md +43 -0
  98. package/.opencode/skills/mafw-cli-scanner/SKILL.md +37 -0
  99. package/.opencode/skills/mafw-compression-verifier/SKILL.md +52 -0
  100. package/.opencode/skills/mafw-desktop-inspector/SKILL.md +170 -0
  101. package/.opencode/skills/mafw-execute/SKILL.md +62 -0
  102. package/.opencode/skills/mafw-gateway-restart/SKILL.md +87 -0
  103. package/.opencode/skills/mafw-github-scanner/SKILL.md +38 -0
  104. package/.opencode/skills/mafw-goal/SKILL.md +76 -0
  105. package/.opencode/skills/mafw-interview/SKILL.md +68 -0
  106. package/.opencode/skills/mafw-memory-extractor/SKILL.md +65 -0
  107. package/.opencode/skills/mafw-plan/SKILL.md +58 -0
  108. package/.opencode/skills/mafw-review/SKILL.md +85 -0
  109. package/.opencode/skills/runtime-plugin-authoring/SKILL.md +791 -0
  110. package/.opencode/skills/slides/SKILL.md +40 -0
  111. package/.opencode/skills/slides/references/copywriting-formulas.md +84 -0
  112. package/.opencode/skills/slides/references/create.md +4 -0
  113. package/.opencode/skills/slides/references/html-template.md +295 -0
  114. package/.opencode/skills/slides/references/layout-patterns.md +137 -0
  115. package/.opencode/skills/slides/references/slide-strategies.md +94 -0
  116. package/.opencode/skills/ui-styling/LICENSE.txt +202 -0
  117. package/.opencode/skills/ui-styling/SKILL.md +324 -0
  118. package/.opencode/skills/ui-styling/references/canvas-design-system.md +320 -0
  119. package/.opencode/skills/ui-styling/references/shadcn-accessibility.md +471 -0
  120. package/.opencode/skills/ui-styling/references/shadcn-components.md +424 -0
  121. package/.opencode/skills/ui-styling/references/shadcn-theming.md +373 -0
  122. package/.opencode/skills/ui-styling/references/tailwind-customization.md +483 -0
  123. package/.opencode/skills/ui-styling/references/tailwind-responsive.md +382 -0
  124. package/.opencode/skills/ui-styling/references/tailwind-utilities.md +455 -0
  125. package/.opencode/skills/ui-styling/scripts/requirements.txt +17 -0
  126. package/.opencode/skills/ui-styling/scripts/shadcn_add.py +308 -0
  127. package/.opencode/skills/ui-styling/scripts/tailwind_config_gen.py +473 -0
  128. package/.opencode/skills/ui-styling/scripts/tests/coverage-ui.json +1 -0
  129. package/.opencode/skills/ui-styling/scripts/tests/requirements.txt +3 -0
  130. package/.opencode/skills/ui-styling/scripts/tests/test_shadcn_add.py +266 -0
  131. package/.opencode/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +394 -0
  132. package/.opencode/skills/ui-ux-pro-max/SKILL.md +388 -0
  133. package/.opencode/skills/ui-ux-pro-max/data/app-interface.csv +31 -0
  134. package/.opencode/skills/ui-ux-pro-max/data/charts.csv +26 -0
  135. package/.opencode/skills/ui-ux-pro-max/data/colors.csv +193 -0
  136. package/.opencode/skills/ui-ux-pro-max/data/google-fonts.csv +1924 -0
  137. package/.opencode/skills/ui-ux-pro-max/data/icons.csv +106 -0
  138. package/.opencode/skills/ui-ux-pro-max/data/landing.csv +35 -0
  139. package/.opencode/skills/ui-ux-pro-max/data/motion.csv +17 -0
  140. package/.opencode/skills/ui-ux-pro-max/data/products.csv +193 -0
  141. package/.opencode/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  142. package/.opencode/skills/ui-ux-pro-max/data/stacks/angular.csv +51 -0
  143. package/.opencode/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  144. package/.opencode/skills/ui-ux-pro-max/data/stacks/avalonia.csv +57 -0
  145. package/.opencode/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  146. package/.opencode/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  147. package/.opencode/skills/ui-ux-pro-max/data/stacks/javafx.csv +76 -0
  148. package/.opencode/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  149. package/.opencode/skills/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
  150. package/.opencode/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  151. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +71 -0
  152. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  153. package/.opencode/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  154. package/.opencode/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  155. package/.opencode/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  156. package/.opencode/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  157. package/.opencode/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  158. package/.opencode/skills/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
  159. package/.opencode/skills/ui-ux-pro-max/data/stacks/uno.csv +60 -0
  160. package/.opencode/skills/ui-ux-pro-max/data/stacks/uwp.csv +56 -0
  161. package/.opencode/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  162. package/.opencode/skills/ui-ux-pro-max/data/stacks/winui.csv +60 -0
  163. package/.opencode/skills/ui-ux-pro-max/data/stacks/wpf.csv +57 -0
  164. package/.opencode/skills/ui-ux-pro-max/data/styles.csv +85 -0
  165. package/.opencode/skills/ui-ux-pro-max/data/typography.csv +75 -0
  166. package/.opencode/skills/ui-ux-pro-max/data/ui-reasoning.csv +162 -0
  167. package/.opencode/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  168. package/.opencode/skills/ui-ux-pro-max/scripts/core.py +464 -0
  169. package/.opencode/skills/ui-ux-pro-max/scripts/design_system.py +1479 -0
  170. package/.opencode/skills/ui-ux-pro-max/scripts/search.py +162 -0
  171. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_core.py +134 -0
  172. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +159 -0
  173. package/.opencode/skills/ui-ux-pro-max/scripts/validate_data.py +114 -0
  174. package/LICENSE +21 -0
  175. package/README.md +199 -0
  176. package/bin/mafw.js +436 -0
  177. package/dist/hooks/bash-python-guide.d.ts +4 -0
  178. package/dist/hooks/bash-python-guide.d.ts.map +1 -0
  179. package/dist/hooks/bash-python-guide.js +67 -0
  180. package/dist/hooks/bash-python-guide.js.map +1 -0
  181. package/dist/hooks/handoff.d.ts +9 -0
  182. package/dist/hooks/handoff.d.ts.map +1 -0
  183. package/dist/hooks/handoff.js +8 -0
  184. package/dist/hooks/handoff.js.map +1 -0
  185. package/dist/hooks/hook-manager.d.ts +32 -0
  186. package/dist/hooks/hook-manager.d.ts.map +1 -0
  187. package/dist/hooks/hook-manager.js +91 -0
  188. package/dist/hooks/hook-manager.js.map +1 -0
  189. package/dist/hooks/llm-after.d.ts +8 -0
  190. package/dist/hooks/llm-after.d.ts.map +1 -0
  191. package/dist/hooks/llm-after.js +8 -0
  192. package/dist/hooks/llm-after.js.map +1 -0
  193. package/dist/hooks/media-ingest.d.ts +43 -0
  194. package/dist/hooks/media-ingest.d.ts.map +1 -0
  195. package/dist/hooks/media-ingest.js +269 -0
  196. package/dist/hooks/media-ingest.js.map +1 -0
  197. package/dist/hooks/memory-guide.d.ts +3 -0
  198. package/dist/hooks/memory-guide.d.ts.map +1 -0
  199. package/dist/hooks/memory-guide.js +47 -0
  200. package/dist/hooks/memory-guide.js.map +1 -0
  201. package/dist/hooks/observation-capture.d.ts +17 -0
  202. package/dist/hooks/observation-capture.d.ts.map +1 -0
  203. package/dist/hooks/observation-capture.js +20 -0
  204. package/dist/hooks/observation-capture.js.map +1 -0
  205. package/dist/hooks/session-compacting.d.ts +6 -0
  206. package/dist/hooks/session-compacting.d.ts.map +1 -0
  207. package/dist/hooks/session-compacting.js +6 -0
  208. package/dist/hooks/session-compacting.js.map +1 -0
  209. package/dist/hooks/session-ending.d.ts +20 -0
  210. package/dist/hooks/session-ending.d.ts.map +1 -0
  211. package/dist/hooks/session-ending.js +113 -0
  212. package/dist/hooks/session-ending.js.map +1 -0
  213. package/dist/hooks/session-recall.d.ts +2 -0
  214. package/dist/hooks/session-recall.d.ts.map +1 -0
  215. package/dist/hooks/session-recall.js +103 -0
  216. package/dist/hooks/session-recall.js.map +1 -0
  217. package/dist/hooks/session-start.d.ts +9 -0
  218. package/dist/hooks/session-start.d.ts.map +1 -0
  219. package/dist/hooks/session-start.js +42 -0
  220. package/dist/hooks/session-start.js.map +1 -0
  221. package/dist/hooks/tool-before.d.ts +10 -0
  222. package/dist/hooks/tool-before.d.ts.map +1 -0
  223. package/dist/hooks/tool-before.js +6 -0
  224. package/dist/hooks/tool-before.js.map +1 -0
  225. package/dist/hooks/tool-executed.d.ts +19 -0
  226. package/dist/hooks/tool-executed.d.ts.map +1 -0
  227. package/dist/hooks/tool-executed.js +12 -0
  228. package/dist/hooks/tool-executed.js.map +1 -0
  229. package/dist/hooks/user-profile.d.ts +2 -0
  230. package/dist/hooks/user-profile.d.ts.map +1 -0
  231. package/dist/hooks/user-profile.js +30 -0
  232. package/dist/hooks/user-profile.js.map +1 -0
  233. package/dist/hooks/user-prompt.d.ts +9 -0
  234. package/dist/hooks/user-prompt.d.ts.map +1 -0
  235. package/dist/hooks/user-prompt.js +8 -0
  236. package/dist/hooks/user-prompt.js.map +1 -0
  237. package/dist/hooks/voice-guide.d.ts +8 -0
  238. package/dist/hooks/voice-guide.d.ts.map +1 -0
  239. package/dist/hooks/voice-guide.js +101 -0
  240. package/dist/hooks/voice-guide.js.map +1 -0
  241. package/dist/plugin.d.ts +55 -0
  242. package/dist/plugin.d.ts.map +1 -0
  243. package/dist/plugin.js +342 -0
  244. package/dist/plugin.js.map +1 -0
  245. package/dist/tools/add-memory.d.ts +17 -0
  246. package/dist/tools/add-memory.d.ts.map +1 -0
  247. package/dist/tools/add-memory.js +60 -0
  248. package/dist/tools/add-memory.js.map +1 -0
  249. package/dist/tools/media-ask.d.ts +14 -0
  250. package/dist/tools/media-ask.d.ts.map +1 -0
  251. package/dist/tools/media-ask.js +224 -0
  252. package/dist/tools/media-ask.js.map +1 -0
  253. package/dist/tools/media-speak.d.ts +16 -0
  254. package/dist/tools/media-speak.d.ts.map +1 -0
  255. package/dist/tools/media-speak.js +78 -0
  256. package/dist/tools/media-speak.js.map +1 -0
  257. package/dist/tools/media-upload.d.ts +14 -0
  258. package/dist/tools/media-upload.d.ts.map +1 -0
  259. package/dist/tools/media-upload.js +155 -0
  260. package/dist/tools/media-upload.js.map +1 -0
  261. package/dist/tools/python-exec.d.ts +13 -0
  262. package/dist/tools/python-exec.d.ts.map +1 -0
  263. package/dist/tools/python-exec.js +99 -0
  264. package/dist/tools/python-exec.js.map +1 -0
  265. package/dist/tools/python-restart.d.ts +11 -0
  266. package/dist/tools/python-restart.d.ts.map +1 -0
  267. package/dist/tools/python-restart.js +33 -0
  268. package/dist/tools/python-restart.js.map +1 -0
  269. package/dist/utils/circuit-breaker.d.ts +23 -0
  270. package/dist/utils/circuit-breaker.d.ts.map +1 -0
  271. package/dist/utils/circuit-breaker.js +75 -0
  272. package/dist/utils/circuit-breaker.js.map +1 -0
  273. package/dist/utils/config-loader.d.ts +20 -0
  274. package/dist/utils/config-loader.d.ts.map +1 -0
  275. package/dist/utils/config-loader.js +194 -0
  276. package/dist/utils/config-loader.js.map +1 -0
  277. package/dist/utils/fallback.d.ts +7 -0
  278. package/dist/utils/fallback.d.ts.map +1 -0
  279. package/dist/utils/fallback.js +20 -0
  280. package/dist/utils/fallback.js.map +1 -0
  281. package/dist/utils/git.d.ts +24 -0
  282. package/dist/utils/git.d.ts.map +1 -0
  283. package/dist/utils/git.js +71 -0
  284. package/dist/utils/git.js.map +1 -0
  285. package/dist/utils/github.d.ts +37 -0
  286. package/dist/utils/github.d.ts.map +1 -0
  287. package/dist/utils/github.js +53 -0
  288. package/dist/utils/github.js.map +1 -0
  289. package/dist/utils/global-path.d.ts +2 -0
  290. package/dist/utils/global-path.d.ts.map +1 -0
  291. package/dist/utils/global-path.js +32 -0
  292. package/dist/utils/global-path.js.map +1 -0
  293. package/dist/utils/logger.d.ts +6 -0
  294. package/dist/utils/logger.d.ts.map +1 -0
  295. package/dist/utils/logger.js +69 -0
  296. package/dist/utils/logger.js.map +1 -0
  297. package/dist/utils/obs-capture.d.ts +7 -0
  298. package/dist/utils/obs-capture.d.ts.map +1 -0
  299. package/dist/utils/obs-capture.js +49 -0
  300. package/dist/utils/obs-capture.js.map +1 -0
  301. package/dist/utils/retry.d.ts +11 -0
  302. package/dist/utils/retry.d.ts.map +1 -0
  303. package/dist/utils/retry.js +45 -0
  304. package/dist/utils/retry.js.map +1 -0
  305. package/dist/utils/self-wiring.d.ts +13 -0
  306. package/dist/utils/self-wiring.d.ts.map +1 -0
  307. package/dist/utils/self-wiring.js +86 -0
  308. package/dist/utils/self-wiring.js.map +1 -0
  309. package/dist/utils/state.d.ts +106 -0
  310. package/dist/utils/state.d.ts.map +1 -0
  311. package/dist/utils/state.js +166 -0
  312. package/dist/utils/state.js.map +1 -0
  313. package/dist/utils/status.d.ts +47 -0
  314. package/dist/utils/status.d.ts.map +1 -0
  315. package/dist/utils/status.js +147 -0
  316. package/dist/utils/status.js.map +1 -0
  317. package/dist/utils/ttl-map.d.ts +17 -0
  318. package/dist/utils/ttl-map.d.ts.map +1 -0
  319. package/dist/utils/ttl-map.js +67 -0
  320. package/dist/utils/ttl-map.js.map +1 -0
  321. package/gateway/dist/automation-engine.js +729 -0
  322. package/gateway/dist/chat/chat-sessions.js +95 -0
  323. package/gateway/dist/chat/graph-runner.js +76 -0
  324. package/gateway/dist/chat/index.js +7 -0
  325. package/gateway/dist/chat/intent-classifier.js +23 -0
  326. package/gateway/dist/config.js +416 -0
  327. package/gateway/dist/core/auth.js +96 -0
  328. package/gateway/dist/core/budget-guard.js +52 -0
  329. package/gateway/dist/core/compression/bm25-index.js +176 -0
  330. package/gateway/dist/core/compression/compression-pipeline.js +157 -0
  331. package/gateway/dist/core/compression/compression-strategy-selector.js +32 -0
  332. package/gateway/dist/core/compression/compression-verifier.js +79 -0
  333. package/gateway/dist/core/compression/diff-compressor.js +69 -0
  334. package/gateway/dist/core/compression/hybrid-compressor.js +104 -0
  335. package/gateway/dist/core/compression/lesson-compactor.js +114 -0
  336. package/gateway/dist/core/compression/memory-index.js +190 -0
  337. package/gateway/dist/core/compression/observation-classifier.js +41 -0
  338. package/gateway/dist/core/compression/observation-deduplicator.js +43 -0
  339. package/gateway/dist/core/compression/rrf-fusion.js +63 -0
  340. package/gateway/dist/core/compression/session-pruner.js +137 -0
  341. package/gateway/dist/core/compression/token-budget-allocator.js +141 -0
  342. package/gateway/dist/core/compression/vector-index.js +117 -0
  343. package/gateway/dist/core/compression/zero-token-compressor.js +80 -0
  344. package/gateway/dist/core/cost/cognitive-router.js +41 -0
  345. package/gateway/dist/core/cost/cost-estimator.js +63 -0
  346. package/gateway/dist/core/cost/types.js +19 -0
  347. package/gateway/dist/core/engine/degradation.js +159 -0
  348. package/gateway/dist/core/engine/goal-worktree-manager.js +131 -0
  349. package/gateway/dist/core/engine/lesson-manager.js +222 -0
  350. package/gateway/dist/core/engine/optimistic-sync.js +100 -0
  351. package/gateway/dist/core/engine/phase-orchestrator.js +153 -0
  352. package/gateway/dist/core/engine/report-generator.js +131 -0
  353. package/gateway/dist/core/engine/state-lock.js +91 -0
  354. package/gateway/dist/core/engine/task-branch-manager.js +102 -0
  355. package/gateway/dist/core/engine/wave-dependency.js +67 -0
  356. package/gateway/dist/core/engine/wave-executor.js +112 -0
  357. package/gateway/dist/core/goal-budget.js +63 -0
  358. package/gateway/dist/core/graph/entity-extractor.js +53 -0
  359. package/gateway/dist/core/graph/graph-searcher.js +120 -0
  360. package/gateway/dist/core/graph/knowledge-graph-manager.js +138 -0
  361. package/gateway/dist/core/graph/relation-inferencer.js +67 -0
  362. package/gateway/dist/core/graph/types.js +2 -0
  363. package/gateway/dist/core/hooks/handoff.js +7 -0
  364. package/gateway/dist/core/hooks/hook-manager.js +91 -0
  365. package/gateway/dist/core/hooks/llm-after.js +7 -0
  366. package/gateway/dist/core/hooks/observation-capture.js +19 -0
  367. package/gateway/dist/core/hooks/session-compacting.js +5 -0
  368. package/gateway/dist/core/hooks/session-ending.js +123 -0
  369. package/gateway/dist/core/hooks/session-start.js +51 -0
  370. package/gateway/dist/core/hooks/tool-before.js +5 -0
  371. package/gateway/dist/core/hooks/tool-executed.js +11 -0
  372. package/gateway/dist/core/hooks/user-prompt.js +7 -0
  373. package/gateway/dist/core/index.js +119 -0
  374. package/gateway/dist/core/langchain/agent-adapter.js +39 -0
  375. package/gateway/dist/core/langchain/index.js +12 -0
  376. package/gateway/dist/core/langchain/memory.js +35 -0
  377. package/gateway/dist/core/langchain/node-runner.js +115 -0
  378. package/gateway/dist/core/langchain/retriever.js +28 -0
  379. package/gateway/dist/core/langgraph/checkpointer.js +200 -0
  380. package/gateway/dist/core/langgraph/graph.js +57 -0
  381. package/gateway/dist/core/langgraph/index.js +25 -0
  382. package/gateway/dist/core/langgraph/loop-state.js +71 -0
  383. package/gateway/dist/core/langgraph/nodes/archive.node.js +27 -0
  384. package/gateway/dist/core/langgraph/nodes/execute.node.js +5 -0
  385. package/gateway/dist/core/langgraph/nodes/plan.node.js +101 -0
  386. package/gateway/dist/core/langgraph/nodes/review.node.js +89 -0
  387. package/gateway/dist/core/langgraph/nodes/sync.node.js +70 -0
  388. package/gateway/dist/core/langgraph/review-parser.js +35 -0
  389. package/gateway/dist/core/langgraph/signature-detector.js +23 -0
  390. package/gateway/dist/core/manager/goal-snapshot.js +73 -0
  391. package/gateway/dist/core/manager/milestone-push.js +81 -0
  392. package/gateway/dist/core/manager/question-ledger.js +121 -0
  393. package/gateway/dist/core/manager/system-rule-templates.js +76 -0
  394. package/gateway/dist/core/mcp/tools.js +562 -0
  395. package/gateway/dist/core/mcp/validator.js +165 -0
  396. package/gateway/dist/core/mcp-server.js +86 -0
  397. package/gateway/dist/core/memory/abstraction-distiller.js +44 -0
  398. package/gateway/dist/core/memory/cognitive-graph.js +103 -0
  399. package/gateway/dist/core/memory/energy-system.js +69 -0
  400. package/gateway/dist/core/memory/extractor.js +164 -0
  401. package/gateway/dist/core/memory/harmonic-index.js +530 -0
  402. package/gateway/dist/core/memory/harmonic-types.js +6 -0
  403. package/gateway/dist/core/memory/injector.js +111 -0
  404. package/gateway/dist/core/memory/l5-store.js +141 -0
  405. package/gateway/dist/core/memory/matcher.js +66 -0
  406. package/gateway/dist/core/memory/merger.js +95 -0
  407. package/gateway/dist/core/memory/migrate-v6.1.js +133 -0
  408. package/gateway/dist/core/memory/minhash-merger.js +195 -0
  409. package/gateway/dist/core/memory/observation-service.js +87 -0
  410. package/gateway/dist/core/memory/privacy-filter.js +163 -0
  411. package/gateway/dist/core/memory/reranker.js +208 -0
  412. package/gateway/dist/core/memory/review-scheduler.js +100 -0
  413. package/gateway/dist/core/memory/salience-perceptor.js +24 -0
  414. package/gateway/dist/core/memory/store.js +284 -0
  415. package/gateway/dist/core/memory/t1-store.js +183 -0
  416. package/gateway/dist/core/memory/t1-to-t2-compressor.js +119 -0
  417. package/gateway/dist/core/memory/validator.js +66 -0
  418. package/gateway/dist/core/plugin.js +902 -0
  419. package/gateway/dist/core/skills/mafw-execute/entry.js +226 -0
  420. package/gateway/dist/core/skills/mafw-goal/entry.js +203 -0
  421. package/gateway/dist/core/skills/mafw-plan/entry.js +184 -0
  422. package/gateway/dist/core/skills/mafw-review/entry.js +200 -0
  423. package/gateway/dist/core/storage/file-storage.js +120 -0
  424. package/gateway/dist/core/storage/sqlite-storage.js +205 -0
  425. package/gateway/dist/core/storage/types.js +2 -0
  426. package/gateway/dist/core/tools/archive-worktree.js +208 -0
  427. package/gateway/dist/core/tools/remote-cli.js +106 -0
  428. package/gateway/dist/core/tools/run-ask-user.js +79 -0
  429. package/gateway/dist/core/tools/run-execute-wave.js +32 -0
  430. package/gateway/dist/core/tools/run-plan.js +42 -0
  431. package/gateway/dist/core/tools/run-record-feedback.js +55 -0
  432. package/gateway/dist/core/tools/run-review.js +46 -0
  433. package/gateway/dist/core/tools/write-lesson.js +97 -0
  434. package/gateway/dist/core/utils/circuit-breaker.js +74 -0
  435. package/gateway/dist/core/utils/config-loader.js +203 -0
  436. package/gateway/dist/core/utils/fallback.js +19 -0
  437. package/gateway/dist/core/utils/git.js +70 -0
  438. package/gateway/dist/core/utils/github.js +52 -0
  439. package/gateway/dist/core/utils/global-path.js +41 -0
  440. package/gateway/dist/core/utils/logger.js +71 -0
  441. package/gateway/dist/core/utils/retry.js +44 -0
  442. package/gateway/dist/core/utils/state.js +175 -0
  443. package/gateway/dist/core/utils/status.js +156 -0
  444. package/gateway/dist/cost/service.js +17 -0
  445. package/gateway/dist/dashboard/api.js +1156 -0
  446. package/gateway/dist/dashboard/public/assets/index-DOXm39dm.css +1 -0
  447. package/gateway/dist/dashboard/public/index.html +13 -0
  448. package/gateway/dist/dashboard/server.js +140 -0
  449. package/gateway/dist/dashboard/types.js +2 -0
  450. package/gateway/dist/desktop-client.js +124 -0
  451. package/gateway/dist/eval-endpoint.js +219 -0
  452. package/gateway/dist/event-bus.js +7 -0
  453. package/gateway/dist/feedback/anchor-promotion.js +99 -0
  454. package/gateway/dist/feedback/deferred-memory.js +13 -0
  455. package/gateway/dist/feedback/miss-attribution.js +20 -0
  456. package/gateway/dist/graph/anchor-graph-store.js +113 -0
  457. package/gateway/dist/graph/anchor-graph.js +48 -0
  458. package/gateway/dist/health.js +53 -0
  459. package/gateway/dist/heartbeat.js +109 -0
  460. package/gateway/dist/index.js +6377 -0
  461. package/gateway/dist/interceptors/memory-injector.js +79 -0
  462. package/gateway/dist/ledger.js +121 -0
  463. package/gateway/dist/loop-monitor.js +74 -0
  464. package/gateway/dist/mcp/handlers/add-memory.js +113 -0
  465. package/gateway/dist/mcp/handlers/ask-user.js +63 -0
  466. package/gateway/dist/mcp/handlers/commit-heuristic.js +19 -0
  467. package/gateway/dist/mcp/handlers/create-goal.js +83 -0
  468. package/gateway/dist/mcp/handlers/desktop-click.js +18 -0
  469. package/gateway/dist/mcp/handlers/desktop-get-state.js +15 -0
  470. package/gateway/dist/mcp/handlers/desktop-navigate.js +18 -0
  471. package/gateway/dist/mcp/handlers/desktop-screenshot.js +16 -0
  472. package/gateway/dist/mcp/handlers/desktop-scroll.js +19 -0
  473. package/gateway/dist/mcp/handlers/desktop-type.js +19 -0
  474. package/gateway/dist/mcp/handlers/draft-automation-rule.js +45 -0
  475. package/gateway/dist/mcp/handlers/get-automation-history.js +15 -0
  476. package/gateway/dist/mcp/handlers/get-automation-rule.js +26 -0
  477. package/gateway/dist/mcp/handlers/get-axioms.js +14 -0
  478. package/gateway/dist/mcp/handlers/get-deltas.js +77 -0
  479. package/gateway/dist/mcp/handlers/get-memory.js +67 -0
  480. package/gateway/dist/mcp/handlers/get-model-route.js +16 -0
  481. package/gateway/dist/mcp/handlers/get-triage-item.js +17 -0
  482. package/gateway/dist/mcp/handlers/list-automation-rules.js +24 -0
  483. package/gateway/dist/mcp/handlers/list-triage-items.js +14 -0
  484. package/gateway/dist/mcp/handlers/load-state.js +54 -0
  485. package/gateway/dist/mcp/handlers/manager-answer-question.js +67 -0
  486. package/gateway/dist/mcp/handlers/manager-cancel-goal.js +58 -0
  487. package/gateway/dist/mcp/handlers/manager-get-evidence.js +57 -0
  488. package/gateway/dist/mcp/handlers/manager-get-goal-status.js +58 -0
  489. package/gateway/dist/mcp/handlers/manager-list-goals.js +60 -0
  490. package/gateway/dist/mcp/handlers/manager-list-pending-questions.js +67 -0
  491. package/gateway/dist/mcp/handlers/manager-set-goal.js +78 -0
  492. package/gateway/dist/mcp/handlers/merge-memory.js +184 -0
  493. package/gateway/dist/mcp/handlers/pin-memory.js +53 -0
  494. package/gateway/dist/mcp/handlers/propose-triage-decision.js +38 -0
  495. package/gateway/dist/mcp/handlers/record-feedback.js +59 -0
  496. package/gateway/dist/mcp/handlers/resolve-merge.js +92 -0
  497. package/gateway/dist/mcp/handlers/restart-agent.js +22 -0
  498. package/gateway/dist/mcp/handlers/run-automation.js +28 -0
  499. package/gateway/dist/mcp/handlers/search-hybrid.js +181 -0
  500. package/gateway/dist/mcp/handlers/supersede-memory.js +47 -0
  501. package/gateway/dist/mcp/handlers/update-state.js +59 -0
  502. package/gateway/dist/mcp/handlers/validate-rule.js +39 -0
  503. package/gateway/dist/mcp/sse-transport.js +97 -0
  504. package/gateway/dist/mcp/streamable-endpoint.js +98 -0
  505. package/gateway/dist/mcp/tool-registry.js +630 -0
  506. package/gateway/dist/media/media-agent.js +755 -0
  507. package/gateway/dist/media/media-plugin-loader.js +254 -0
  508. package/gateway/dist/media/media-runtime-executor.js +227 -0
  509. package/gateway/dist/media/media-service.js +199 -0
  510. package/gateway/dist/media/pi-adapter.js +159 -0
  511. package/gateway/dist/media/plugin-context.js +14 -0
  512. package/gateway/dist/media/tts-service.js +173 -0
  513. package/gateway/dist/memory/abstraction-matcher.js +28 -0
  514. package/gateway/dist/memory/consolidation-service.js +209 -0
  515. package/gateway/dist/memory/derived-terms.js +50 -0
  516. package/gateway/dist/memory/embedding-provider.js +330 -0
  517. package/gateway/dist/memory/embedding-runtime.js +174 -0
  518. package/gateway/dist/memory/event-log.js +84 -0
  519. package/gateway/dist/memory/gateway-db.js +465 -0
  520. package/gateway/dist/memory/harmonic-file-store.js +338 -0
  521. package/gateway/dist/memory/llamacpp-provider.js +370 -0
  522. package/gateway/dist/memory/okf-index-renderer.js +61 -0
  523. package/gateway/dist/memory/okf-migrator.js +71 -0
  524. package/gateway/dist/memory/okf-parser.js +57 -0
  525. package/gateway/dist/memory/okf-writer.js +107 -0
  526. package/gateway/dist/memory/service.js +81 -0
  527. package/gateway/dist/memory/t1-db.js +139 -0
  528. package/gateway/dist/memory/unmerge-cleanup.js +82 -0
  529. package/gateway/dist/memory/vector-store.js +246 -0
  530. package/gateway/dist/memory/write-queue.js +31 -0
  531. package/gateway/dist/metrics/collector.js +28 -0
  532. package/gateway/dist/mobile/device-store.js +123 -0
  533. package/gateway/dist/mobile/mdns-advertiser.js +55 -0
  534. package/gateway/dist/mobile/media-proxy.js +292 -0
  535. package/gateway/dist/mobile/pairing.js +139 -0
  536. package/gateway/dist/mobile/push-gateway.js +126 -0
  537. package/gateway/dist/mobile/tts-artifact.js +43 -0
  538. package/gateway/dist/opencode-adapter.js +241 -0
  539. package/gateway/dist/orchestration/outcome-recorder.js +218 -0
  540. package/gateway/dist/orchestration/policy.js +57 -0
  541. package/gateway/dist/orchestration/registry.js +47 -0
  542. package/gateway/dist/plugins/hub.js +188 -0
  543. package/gateway/dist/poll.js +110 -0
  544. package/gateway/dist/python/kernel-service.js +762 -0
  545. package/gateway/dist/recall/constraints-migrate.js +108 -0
  546. package/gateway/dist/recall/data-dir-migrate.js +120 -0
  547. package/gateway/dist/recall/gateway-db-migrate.js +134 -0
  548. package/gateway/dist/recall/index-scan.js +437 -0
  549. package/gateway/dist/recall/inject-format.js +222 -0
  550. package/gateway/dist/recall/legacy-t1-archive.js +86 -0
  551. package/gateway/dist/recall/memory-worker.js +128 -0
  552. package/gateway/dist/recall/pipeline-rules.js +76 -0
  553. package/gateway/dist/recall/recall-context.js +297 -0
  554. package/gateway/dist/recall/reflect-cursor.js +50 -0
  555. package/gateway/dist/recall/reflection.js +318 -0
  556. package/gateway/dist/recall/session-worker-pool.js +100 -0
  557. package/gateway/dist/recall/stale-verify.js +72 -0
  558. package/gateway/dist/recall/step-inject.js +173 -0
  559. package/gateway/dist/recall/time-anchor.js +143 -0
  560. package/gateway/dist/recall/ttl-map.js +72 -0
  561. package/gateway/dist/recall/turn-completion.js +44 -0
  562. package/gateway/dist/recall/turn-pipeline.js +150 -0
  563. package/gateway/dist/recovery.js +158 -0
  564. package/gateway/dist/resources/sdk-session.js +202 -0
  565. package/gateway/dist/retrieval/guided-retriever.js +131 -0
  566. package/gateway/dist/retrieval/idf-stats.js +31 -0
  567. package/gateway/dist/routes/embedding-config.js +100 -0
  568. package/gateway/dist/routes/goal-sessions.js +45 -0
  569. package/gateway/dist/routes/manager-rotate.js +55 -0
  570. package/gateway/dist/routes/media-switch.js +78 -0
  571. package/gateway/dist/routes/model-config.js +132 -0
  572. package/gateway/dist/routes/note-board.js +51 -0
  573. package/gateway/dist/routes/permission.js +43 -0
  574. package/gateway/dist/routes/pinned-recall.js +36 -0
  575. package/gateway/dist/routes/plugins.js +77 -0
  576. package/gateway/dist/routes/restart-agent.js +28 -0
  577. package/gateway/dist/routes/runtime-switch.js +80 -0
  578. package/gateway/dist/routes/session-branch.js +63 -0
  579. package/gateway/dist/routes/session-mutations.js +58 -0
  580. package/gateway/dist/routes/session-summarize.js +49 -0
  581. package/gateway/dist/routes/usage-plugins.js +251 -0
  582. package/gateway/dist/runtime/agent-definition.js +2 -0
  583. package/gateway/dist/runtime/auth.js +80 -0
  584. package/gateway/dist/runtime/completion-http.js +97 -0
  585. package/gateway/dist/runtime/contract.js +37 -0
  586. package/gateway/dist/runtime/loader.js +240 -0
  587. package/gateway/dist/runtime/normalize.js +59 -0
  588. package/gateway/dist/runtime/opencode-runtime.js +336 -0
  589. package/gateway/dist/runtime/pi/pi-agent-config.js +294 -0
  590. package/gateway/dist/runtime/pi/pi-approval-bridge.js +44 -0
  591. package/gateway/dist/runtime/pi/pi-approval-extension.js +62 -0
  592. package/gateway/dist/runtime/pi/pi-events.js +122 -0
  593. package/gateway/dist/runtime/pi/pi-messages.js +20 -0
  594. package/gateway/dist/runtime/pi/pi-provider.js +19 -0
  595. package/gateway/dist/runtime/pi/pi-session-storage.js +44 -0
  596. package/gateway/dist/runtime/pi/pi-session.js +328 -0
  597. package/gateway/dist/runtime/plugins/pi-runtime.js +285 -0
  598. package/gateway/dist/runtime/serve-for-runtime.js +22 -0
  599. package/gateway/dist/runtime/serve-sidecar.js +164 -0
  600. package/gateway/dist/runtime/serve-supervisor.js +80 -0
  601. package/gateway/dist/self-update.js +309 -0
  602. package/gateway/dist/skills/manager-agent-config.js +37 -0
  603. package/gateway/dist/skills/manager-identity.js +11 -0
  604. package/gateway/dist/skills/memory-curator-agent.js +55 -0
  605. package/gateway/dist/trajectory/api.js +154 -0
  606. package/gateway/dist/trajectory/backfill-provider.js +41 -0
  607. package/gateway/dist/trajectory/collector.js +337 -0
  608. package/gateway/dist/trajectory/trajectory-store.js +336 -0
  609. package/gateway/dist/trajectory/types.js +17 -0
  610. package/gateway/dist/tray/index.js +131 -0
  611. package/gateway/dist/tray/tray.ps1 +96 -0
  612. package/gateway/dist/types.js +2 -0
  613. package/gateway/dist/usage/builtin-plugins/commandcode.js +71 -0
  614. package/gateway/dist/usage/builtin-plugins/deepseek.js +56 -0
  615. package/gateway/dist/usage/builtin-plugins/gateway.js +127 -0
  616. package/gateway/dist/usage/builtin-plugins/kimi-for-coding.js +93 -0
  617. package/gateway/dist/usage/builtin-plugins/kimi.js +48 -0
  618. package/gateway/dist/usage/builtin-plugins/opencode-go.js +54 -0
  619. package/gateway/dist/usage/builtin-plugins/openrouter.js +48 -0
  620. package/gateway/dist/usage/builtin-plugins/siliconflow-cn.js +49 -0
  621. package/gateway/dist/usage/builtin-plugins/zhipuai-coding-plan.js +44 -0
  622. package/gateway/dist/usage/model-prices.js +39 -0
  623. package/gateway/dist/usage/model-stats.js +21 -0
  624. package/gateway/dist/usage/plugin-context.js +79 -0
  625. package/gateway/dist/usage/plugin-loader.js +280 -0
  626. package/gateway/dist/usage/templates.js +227 -0
  627. package/gateway/dist/usage/types.js +2 -0
  628. package/gateway/dist/usage/usage-poller.js +216 -0
  629. package/gateway/package.json +37 -0
  630. package/package.json +78 -0
  631. package/packages/tui/dist/cli.js +4664 -0
@@ -0,0 +1,437 @@
1
+ "use strict";
2
+ // Index scan: a cheap worker-model call reads the full harmonic index
3
+ // (formatted as compact text) and picks relevant memory IDs for a given query.
4
+ // This catches the semantic gap that BM25 misses — preference queries,
5
+ // paraphrase, cross-session aggregation.
6
+ //
7
+ // Transport: direct OpenAI-compatible HTTP chat completion (NOT an opencode
8
+ // session). The scan is a stateless classification call — a session adds
9
+ // create/prompt/delete churn and a per-session prompt-cache key for zero
10
+ // benefit, and one-shot sessions flooded the session registry.
11
+ //
12
+ // Cache-first design (the dominant cost lever is provider prompt-cache hits):
13
+ // - The index text is a cacheable prefix: static header (no entry count),
14
+ // entries sorted oldest-first (append-only), no volatile fields (energy
15
+ // omitted), and an explicit `cache_control: ephemeral` marker on the
16
+ // index block → deterministic DashScope explicit-cache hits (10% input
17
+ // billing, 5-min TTL reset on hit) instead of best-effort implicit cache.
18
+ // - The query is always the final suffix — dynamic content last.
19
+ //
20
+ // The index text is ~25-30k tokens for 690 entries — well within the model's
21
+ // context window. Scan latency with a warm cache is a few seconds; results
22
+ // are consumed via async prefetch snapshots, never on the sync recall path
23
+ // (the plugin client aborts recall requests after 100ms).
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.IndexScanService = void 0;
26
+ exports.resolveScanBaseUrl = resolveScanBaseUrl;
27
+ exports.hashText = hashText;
28
+ exports.formatEntryForIndex = formatEntryForIndex;
29
+ exports.formatIndexForScan = formatIndexForScan;
30
+ exports.formatIndexForScanSlim = formatIndexForScanSlim;
31
+ exports.parseScanResponse = parseScanResponse;
32
+ exports.resolveShortIds = resolveShortIds;
33
+ const logger_1 = require("../core/utils/logger");
34
+ const memory_curator_agent_1 = require("../skills/memory-curator-agent");
35
+ const completion_http_1 = require("../runtime/completion-http");
36
+ /**
37
+ * Map a provider id to its OpenAI-compatible chat-completions endpoint.
38
+ * `endpoints` (config `recall.scanEndpoints`) wins over the hardcoded table;
39
+ * unknown providers resolve to undefined — the caller may still inject an
40
+ * endpoint via ScanHttpDeps.resolveEndpoint (e.g. derived from the opencode
41
+ * provider config) or fall back to BM25-only recall.
42
+ */
43
+ function resolveScanBaseUrl(providerID, endpoints) {
44
+ if (!providerID)
45
+ return undefined;
46
+ const mapped = endpoints?.[providerID];
47
+ if (mapped)
48
+ return mapped;
49
+ const p = providerID.toLowerCase();
50
+ if (p.includes('alibaba') || p.includes('dashscope') || p.includes('qwen')) {
51
+ return 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions';
52
+ }
53
+ return undefined;
54
+ }
55
+ /** FNV-1a (32-bit) — cheap byte-stability fingerprint for the index text. */
56
+ function hashText(s) {
57
+ let h = 0x811c9dc5;
58
+ for (let i = 0; i < s.length; i++) {
59
+ h ^= s.charCodeAt(i);
60
+ h = Math.imul(h, 0x01000193);
61
+ }
62
+ return (h >>> 0).toString(16).padStart(8, '0');
63
+ }
64
+ const SCAN_SYSTEM = `You are a memory retrieval system. Given a memory index and a user query, identify the most relevant memory entries.
65
+
66
+ The index lists memories in this format:
67
+ - [id:<short_id>] (<date>) <type> | <summary> | anchors: <keywords>
68
+
69
+ Return ONLY valid JSON (no markdown):
70
+ {"relevant_ids": ["<short_id>", ...], "reasoning": "<one sentence>", "confidence": <0.0-1.0>}
71
+
72
+ Rules:
73
+ - You MUST return 5-8 entry IDs. If the index has entries at all, return at least 5.
74
+ - Return MORE entries rather than fewer — false negatives are worse than false positives
75
+ - Rank them by relevance (most relevant first)
76
+ - Consider semantic relevance, not just keyword matching — look for topic overlap, related concepts, paraphrases
77
+ - For preference queries (what does the user like/dislike), prioritize entries with "preference" type or "pref:" anchors
78
+ - For temporal queries (when/what happened), prioritize entries with matching dates
79
+ - For multi-session queries (what did we discuss about X), look for entries sharing topic anchors
80
+ - If answering the question requires combining information from multiple memories, include ALL relevant entries
81
+ - confidence = how sure you are that the selected entries answer the query (0.0 = guess, 1.0 = certain)
82
+ - If nothing is relevant, return {"relevant_ids": [], "reasoning": "no relevant memories", "confidence": 0.0}
83
+ ${memory_curator_agent_1.HARD_BOUNDARIES}`;
84
+ /**
85
+ * Format a single harmonic index entry into a compact index line.
86
+ * Only immutable fields are included (date, type, summary, anchors) —
87
+ * volatile fields like energy would invalidate the provider prompt cache
88
+ * on every decay pass.
89
+ *
90
+ * Truncation opts (Step 4 slimming): absCap/anchorCap shorten the line for
91
+ * scan-prompt size; caps are caller-fixed constants so the rendered text
92
+ * stays byte-stable across refreshes (cache-safe). Truncation slices at
93
+ * UTF-16 code-unit boundaries — fine for CJK BMP text, only cosmetic for
94
+ * the rare astral char.
95
+ */
96
+ function formatEntryForIndex(entry, opts) {
97
+ // Tail-10 short id: harmonic ids are mem_<millis>_<rand>, so a head prefix
98
+ // collides for same-millisecond writes (batch ingests share one prefix —
99
+ // "unique 12-char prefixes: 1/53" in the LongMemEval harness). The tail
100
+ // (rand + ts suffix) is unique per entry; resolveShortIds matches endsWith.
101
+ const id = (entry.id || '?').slice(-10);
102
+ const date = formatDateOnly(entry.created_at);
103
+ const type = entry.type || 'unknown';
104
+ // Don't truncate abstraction — let the model see full content for better relevance judgment
105
+ let summary = (entry.primary_abstraction || '').replace(/\n/g, ' ');
106
+ if (opts?.absCap && summary.length > opts.absCap)
107
+ summary = summary.slice(0, opts.absCap) + '…';
108
+ const anchors = (entry.cue_anchors || []).slice(0, opts?.anchorCap ?? 5).join(', ');
109
+ return `- [id:${id}] (${date}) ${type} | ${summary} | anchors: ${anchors}`;
110
+ }
111
+ function formatDateOnly(iso) {
112
+ if (!iso)
113
+ return 'unknown';
114
+ try {
115
+ const d = new Date(iso);
116
+ if (Number.isNaN(d.getTime()))
117
+ return 'unknown';
118
+ const yyyy = d.getFullYear();
119
+ const mm = String(d.getMonth() + 1).padStart(2, '0');
120
+ const dd = String(d.getDate()).padStart(2, '0');
121
+ return `${yyyy}-${mm}-${dd}`;
122
+ }
123
+ catch {
124
+ return 'unknown';
125
+ }
126
+ }
127
+ /**
128
+ * Format the full harmonic index into a compact text block for scanning.
129
+ * Excludes superseded entries. Sorted by created_at ASCENDING (oldest first)
130
+ * so the text is append-only: after new writes the refreshed text keeps the
131
+ * entire previous prefix byte-identical, preserving provider prompt-cache
132
+ * hits across hourly refreshes. Header is static (no entry count) for the
133
+ * same reason.
134
+ */
135
+ function formatIndexForScan(index) {
136
+ const entries = index.getIndex().entries
137
+ .filter((e) => !e.superseded_by)
138
+ .sort((a, b) => {
139
+ const ta = a.created_at ? new Date(a.created_at).getTime() : 0;
140
+ const tb = b.created_at ? new Date(b.created_at).getTime() : 0;
141
+ return ta - tb; // oldest first — append-only order
142
+ });
143
+ const lines = entries.map((e) => formatEntryForIndex(e));
144
+ return `# Memory Index\n\n${lines.join('\n')}`;
145
+ }
146
+ /** Slimming variant — same ordering/filtering, truncated lines (Step 4 A/B). */
147
+ function formatIndexForScanSlim(index, opts) {
148
+ const entries = index.getIndex().entries
149
+ .filter((e) => !e.superseded_by)
150
+ .sort((a, b) => {
151
+ const ta = a.created_at ? new Date(a.created_at).getTime() : 0;
152
+ const tb = b.created_at ? new Date(b.created_at).getTime() : 0;
153
+ return ta - tb;
154
+ });
155
+ const lines = entries.map((e) => formatEntryForIndex(e, opts));
156
+ return `# Memory Index\n\n${lines.join('\n')}`;
157
+ }
158
+ /**
159
+ * Parse the scan response into structured result.
160
+ * Tolerant of markdown fences and extra text around JSON.
161
+ */
162
+ function parseScanResponse(text) {
163
+ if (!text?.trim())
164
+ return null;
165
+ try {
166
+ // Strip markdown fences if present
167
+ const cleaned = text.replace(/^```(?:json)?\s*/i, '').replace(/```\s*$/, '').trim();
168
+ const parsed = JSON.parse(cleaned);
169
+ if (!Array.isArray(parsed?.relevant_ids))
170
+ return null;
171
+ return {
172
+ relevantIds: parsed.relevant_ids.filter((id) => typeof id === 'string').slice(0, 10),
173
+ confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
174
+ reasoning: typeof parsed.reasoning === 'string' ? parsed.reasoning : undefined,
175
+ };
176
+ }
177
+ catch {
178
+ return null;
179
+ }
180
+ }
181
+ /**
182
+ * Resolve short IDs from scan response to full harmonic index entry IDs.
183
+ * The index shows tail-10 short ids (see formatEntryForIndex); this maps
184
+ * them back with endsWith. Ambiguous tails resolve to the first match —
185
+ * tail collisions are ~0 for 10 chars over ~1k entries.
186
+ */
187
+ function resolveShortIds(shortIds, index) {
188
+ const entries = index.getIndex().entries;
189
+ const resolved = [];
190
+ for (const shortId of shortIds) {
191
+ const match = entries.find((e) => e.id.endsWith(shortId));
192
+ if (match)
193
+ resolved.push(match.id);
194
+ }
195
+ return resolved;
196
+ }
197
+ /**
198
+ * IndexScanService runs scan queries over direct HTTP and caches the index
199
+ * text. Created once by the gateway, reused across all scan calls.
200
+ */
201
+ class IndexScanService {
202
+ index;
203
+ workerModel;
204
+ deps;
205
+ cachedIndexText = null;
206
+ cachedAt = 0;
207
+ lastIndexHash = null;
208
+ inFlight = null;
209
+ // Failure cooldown state: a slow or broken worker model must not burn a
210
+ // request on every recall call. Exponential backoff (1/2/4 min, capped);
211
+ // a healthy scan resets the streak. Low-confidence results are healthy
212
+ // "no match" answers and do NOT arm the cooldown.
213
+ lastFailAt = 0;
214
+ consecutiveFails = 0;
215
+ lastAttemptFailed = false;
216
+ // Warn-once flag for unresolvable endpoint/key so a misconfiguration
217
+ // doesn't spam the log on every scan attempt.
218
+ warnedUnresolvable = false;
219
+ // Async prefetch snapshots: scan results precomputed when a user message
220
+ // arrives, consumed later by the sync recall path (never awaited there).
221
+ snapshots = new Map();
222
+ lastPrefetchAt = new Map();
223
+ static SNAPSHOT_TTL_MS = 10 * 60_000;
224
+ static SNAPSHOT_CAP = 64;
225
+ static PREFETCH_THROTTLE_MS = 60_000;
226
+ constructor(index, workerModel, deps = {}) {
227
+ this.index = index;
228
+ this.workerModel = workerModel;
229
+ this.deps = deps;
230
+ }
231
+ /** Refresh the cached index text. Called hourly by turn-compress cron. */
232
+ refreshCache() {
233
+ const text = formatIndexForScan(this.index);
234
+ const hash = hashText(text);
235
+ const changed = this.lastIndexHash !== null && this.lastIndexHash !== hash;
236
+ logger_1.log.info(`[IndexScan] cache refreshed: ${text.split('\n').length - 2} entries, ${text.length} chars, hash=${hash}${this.lastIndexHash !== null ? `, changed=${changed}` : ''}`);
237
+ this.cachedIndexText = text;
238
+ this.lastIndexHash = hash;
239
+ this.cachedAt = Date.now();
240
+ }
241
+ /** Get the cached index text, refreshing if needed. */
242
+ getIndexText() {
243
+ if (!this.cachedIndexText) {
244
+ this.refreshCache();
245
+ }
246
+ return this.cachedIndexText;
247
+ }
248
+ /**
249
+ * Run a scan: send cached index + query to the worker model over direct
250
+ * HTTP, parse the response. Returns null on failure (fail-open: callers
251
+ * fall back to BM25 only). Deduplicates concurrent calls — if a scan is
252
+ * already in-flight, returns the same promise.
253
+ */
254
+ async scan(query, options = {}) {
255
+ const timeoutMs = options.timeoutMs ?? this.deps.timeoutMs ?? 30_000;
256
+ const minConfidence = options.minConfidence ?? 0.3;
257
+ // Failure cooldown (exponential backoff: 1/2/4 min, capped)
258
+ if (this.consecutiveFails > 0) {
259
+ const cooldown = Math.min(60_000 * 2 ** (this.consecutiveFails - 1), 240_000);
260
+ if (Date.now() - this.lastFailAt < cooldown)
261
+ return null;
262
+ }
263
+ // Deduplicate concurrent scans
264
+ if (this.inFlight)
265
+ return this.inFlight;
266
+ this.inFlight = this._doScan(query, timeoutMs, minConfidence);
267
+ try {
268
+ const result = await this.inFlight;
269
+ if (this.lastAttemptFailed) {
270
+ this.consecutiveFails++;
271
+ this.lastFailAt = Date.now();
272
+ }
273
+ else {
274
+ this.consecutiveFails = 0;
275
+ }
276
+ return result;
277
+ }
278
+ finally {
279
+ this.inFlight = null;
280
+ }
281
+ }
282
+ async _doScan(query, timeoutMs, minConfidence) {
283
+ const providerID = this.workerModel?.providerID;
284
+ const modelID = this.workerModel?.modelID;
285
+ const startedAt = Date.now();
286
+ try {
287
+ const indexText = this.getIndexText();
288
+ const req = {
289
+ model: { providerID: providerID ?? '', modelID: modelID ?? '' },
290
+ system: [
291
+ { text: SCAN_SYSTEM },
292
+ // Cacheable-prefix hint: DashScope caches the block from the start
293
+ // of the messages up to here (SCAN_SYSTEM + index) for 5 minutes,
294
+ // reset on every hit → deterministic ~10% input cost. Mapped to
295
+ // `cache_control: ephemeral` by the direct-HTTP transport; other
296
+ // runtimes may ignore it (fail-open).
297
+ { text: indexText, cacheable: true },
298
+ ],
299
+ user: [{ type: 'text', text: `User query: ${query}\n\nSelect the most relevant memory entries from the index above.` }],
300
+ temperature: 0,
301
+ // Reasoning models (glm-5.3-flash always thinks, ~300-1500 reasoning
302
+ // tokens) need headroom or the JSON gets truncated mid-stream
303
+ // (finish_reason=length → unparsable → cooldown). The JSON itself is
304
+ // ~100 tokens; 4096 covers the reasoning budget with margin.
305
+ maxTokens: 4096,
306
+ timeoutMs,
307
+ };
308
+ let content;
309
+ let usage;
310
+ const channel = this.deps.completion?.();
311
+ try {
312
+ if (!channel)
313
+ throw new Error('no completion channel');
314
+ const res = await channel.complete(req);
315
+ content = res.text;
316
+ usage = res.usage;
317
+ }
318
+ catch (contractErr) {
319
+ if (channel) {
320
+ logger_1.log.warn(`[IndexScan] completion channel failed (${contractErr.message}), falling back to direct HTTP`);
321
+ }
322
+ let res;
323
+ try {
324
+ res = await (0, completion_http_1.httpComplete)(req, this.deps);
325
+ }
326
+ catch (httpErr) {
327
+ if (/no endpoint or API key/.test(httpErr.message)) {
328
+ if (!this.warnedUnresolvable) {
329
+ logger_1.log.warn(`[IndexScan] no endpoint or API key for provider "${providerID ?? '?'}" — scan disabled`);
330
+ this.warnedUnresolvable = true;
331
+ }
332
+ this.lastAttemptFailed = true;
333
+ return null;
334
+ }
335
+ logger_1.log.warn(`[IndexScan] scan failed: ${httpErr.message}`);
336
+ this.lastAttemptFailed = true;
337
+ return null;
338
+ }
339
+ content = res.text;
340
+ usage = res.usage;
341
+ }
342
+ const result = parseScanResponse(content);
343
+ // Self-record usage: direct-HTTP scans bypass opencode, so without this
344
+ // sink the memory-system token stats silently lose the scan's share.
345
+ if (this.deps.recordUsage && usage) {
346
+ try {
347
+ this.deps.recordUsage({
348
+ providerID,
349
+ modelID,
350
+ input: usage.input,
351
+ cached: usage.cached,
352
+ output: usage.output,
353
+ latencyMs: Date.now() - startedAt,
354
+ ok: result != null,
355
+ indexHash: hashText(this.getIndexText()),
356
+ });
357
+ }
358
+ catch { /* never block the scan path on recorder errors */ }
359
+ }
360
+ if (!result) {
361
+ this.lastAttemptFailed = true; // unparsable output — model likely broken
362
+ logger_1.log.warn(`[IndexScan] scan failed: unparsable response (${Date.now() - startedAt}ms)`);
363
+ return null;
364
+ }
365
+ // Resolve short IDs to full IDs
366
+ result.relevantIds = resolveShortIds(result.relevantIds, this.index);
367
+ const elapsed = Date.now() - startedAt;
368
+ if (result.confidence < minConfidence) {
369
+ logger_1.log.info(`[IndexScan] ok: low confidence ${result.confidence} < ${minConfidence}, discarding (${elapsed}ms${usage?.cached != null ? `, cachedTokens=${usage.cached}` : ''})`);
370
+ this.lastAttemptFailed = false; // healthy "no match"
371
+ return null;
372
+ }
373
+ this.lastAttemptFailed = false;
374
+ logger_1.log.info(`[IndexScan] ok: ids=${result.relevantIds.length} confidence=${result.confidence} (${elapsed}ms${usage?.cached != null ? `, cachedTokens=${usage.cached}` : ''})`);
375
+ return result;
376
+ }
377
+ catch (err) {
378
+ logger_1.log.warn(`[IndexScan] scan failed: ${err.message}`);
379
+ this.lastAttemptFailed = true;
380
+ return null;
381
+ }
382
+ }
383
+ /**
384
+ * Fire-and-forget prefetch: scan the query now and store the result as a
385
+ * per-session snapshot for later sync-path consumption. Throttled per
386
+ * session (60s); scan-internal dedup/cooldown still apply.
387
+ */
388
+ prefetch(sessionID, query) {
389
+ if (!sessionID || !query.trim())
390
+ return;
391
+ const now = Date.now();
392
+ const last = this.lastPrefetchAt.get(sessionID) ?? 0;
393
+ if (now - last < IndexScanService.PREFETCH_THROTTLE_MS)
394
+ return;
395
+ this.lastPrefetchAt.set(sessionID, now);
396
+ void this.scan(query)
397
+ .then((result) => {
398
+ if (result)
399
+ this.setSnapshot(sessionID, result);
400
+ })
401
+ .catch(() => { });
402
+ }
403
+ setSnapshot(sessionID, result) {
404
+ if (this.snapshots.size >= IndexScanService.SNAPSHOT_CAP && !this.snapshots.has(sessionID)) {
405
+ // Evict the oldest snapshot when at capacity.
406
+ let oldestKey = '';
407
+ let oldestAt = Infinity;
408
+ for (const [k, v] of this.snapshots) {
409
+ if (v.at < oldestAt) {
410
+ oldestAt = v.at;
411
+ oldestKey = k;
412
+ }
413
+ }
414
+ if (oldestKey)
415
+ this.snapshots.delete(oldestKey);
416
+ }
417
+ this.snapshots.set(sessionID, { result, at: Date.now() });
418
+ }
419
+ /**
420
+ * Latest precomputed scan snapshot for a session. The sync recall path
421
+ * merges this in-memory and never awaits a scan. Snapshots expire after
422
+ * 10 minutes.
423
+ */
424
+ getSnapshot(sessionID) {
425
+ const snap = this.snapshots.get(sessionID);
426
+ if (!snap)
427
+ return null;
428
+ if (Date.now() - snap.at > IndexScanService.SNAPSHOT_TTL_MS) {
429
+ this.snapshots.delete(sessionID);
430
+ return null;
431
+ }
432
+ return snap.result;
433
+ }
434
+ /** No long-lived resources to dispose (direct HTTP, no sessions). */
435
+ async dispose() { }
436
+ }
437
+ exports.IndexScanService = IndexScanService;
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NOTE_BOARD_BUDGET = exports.PINNED_BUDGET = void 0;
4
+ exports.renderMemoryBlocks = renderMemoryBlocks;
5
+ exports.formatRecallContext = formatRecallContext;
6
+ exports.formatPinnedProfile = formatPinnedProfile;
7
+ exports.formatNoteBoard = formatNoteBoard;
8
+ function formatDate(iso) {
9
+ if (!iso)
10
+ return '';
11
+ try {
12
+ const d = new Date(iso);
13
+ if (Number.isNaN(d.getTime()))
14
+ return '';
15
+ const yyyy = d.getFullYear();
16
+ const mm = String(d.getMonth() + 1).padStart(2, '0');
17
+ const dd = String(d.getDate()).padStart(2, '0');
18
+ return `${yyyy}-${mm}-${dd}`;
19
+ }
20
+ catch {
21
+ return '';
22
+ }
23
+ }
24
+ function pointerLine(m) {
25
+ const tags = [];
26
+ const date = formatDate(m.created_at);
27
+ if (date)
28
+ tags.push(date);
29
+ if (m.type)
30
+ tags.push(m.type);
31
+ const tagStr = tags.length > 0 ? `[${tags.join(' ')}] ` : '';
32
+ const text = (m.primary_abstraction || m.memory_value || '?').replace(/\n/g, ' ');
33
+ const energy = typeof m.energy === 'number' ? m.energy.toFixed(1) : '?';
34
+ // ids are `mem_<timestamp>_<rand>` — the FIRST 6 chars are constant across
35
+ // every memory; use the tail so pointers stay unique and verifiable.
36
+ return `- #mem-${(m.id || '?').slice(-6)} ${tagStr}"${text}" (E:${energy})`;
37
+ }
38
+ // Maps an entry to its block bucket. Order matters: parametric/delta entries go
39
+ // to <deltas>, procedural patterns to <patterns>, episodic/history to
40
+ // <history>, everything else to <facts>. Mirrors the classification used by
41
+ // the gateway first-turn transform.
42
+ function bucketFor(entry) {
43
+ if (entry.source === 'parametric' || entry.type === 'delta' || entry.type === 'constraint' || entry.type === 'prompt')
44
+ return 'deltas';
45
+ if (entry.source === 'procedural' || entry.type === 'pattern')
46
+ return 'patterns';
47
+ if (entry.source === 'episodic' || entry.type === 'history' || entry.type === 'review' || entry.verdict)
48
+ return 'history';
49
+ return 'facts';
50
+ }
51
+ function lineFor(entry) {
52
+ switch (bucketFor(entry)) {
53
+ case 'deltas':
54
+ return `[Δ ${entry.type || 'constraint'}] ${entry.content || entry.rule || entry.prompt_delta || entry.pattern_template || ''}`;
55
+ case 'patterns':
56
+ return `[${((entry.successRate || 0) * 100).toFixed(0)}%] ${entry.pattern || entry.content || ''}`;
57
+ case 'history':
58
+ return `Loop ${entry.loopNum || '?'}: ${entry.verdict || '?'} ☑ ${entry.summary || entry.content || ''}`;
59
+ case 'facts': {
60
+ const content = (entry.facts && entry.facts.length ? entry.facts.join('; ') : entry.content) || '';
61
+ return content ? `☑ ${content}` : '';
62
+ }
63
+ }
64
+ }
65
+ /**
66
+ * Renders a list of memory entries into XML block strings, one per bucket:
67
+ * `<deltas>`, `<patterns>`, `<facts>`, `<history>`. Empty buckets are omitted.
68
+ * Callers join with "\n\n".
69
+ */
70
+ function renderMemoryBlocks(entries) {
71
+ const buckets = { deltas: [], patterns: [], facts: [], history: [] };
72
+ for (const entry of entries) {
73
+ const line = lineFor(entry);
74
+ if (line.trim())
75
+ buckets[bucketFor(entry)].push(line);
76
+ }
77
+ const chunks = [];
78
+ for (const tag of ['deltas', 'patterns', 'facts', 'history']) {
79
+ if (buckets[tag].length > 0)
80
+ chunks.push(`<${tag}>\n${buckets[tag].join('\n')}\n</${tag}>`);
81
+ }
82
+ return chunks;
83
+ }
84
+ // ---- Pointer rendering (boundary recall) ----
85
+ const POINTER_HEADER = `[联想线索 · 依据前请验证:取全文 → mafw_get_memory("#mem-后6位");检索更多 → mafw_search_hybrid]`;
86
+ const TAG = `<recall>`;
87
+ const END_TAG = `</recall>`;
88
+ /**
89
+ * Group memories by type for better cross-session visibility.
90
+ * Multi-session queries benefit from seeing memories organized by topic.
91
+ */
92
+ function groupMemoriesByType(memories) {
93
+ const groups = new Map();
94
+ for (const m of memories) {
95
+ const type = m.type || 'other';
96
+ if (!groups.has(type))
97
+ groups.set(type, []);
98
+ groups.get(type).push(m);
99
+ }
100
+ return groups;
101
+ }
102
+ function formatRecallContext(memories) {
103
+ if (memories.length === 0)
104
+ return { pointers: null };
105
+ // Episodic grouping: memories sharing a source session render under one
106
+ // session header with a date range (Memora-style narrative recovery —
107
+ // multi-session questions need to see that two hits came from one episode).
108
+ const sessionGroups = new Map();
109
+ for (const m of memories) {
110
+ if (!m.source_session_id)
111
+ continue;
112
+ if (!sessionGroups.has(m.source_session_id))
113
+ sessionGroups.set(m.source_session_id, []);
114
+ sessionGroups.get(m.source_session_id).push(m);
115
+ }
116
+ const groupedIds = new Set();
117
+ for (const mems of sessionGroups.values()) {
118
+ if (mems.length < 2)
119
+ continue;
120
+ for (const m of mems)
121
+ groupedIds.add(m.id);
122
+ }
123
+ const groups = groupMemoriesByType(memories);
124
+ const lines = [];
125
+ let sessionHeaders = 0;
126
+ for (const [sessionId, mems] of sessionGroups) {
127
+ if (mems.length < 2)
128
+ continue;
129
+ const dates = mems.map(m => formatDate(m.created_at)).filter(Boolean).sort();
130
+ const range = dates.length >= 2 && dates[0] !== dates[dates.length - 1]
131
+ ? `${dates[0]}~${dates[dates.length - 1]}`
132
+ : dates[0] ?? '';
133
+ lines.push(`[session ${sessionId.slice(0, 8)}${range ? ' · ' + range : ''}]`);
134
+ for (const m of mems) {
135
+ lines.push(` ${pointerLine(m)}`);
136
+ }
137
+ sessionHeaders++;
138
+ }
139
+ // Everything else (ungrouped or singletons) renders as before.
140
+ const rest = memories.filter(m => !groupedIds.has(m.id));
141
+ const restGroups = groupMemoriesByType(rest);
142
+ if (restGroups.size <= 1 && sessionHeaders === 0) {
143
+ lines.push(...rest.slice(0, 3).map(pointerLine));
144
+ }
145
+ else if (rest.length > 0) {
146
+ for (const [type, mems] of restGroups) {
147
+ if (mems.length === 0)
148
+ continue;
149
+ lines.push(`[${type}]`);
150
+ for (const m of mems.slice(0, 2)) {
151
+ lines.push(` ${pointerLine(m)}`);
152
+ }
153
+ }
154
+ }
155
+ const pointers = `${TAG}\n${POINTER_HEADER}\n${lines.slice(0, 5).join('\n')}\n${END_TAG}`;
156
+ return { pointers };
157
+ }
158
+ // ---- Pinned disclosure profile rendering (GET /api/recall/pinned) ----
159
+ exports.PINNED_BUDGET = { max: 20, maxChars: 2000 };
160
+ const PROFILE_TAG = '<user-profile>';
161
+ const PROFILE_END_TAG = '</user-profile>';
162
+ /**
163
+ * Renders pinned memories into the <user-profile> disclosure block.
164
+ * Enforces the hard budget (max entries / max chars); entries beyond the
165
+ * budget are dropped (caller logs the overflow count). Returns null profile
166
+ * for an empty set — callers must not inject an empty block.
167
+ */
168
+ function formatPinnedProfile(entries) {
169
+ const lines = [];
170
+ let chars = 0;
171
+ for (const m of entries.slice(0, exports.PINNED_BUDGET.max)) {
172
+ const text = (m.memory_value || m.primary_abstraction || '?').replace(/\n/g, ' ');
173
+ const line = `- ${text}`;
174
+ if (chars + line.length > exports.PINNED_BUDGET.maxChars)
175
+ break;
176
+ lines.push(line);
177
+ chars += line.length;
178
+ }
179
+ if (lines.length === 0)
180
+ return { profile: null, used: 0 };
181
+ return { profile: `${PROFILE_TAG}\n${lines.join('\n')}\n${PROFILE_END_TAG}`, used: lines.length };
182
+ }
183
+ // ---- Sticky note board rendering (appended to /api/recall/context) ----
184
+ // maxChars 1600 ≈ 800-1200 tok/turn(中文 ≈1 字/token),低于 pinned 的 2000
185
+ // 保持"pinned=长期身份 > sticky=近期提醒"的层级。perNoteCap 是防饿死关键:
186
+ // 无上限时一条超长便签吃光总预算,后续条目全部被 break 丢弃(实证 bug)。
187
+ exports.NOTE_BOARD_BUDGET = { max: 10, maxChars: 1600, perNoteCap: 240 };
188
+ const BOARD_TAG = '<note-board>';
189
+ const BOARD_END_TAG = '</note-board>';
190
+ /**
191
+ * Renders sticky notes into the <note-board> block: guaranteed per-turn
192
+ * visibility for user-commissioned memories until their sticky_until passes.
193
+ * Board-level expiry only — the memory itself is never deleted or hidden
194
+ * from search. Enforces a hard budget; returns null board for an empty set.
195
+ * `now` is injectable for deterministic tests.
196
+ */
197
+ function formatNoteBoard(entries, now = new Date()) {
198
+ const lines = [];
199
+ let chars = 0;
200
+ for (const m of entries.slice(0, exports.NOTE_BOARD_BUDGET.max)) {
201
+ let text = (m.memory_value || m.primary_abstraction || '?').replace(/\n/g, ' ');
202
+ if (text.length > exports.NOTE_BOARD_BUDGET.perNoteCap) {
203
+ // 单条截断防饿死:板上只留前 240 字;完整内容 NotesDock/get_memory 可看
204
+ text = `${text.slice(0, exports.NOTE_BOARD_BUDGET.perNoteCap)}…`;
205
+ }
206
+ const until = m.sticky_until ? new Date(m.sticky_until) : null;
207
+ const valid = until && !Number.isNaN(until.getTime());
208
+ const daysLeft = valid ? Math.max(0, Math.ceil((until.getTime() - now.getTime()) / 86400e3)) : null;
209
+ const date = valid ? formatDate(m.sticky_until) : '';
210
+ const line = `- ${date ? `[${date.slice(5)}] ` : ''}${text}${daysLeft !== null ? `(剩 ${daysLeft} 天)` : ''}`;
211
+ if (chars + line.length > exports.NOTE_BOARD_BUDGET.maxChars)
212
+ break;
213
+ lines.push(line);
214
+ chars += line.length;
215
+ }
216
+ if (lines.length === 0)
217
+ return { board: null, used: 0 };
218
+ return {
219
+ board: `${BOARD_TAG}\n[用户叮嘱 · 到期自动下架,记忆本体保留]\n${lines.join('\n')}\n${BOARD_END_TAG}`,
220
+ used: lines.length,
221
+ };
222
+ }