@herbertgao/pi-extensions 2026.8.11 → 2026.8.13

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 (476) hide show
  1. package/README.md +23 -10
  2. package/{node_modules/@herbertgao/pi-mermaid-open/LICENSE → THIRD_PARTY_NOTICES.md} +19 -1
  3. package/node_modules/@czottmann/pi-automode/CHANGELOG.md +15 -0
  4. package/node_modules/@czottmann/pi-automode/README.md +5 -3
  5. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-001-permission-precedence-and-trust-boundaries.md +2 -0
  6. package/node_modules/@czottmann/pi-automode/docs/adr/ADR-002-global-config-in-extension-data-directory.md +60 -0
  7. package/node_modules/@czottmann/pi-automode/docs/adr/INDEX.md +1 -0
  8. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +4 -4
  9. package/node_modules/@czottmann/pi-automode/docs/configuration.md +20 -4
  10. package/node_modules/@czottmann/pi-automode/docs/observability-logging.md +1 -1
  11. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/bash.ts +692 -0
  12. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +181 -3
  13. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +4 -1
  14. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +143 -36
  15. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +103 -148
  16. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +32 -6
  17. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +344 -22
  18. package/node_modules/@czottmann/pi-automode/extensions/auto-mode.ts +1 -0
  19. package/node_modules/@czottmann/pi-automode/package.json +4 -1
  20. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -1
  21. package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -1
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +4 -0
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +21 -1
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +1 -1
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +19 -19
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +4 -2
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +2 -1
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +10 -2
  29. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  30. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +10 -0
  31. package/node_modules/@herbertgao/pi-subagents/README.md +21 -15
  32. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  33. package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +11 -8
  34. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +184 -106
  35. package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +31 -11
  36. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +9 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/index.ts +156 -31
  38. package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +17 -0
  39. package/node_modules/@herbertgao/pi-subagents/src/model-resolver.ts +14 -0
  40. package/node_modules/@herbertgao/pi-subagents/src/schedule.ts +8 -0
  41. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +32 -1
  42. package/node_modules/@herbertgao/pi-subagents/src/types.ts +17 -2
  43. package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +23 -2
  44. package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +259 -24
  45. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  46. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +376 -0
  47. package/node_modules/@luxusai/pi-hindsight/README.md +92 -0
  48. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +56 -0
  49. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +169 -0
  50. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +107 -0
  51. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +89 -0
  52. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +189 -0
  53. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  54. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  55. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +54 -0
  56. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +75 -0
  57. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +300 -0
  58. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +158 -0
  59. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +164 -0
  60. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +139 -0
  61. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +74 -0
  62. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +261 -0
  63. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +151 -0
  64. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +18 -0
  65. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +46 -0
  66. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +383 -0
  67. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +240 -0
  68. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +176 -0
  69. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +178 -0
  70. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +41 -0
  71. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +386 -0
  72. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +39 -0
  73. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +34 -0
  74. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +140 -0
  75. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +61 -0
  76. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +925 -0
  77. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +247 -0
  78. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +547 -0
  79. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +480 -0
  80. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +182 -0
  81. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +86 -0
  82. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +1010 -0
  83. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +175 -0
  84. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +425 -0
  85. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +210 -0
  86. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +817 -0
  87. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +25 -0
  88. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +319 -0
  89. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +196 -0
  90. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +189 -0
  91. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +65 -0
  92. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +260 -0
  93. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +244 -0
  94. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +59 -0
  95. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +82 -0
  96. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +45 -0
  97. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +331 -0
  98. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +354 -0
  99. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +66 -0
  100. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +132 -0
  101. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +228 -0
  102. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +74 -0
  103. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +16 -0
  104. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +648 -0
  105. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +105 -0
  106. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +68 -0
  107. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +71 -0
  108. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +13 -0
  109. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +162 -0
  110. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +131 -0
  111. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +104 -0
  112. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +37 -0
  113. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +893 -0
  114. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +35 -0
  115. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +177 -0
  116. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +7 -0
  117. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +21 -0
  118. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +112 -0
  119. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +111 -0
  120. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +214 -0
  121. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +70 -0
  122. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +392 -0
  123. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +45 -0
  124. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +9 -0
  125. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +860 -0
  126. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +105 -0
  127. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +189 -0
  128. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +299 -0
  129. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +201 -0
  130. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +47 -0
  131. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +239 -0
  132. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +50 -0
  133. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +259 -0
  134. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +77 -0
  135. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +534 -0
  136. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +319 -0
  137. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +325 -0
  138. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +42 -0
  139. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +244 -0
  140. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +56 -0
  141. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +50 -0
  142. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +167 -0
  143. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +207 -0
  144. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +120 -0
  145. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +8 -0
  146. package/node_modules/@luxusai/pi-hindsight/package.json +118 -0
  147. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +75 -0
  148. package/node_modules/@narumitw/pi-btw/dist/index.ts +143 -46
  149. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  150. package/node_modules/@narumitw/pi-btw/package.json +6 -6
  151. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +136 -22
  152. package/node_modules/{@herbertgao → @tifan}/pi-copy-response/README.md +2 -4
  153. package/node_modules/{@herbertgao → @tifan}/pi-copy-response/package.json +5 -15
  154. package/node_modules/{@herbertgao → @tifan}/pi-handoff/README.md +4 -6
  155. package/node_modules/@tifan/pi-handoff/package.json +43 -0
  156. package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/index.ts +1 -1
  157. package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/README.md +2 -4
  158. package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/package.json +5 -15
  159. package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/README.md +5 -7
  160. package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/package.json +5 -19
  161. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/README.md +4 -6
  162. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/package.json +6 -16
  163. package/node_modules/{@herbertgao → @tifan}/pi-preferred-thinking/src/index.ts +1 -6
  164. package/node_modules/{@herbertgao → @tifan}/pi-recap/README.md +4 -6
  165. package/node_modules/@tifan/pi-recap/package.json +42 -0
  166. package/node_modules/{@herbertgao → @tifan}/pi-recap/src/models.ts +1 -6
  167. package/node_modules/{@herbertgao → @tifan}/pi-rename/README.md +13 -8
  168. package/node_modules/@tifan/pi-rename/package.json +47 -0
  169. package/node_modules/@tifan/pi-rename/src/herdr-label.ts +4 -0
  170. package/node_modules/{@herbertgao → @tifan}/pi-rename/src/index.ts +4 -16
  171. package/node_modules/{@herbertgao → @tifan}/pi-rename/src/models.ts +1 -6
  172. package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/README.md +2 -4
  173. package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/package.json +5 -15
  174. package/node_modules/pi-lens/CHANGELOG.md +96 -1
  175. package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +2 -1
  176. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +14 -5
  177. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +48 -18
  178. package/node_modules/pi-lens/dist/clients/ast-grep-client.js +1 -3
  179. package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +16 -4
  180. package/node_modules/pi-lens/dist/clients/bash-file-access.js +3 -1
  181. package/node_modules/pi-lens/dist/clients/biome-client.js +1 -3
  182. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +21 -8
  183. package/node_modules/pi-lens/dist/clients/bounded-cache.js +24 -8
  184. package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +4 -3
  185. package/node_modules/pi-lens/dist/clients/cache-manager.js +6 -7
  186. package/node_modules/pi-lens/dist/clients/cache-observability.js +461 -66
  187. package/node_modules/pi-lens/dist/clients/call-graph.js +238 -63
  188. package/node_modules/pi-lens/dist/clients/codebase-model.js +13 -5
  189. package/node_modules/pi-lens/dist/clients/complexity-client.js +1 -3
  190. package/node_modules/pi-lens/dist/clients/dead-code-client.js +19 -8
  191. package/node_modules/pi-lens/dist/clients/dead-code-logger.js +1 -2
  192. package/node_modules/pi-lens/dist/clients/deadline-utils.js +3 -1
  193. package/node_modules/pi-lens/dist/clients/debug-heap.js +8 -1
  194. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +1 -1
  195. package/node_modules/pi-lens/dist/clients/dependency-checker.js +11 -7
  196. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +39 -2
  197. package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +5 -1
  198. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +2 -3
  199. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +13 -1
  200. package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +3 -1
  201. package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +2 -1
  202. package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +1 -1
  203. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +9 -4
  204. package/node_modules/pi-lens/dist/clients/dispatch/rules/error-swallowing.js +3 -1
  205. package/node_modules/pi-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +6 -2
  206. package/node_modules/pi-lens/dist/clients/dispatch/rules/unsafe-boundary.js +31 -7
  207. package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +5 -2
  208. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +10 -14
  209. package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +5 -1
  210. package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +1 -1
  211. package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +3 -1
  212. package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +25 -6
  213. package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +1 -1
  214. package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +1 -1
  215. package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +3 -1
  216. package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +13 -8
  217. package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +4 -1
  218. package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +25 -10
  219. package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +1 -1
  220. package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +1 -1
  221. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +1 -1
  222. package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +28 -4
  223. package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +6 -8
  224. package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +212 -11
  225. package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +1 -1
  226. package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +8 -4
  227. package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -6
  228. package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +23 -14
  229. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +1 -1
  230. package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +7 -9
  231. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +5 -2
  232. package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +7 -7
  233. package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +6 -8
  234. package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +13 -8
  235. package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +13 -8
  236. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +17 -3
  237. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +7 -1
  238. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +24 -1
  239. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +35 -0
  240. package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +1 -3
  241. package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +6 -8
  242. package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +1 -1
  243. package/node_modules/pi-lens/dist/clients/dispatch/suppress-writer.js +2 -1
  244. package/node_modules/pi-lens/dist/clients/dispatch/types.js +6 -1
  245. package/node_modules/pi-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +6 -2
  246. package/node_modules/pi-lens/dist/clients/extension-log.js +5 -3
  247. package/node_modules/pi-lens/dist/clients/feature-hints.js +8 -1
  248. package/node_modules/pi-lens/dist/clients/file-kinds.js +34 -129
  249. package/node_modules/pi-lens/dist/clients/file-utils.js +17 -1
  250. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +57 -2
  251. package/node_modules/pi-lens/dist/clients/formatters.js +144 -27
  252. package/node_modules/pi-lens/dist/clients/freshness.js +38 -0
  253. package/node_modules/pi-lens/dist/clients/git-guard.js +43 -13
  254. package/node_modules/pi-lens/dist/clients/go-client.js +1 -3
  255. package/node_modules/pi-lens/dist/clients/grammar-source.js +1 -1
  256. package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +30 -4
  257. package/node_modules/pi-lens/dist/clients/host-edit-normalize.js +1 -3
  258. package/node_modules/pi-lens/dist/clients/install-diagnostics.js +4 -2
  259. package/node_modules/pi-lens/dist/clients/installer/index.js +86 -77
  260. package/node_modules/pi-lens/dist/clients/instance-reaper.js +5 -2
  261. package/node_modules/pi-lens/dist/clients/instance-registry.js +4 -2
  262. package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
  263. package/node_modules/pi-lens/dist/clients/knip-client.js +29 -15
  264. package/node_modules/pi-lens/dist/clients/language-profile.js +4 -1
  265. package/node_modules/pi-lens/dist/clients/latency-logger.js +13 -2
  266. package/node_modules/pi-lens/dist/clients/lens-config.js +1 -1
  267. package/node_modules/pi-lens/dist/clients/lens-map.js +8 -3
  268. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +1 -1
  269. package/node_modules/pi-lens/dist/clients/lsp/config.js +4 -6
  270. package/node_modules/pi-lens/dist/clients/lsp/edits.js +119 -32
  271. package/node_modules/pi-lens/dist/clients/lsp/index.js +94 -35
  272. package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +2 -1
  273. package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +4 -1
  274. package/node_modules/pi-lens/dist/clients/lsp/launch.js +11 -3
  275. package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +152 -0
  276. package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -7
  277. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +10 -2
  278. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +9 -4
  279. package/node_modules/pi-lens/dist/clients/lsp-document-symbols.js +1 -3
  280. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +10 -20
  281. package/node_modules/pi-lens/dist/clients/mcp/host-shim.js +1 -1
  282. package/node_modules/pi-lens/dist/clients/memory-sampler.js +73 -7
  283. package/node_modules/pi-lens/dist/clients/metrics-client.js +1 -3
  284. package/node_modules/pi-lens/dist/clients/middle-man-analysis.js +8 -3
  285. package/node_modules/pi-lens/dist/clients/module-report-lsp.js +7 -1
  286. package/node_modules/pi-lens/dist/clients/module-report.js +14 -17
  287. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +3 -5
  288. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +220 -0
  289. package/node_modules/pi-lens/dist/clients/package-manager.js +6 -1
  290. package/node_modules/pi-lens/dist/clients/pipeline.js +57 -9
  291. package/node_modules/pi-lens/dist/clients/project-conventions.js +2 -8
  292. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +10 -7
  293. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +2 -1
  294. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +3 -1
  295. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +13 -9
  296. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +3 -1
  297. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -2
  298. package/node_modules/pi-lens/dist/clients/project-lens-config.js +12 -5
  299. package/node_modules/pi-lens/dist/clients/project-snapshot-fingerprint.js +48 -0
  300. package/node_modules/pi-lens/dist/clients/project-snapshot-persist-worker.js +15 -1
  301. package/node_modules/pi-lens/dist/clients/project-snapshot.js +472 -66
  302. package/node_modules/pi-lens/dist/clients/read-expansion.js +12 -2
  303. package/node_modules/pi-lens/dist/clients/read-guard-logger.js +5 -4
  304. package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +5 -2
  305. package/node_modules/pi-lens/dist/clients/read-guard.js +1 -1
  306. package/node_modules/pi-lens/dist/clients/resource-sampler.js +1 -1
  307. package/node_modules/pi-lens/dist/clients/reverse-deps.js +6 -5
  308. package/node_modules/pi-lens/dist/clients/review-graph/builder.js +42 -17
  309. package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +8 -2
  310. package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +10 -1
  311. package/node_modules/pi-lens/dist/clients/review-graph/query.js +2 -1
  312. package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +4 -2
  313. package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +2 -2
  314. package/node_modules/pi-lens/dist/clients/ruff-client.js +6 -5
  315. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +63 -31
  316. package/node_modules/pi-lens/dist/clients/runtime-context.js +12 -4
  317. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +83 -4
  318. package/node_modules/pi-lens/dist/clients/runtime-session.js +14 -0
  319. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +59 -3
  320. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +148 -34
  321. package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -6
  322. package/node_modules/pi-lens/dist/clients/rust-client.js +1 -3
  323. package/node_modules/pi-lens/dist/clients/safe-spawn.js +150 -11
  324. package/node_modules/pi-lens/dist/clients/security-scan-client.js +1 -3
  325. package/node_modules/pi-lens/dist/clients/session-summary.js +3 -1
  326. package/node_modules/pi-lens/dist/clients/sg-runner.js +1 -3
  327. package/node_modules/pi-lens/dist/clients/source-filter.js +10 -6
  328. package/node_modules/pi-lens/dist/clients/source-walker.js +1 -1
  329. package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +96 -0
  330. package/node_modules/pi-lens/dist/clients/startup-scan.js +4 -1
  331. package/node_modules/pi-lens/dist/clients/tool-definition.js +7 -0
  332. package/node_modules/pi-lens/dist/clients/tool-policy.js +26 -11
  333. package/node_modules/pi-lens/dist/clients/tool-render.js +2 -1
  334. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +10 -4
  335. package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +2 -2
  336. package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +12 -5
  337. package/node_modules/pi-lens/dist/clients/warm-attach.js +12 -5
  338. package/node_modules/pi-lens/dist/clients/widget-state.js +27 -11
  339. package/node_modules/pi-lens/dist/clients/word-index.js +3 -1
  340. package/node_modules/pi-lens/dist/clients/workspace-topology.js +8 -2
  341. package/node_modules/pi-lens/dist/clients/zizmor-config.js +9 -1
  342. package/node_modules/pi-lens/dist/i18n.js +13 -5
  343. package/node_modules/pi-lens/dist/index.js +10797 -8627
  344. package/node_modules/pi-lens/dist/mcp/server.js +3 -1
  345. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +68 -12
  346. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +21 -7
  347. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +164 -35
  348. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +24 -7
  349. package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
  350. package/node_modules/pi-lens/dist/tools/module-report.js +1 -1
  351. package/node_modules/pi-lens/dist/tools/project-report.js +1 -1
  352. package/node_modules/pi-lens/dist/tools/symbol-search.js +5 -2
  353. package/node_modules/pi-lens/docs/durable-store-audit-1202.md +1 -1
  354. package/node_modules/pi-lens/docs/servercapabilities.md +1 -1
  355. package/node_modules/pi-lens/package.json +160 -156
  356. package/node_modules/pi-lens/rules/typos/_typos.toml +2 -2
  357. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +31 -13
  358. package/node_modules/pi-lens/scripts/download-grammars.js +9 -3
  359. package/node_modules/pi-lens/scripts/rpc-load-check.mjs +30 -9
  360. package/node_modules/pi-mcp-adapter/CHANGELOG.md +47 -0
  361. package/node_modules/pi-mcp-adapter/README.md +42 -13
  362. package/node_modules/pi-mcp-adapter/commands.ts +4 -2
  363. package/node_modules/pi-mcp-adapter/config.ts +45 -5
  364. package/node_modules/pi-mcp-adapter/direct-tools.ts +103 -92
  365. package/node_modules/pi-mcp-adapter/dist/agent-dir.d.ts +10 -0
  366. package/node_modules/pi-mcp-adapter/dist/agent-dir.js +63 -0
  367. package/node_modules/pi-mcp-adapter/dist/agent-dir.js.map +1 -0
  368. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.d.ts +8 -0
  369. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js +356 -0
  370. package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js.map +1 -0
  371. package/node_modules/pi-mcp-adapter/dist/config.d.ts +123 -0
  372. package/node_modules/pi-mcp-adapter/dist/config.js +1087 -0
  373. package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -0
  374. package/node_modules/pi-mcp-adapter/dist/metadata-cache.d.ts +19 -0
  375. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +319 -0
  376. package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +1 -0
  377. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.d.ts +2 -0
  378. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js +156 -0
  379. package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js.map +1 -0
  380. package/node_modules/pi-mcp-adapter/dist/resource-tools.d.ts +1 -0
  381. package/node_modules/pi-mcp-adapter/dist/resource-tools.js +15 -0
  382. package/node_modules/pi-mcp-adapter/dist/resource-tools.js.map +1 -0
  383. package/node_modules/pi-mcp-adapter/dist/types.d.ts +564 -0
  384. package/node_modules/pi-mcp-adapter/dist/types.js +287 -0
  385. package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -0
  386. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.d.ts +6 -0
  387. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js +37 -0
  388. package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js.map +1 -0
  389. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.d.ts +104 -0
  390. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js +59 -0
  391. package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js.map +1 -0
  392. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.d.ts +4 -0
  393. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js +27 -0
  394. package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js.map +1 -0
  395. package/node_modules/pi-mcp-adapter/dist/utils.d.ts +32 -0
  396. package/node_modules/pi-mcp-adapter/dist/utils.js +338 -0
  397. package/node_modules/pi-mcp-adapter/dist/utils.js.map +1 -0
  398. package/node_modules/pi-mcp-adapter/failure-backoff.ts +23 -0
  399. package/node_modules/pi-mcp-adapter/index.ts +204 -82
  400. package/node_modules/pi-mcp-adapter/init.ts +41 -30
  401. package/node_modules/pi-mcp-adapter/lifecycle.ts +16 -8
  402. package/node_modules/pi-mcp-adapter/mcp-code.ts +2 -0
  403. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +66 -16
  404. package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +95 -7
  405. package/node_modules/pi-mcp-adapter/mcp-probe.ts +18 -4
  406. package/node_modules/pi-mcp-adapter/mcp-references.ts +368 -0
  407. package/node_modules/pi-mcp-adapter/mcp-status.ts +8 -19
  408. package/node_modules/pi-mcp-adapter/metadata-cache.ts +20 -12
  409. package/node_modules/pi-mcp-adapter/namespace-tools.ts +276 -0
  410. package/node_modules/pi-mcp-adapter/package.json +24 -6
  411. package/node_modules/pi-mcp-adapter/proxy-modes.ts +66 -10
  412. package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -0
  413. package/node_modules/pi-mcp-adapter/server-manager.ts +63 -12
  414. package/node_modules/pi-mcp-adapter/state.ts +1 -3
  415. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +16 -2
  416. package/node_modules/pi-mcp-adapter/types.ts +48 -1
  417. package/node_modules/pi-mcp-adapter/ui-server.ts +3 -19
  418. package/node_modules/pi-mcp-adapter/ui-session.ts +2 -1
  419. package/node_modules/pi-mcp-adapter/utils.ts +21 -19
  420. package/node_modules/pi-web-access/CHANGELOG.md +26 -0
  421. package/node_modules/pi-web-access/README.md +71 -14
  422. package/node_modules/pi-web-access/chrome-cookies.ts +115 -35
  423. package/node_modules/pi-web-access/credential-source.ts +6 -2
  424. package/node_modules/pi-web-access/curator-page.ts +5 -3
  425. package/node_modules/pi-web-access/curator-server.ts +2 -1
  426. package/node_modules/pi-web-access/extract.ts +89 -9
  427. package/node_modules/pi-web-access/fetch-params.ts +13 -0
  428. package/node_modules/pi-web-access/gemini-adc.ts +298 -0
  429. package/node_modules/pi-web-access/gemini-api.ts +59 -15
  430. package/node_modules/pi-web-access/gemini-search.ts +58 -18
  431. package/node_modules/pi-web-access/gemini-url-context.ts +3 -2
  432. package/node_modules/pi-web-access/gemini-web-config.ts +57 -7
  433. package/node_modules/pi-web-access/gemini-web.ts +9 -3
  434. package/node_modules/pi-web-access/github-api.ts +5 -4
  435. package/node_modules/pi-web-access/github-issue-pr.ts +700 -0
  436. package/node_modules/pi-web-access/index.ts +403 -297
  437. package/node_modules/pi-web-access/kimi-search.ts +234 -0
  438. package/node_modules/pi-web-access/openai-search.ts +118 -21
  439. package/node_modules/pi-web-access/package.json +3 -2
  440. package/node_modules/pi-web-access/ssrf-protection.ts +4 -1
  441. package/node_modules/pi-web-access/utils.ts +307 -1
  442. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  443. package/package.json +46 -37
  444. package/node_modules/@herbertgao/pi-copy-response/CHANGELOG.md +0 -49
  445. package/node_modules/@herbertgao/pi-copy-response/LICENSE +0 -22
  446. package/node_modules/@herbertgao/pi-handoff/CHANGELOG.md +0 -83
  447. package/node_modules/@herbertgao/pi-handoff/LICENSE +0 -22
  448. package/node_modules/@herbertgao/pi-handoff/package.json +0 -60
  449. package/node_modules/@herbertgao/pi-inline-skills/CHANGELOG.md +0 -57
  450. package/node_modules/@herbertgao/pi-inline-skills/LICENSE +0 -22
  451. package/node_modules/@herbertgao/pi-mermaid-open/CHANGELOG.md +0 -39
  452. package/node_modules/@herbertgao/pi-preferred-thinking/CHANGELOG.md +0 -63
  453. package/node_modules/@herbertgao/pi-preferred-thinking/LICENSE +0 -22
  454. package/node_modules/@herbertgao/pi-recap/CHANGELOG.md +0 -101
  455. package/node_modules/@herbertgao/pi-recap/LICENSE +0 -22
  456. package/node_modules/@herbertgao/pi-recap/package.json +0 -55
  457. package/node_modules/@herbertgao/pi-rename/CHANGELOG.md +0 -49
  458. package/node_modules/@herbertgao/pi-rename/LICENSE +0 -22
  459. package/node_modules/@herbertgao/pi-rename/package.json +0 -63
  460. package/node_modules/@herbertgao/pi-titlebar-spinner/CHANGELOG.md +0 -25
  461. package/node_modules/@herbertgao/pi-titlebar-spinner/LICENSE +0 -22
  462. /package/node_modules/{@herbertgao → @tifan}/pi-copy-response/src/index.ts +0 -0
  463. /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/complete-text.ts +0 -0
  464. /package/node_modules/{@herbertgao → @tifan}/pi-handoff/src/session-query.ts +0 -0
  465. /package/node_modules/{@herbertgao → @tifan}/pi-inline-skills/src/index.ts +0 -0
  466. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/herdr-plugin.toml +0 -0
  467. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/herdr-plugin/viewer.mjs +0 -0
  468. /package/node_modules/{@herbertgao → @tifan}/pi-mermaid-open/src/index.ts +0 -0
  469. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/index.ts +0 -0
  470. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/model-picker.ts +0 -0
  471. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/sanitize.ts +0 -0
  472. /package/node_modules/{@herbertgao → @tifan}/pi-recap/src/tui.ts +0 -0
  473. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/model-picker.ts +0 -0
  474. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/naming.ts +0 -0
  475. /package/node_modules/{@herbertgao → @tifan}/pi-rename/src/sanitize.ts +0 -0
  476. /package/node_modules/{@herbertgao → @tifan}/pi-titlebar-spinner/src/index.ts +0 -0
@@ -0,0 +1,331 @@
1
+ import { basename } from "node:path";
2
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
3
+ import {
4
+ RECALL_SCORE_FIELDS,
5
+ type HindsightLikeClient,
6
+ type HindsightTagGroup,
7
+ type RecallBlock,
8
+ type RecallFailure,
9
+ type RecallMinScores,
10
+ type RecallResultItem,
11
+ type RecallRole,
12
+ type ResolvedConfig,
13
+ } from "../types.js";
14
+ import { isInjectedHindsightMemory, projectMessageText } from "../utils/messages.js";
15
+ import { createMemoryIdentity } from "../operations/memory-identity.js";
16
+ import { redactError } from "../utils/sanitize.js";
17
+ import { withTimeout } from "../client/timeout.js";
18
+ import { loadMentalModelsForScopes } from "./mental-models.js";
19
+
20
+ export interface RecallScope {
21
+ kind?: "project" | "global";
22
+ bankId: string;
23
+ tagGroups?: HindsightTagGroup[];
24
+ }
25
+
26
+ function sourceFactText(value: unknown): string | undefined {
27
+ if (!value || typeof value !== "object") return undefined;
28
+ const text = (value as { text?: unknown }).text;
29
+ return typeof text === "string" && text.trim() ? text : undefined;
30
+ }
31
+
32
+ function textFromRecallResponse(response: unknown): RecallResultItem[] {
33
+ const record = response as Record<string, unknown>;
34
+ const raw = Array.isArray(record.results)
35
+ ? record.results
36
+ : Array.isArray(record.memories)
37
+ ? record.memories
38
+ : Array.isArray(response)
39
+ ? response
40
+ : [];
41
+ const sourceFacts =
42
+ record.source_facts && typeof record.source_facts === "object"
43
+ ? (record.source_facts as Record<string, unknown>)
44
+ : undefined;
45
+ return raw.map((entry) => {
46
+ const item = entry as RecallResultItem;
47
+ if (!sourceFacts || !item.source_fact_ids?.length) return item;
48
+ const evidence = item.source_fact_ids
49
+ .map((id) => sourceFactText(sourceFacts[id]))
50
+ .filter((text): text is string => Boolean(text));
51
+ return evidence.length ? { ...item, sourceFacts: evidence } : item;
52
+ });
53
+ }
54
+
55
+ function itemText(item: RecallResultItem): string {
56
+ return item.text ?? item.content ?? JSON.stringify(item);
57
+ }
58
+
59
+ export interface RecallQueryPolicy {
60
+ roles: RecallRole[];
61
+ contextTurns: number;
62
+ maxQueryChars: number;
63
+ preamble?: string;
64
+ includeDate?: boolean;
65
+ now?: Date;
66
+ hints?: string[];
67
+ }
68
+
69
+ function messageRole(message: AgentMessage): string {
70
+ return (message as unknown as { role?: string }).role ?? "unknown";
71
+ }
72
+
73
+ function messageContent(message: AgentMessage): string {
74
+ const content = projectMessageText(message).content;
75
+ return typeof content === "string" ? content : JSON.stringify(content ?? "");
76
+ }
77
+
78
+ function formatQueryMessage(message: AgentMessage): string | undefined {
79
+ const content = messageContent(message).trim();
80
+ return content ? `${messageRole(message)}: ${content}` : undefined;
81
+ }
82
+
83
+ export function truncateRecallQuery(query: string, maxChars: number): string {
84
+ if (query.length <= maxChars) return query;
85
+ return query.slice(query.length - maxChars).trimStart();
86
+ }
87
+
88
+ function truncateRecallQueryLines(
89
+ prefixLines: string[],
90
+ messageLines: string[],
91
+ maxChars: number,
92
+ ): string {
93
+ const lines = [...prefixLines, ...messageLines];
94
+ const query = lines.join("\n\n").trim();
95
+ if (query.length <= maxChars) return query;
96
+ const prefix = prefixLines.join("\n\n").trim();
97
+ if (!prefix) return truncateRecallQuery(query, maxChars);
98
+ const separator = "\n\n";
99
+ const remaining = maxChars - prefix.length - separator.length;
100
+ if (remaining >= 20)
101
+ return `${prefix}${separator}${truncateRecallQuery(messageLines.join("\n\n"), remaining)}`;
102
+ return truncateRecallQuery(query, maxChars);
103
+ }
104
+
105
+ export function composeRecallQuery(messages: AgentMessage[], policy: RecallQueryPolicy): string {
106
+ const allowedRoles = new Set<string>(policy.roles);
107
+ const selectedLines = messages
108
+ .filter((message) => allowedRoles.has(messageRole(message)))
109
+ .filter((message) => !isInjectedHindsightMemory(message))
110
+ .map(formatQueryMessage)
111
+ .filter((line): line is string => Boolean(line))
112
+ .slice(-Math.max(1, policy.contextTurns));
113
+ const prefixLines = [
114
+ ...(policy.preamble?.trim() ? [policy.preamble.trim()] : []),
115
+ ...(policy.includeDate
116
+ ? [`Current date: ${(policy.now ?? new Date()).toISOString().slice(0, 10)}`]
117
+ : []),
118
+ ...(policy.hints?.length ? [`Context hints: ${policy.hints.join("; ")}`] : []),
119
+ ];
120
+ return truncateRecallQueryLines(
121
+ prefixLines,
122
+ selectedLines.length ? selectedLines : ["current Pi coding task"],
123
+ policy.maxQueryChars,
124
+ );
125
+ }
126
+
127
+ const RECALL_SOURCE_FACT_LINES = 3;
128
+
129
+ export function renderRecallBlocks(blocks: RecallBlock[], topK = 12): string {
130
+ const nonEmpty = blocks.filter((block) => block.memoryCount > 0);
131
+ if (nonEmpty.length === 0) return "";
132
+ const lines = [
133
+ "<hindsight-memory>",
134
+ "Relevant prior memory. Use as context; do not quote unless useful.",
135
+ ];
136
+ for (const block of nonEmpty) {
137
+ lines.push(`\nBank: ${block.bankId}`);
138
+ block.results.slice(0, topK).forEach((item, index) => {
139
+ const tags = item.tags?.length ? ` [${item.tags.join(", ")}]` : "";
140
+ lines.push(`${index + 1}. ${itemText(item)}${tags}`);
141
+ for (const fact of (item.sourceFacts ?? []).slice(0, RECALL_SOURCE_FACT_LINES)) {
142
+ lines.push(` - evidence: ${fact}`);
143
+ }
144
+ });
145
+ }
146
+ lines.push("</hindsight-memory>");
147
+ return lines.join("\n");
148
+ }
149
+
150
+ function preambleForScope(config: ResolvedConfig, scope: RecallScope): string {
151
+ if (scope.kind === "project") return config.recall.projectQueryPreamble;
152
+ if (scope.kind === "global") return config.recall.globalQueryPreamble;
153
+ return config.recall.queryPreamble;
154
+ }
155
+
156
+ // User Bank recall is a smaller, durable supplement to project context, not an equal-weight
157
+ // second corpus, so it gets its own (smaller by default) token budget. Project Bank recall
158
+ // keeps recall.maxTokens unchanged so single-scope callers see no behavior change.
159
+ function maxTokensForScope(config: ResolvedConfig, scope: RecallScope): number {
160
+ return scope.kind === "global" ? config.recall.userMaxTokens : config.recall.maxTokens;
161
+ }
162
+
163
+ function queryHints(cwd: string, config: ResolvedConfig, scope: RecallScope): string[] {
164
+ const hints = scope.kind ? [`scope:${scope.kind}`] : [];
165
+ if (!config.recall.includeRepoHintsInQuery || scope.kind === "global") return hints;
166
+ const identity = createMemoryIdentity(cwd, config);
167
+ return [
168
+ ...hints,
169
+ `project:${identity.projectId}`,
170
+ `repo:${identity.repoKey}`,
171
+ `cwd:${basename(cwd)}`,
172
+ ];
173
+ }
174
+
175
+ export async function recallForContext(args: {
176
+ client: HindsightLikeClient;
177
+ config: ResolvedConfig;
178
+ scopes: RecallScope[];
179
+ messages: AgentMessage[];
180
+ cwd?: string;
181
+ }): Promise<{
182
+ rendered: string;
183
+ blocks: RecallBlock[];
184
+ failed: number;
185
+ failures: RecallFailure[];
186
+ }> {
187
+ const blocks: RecallBlock[] = [];
188
+ const failures: RecallFailure[] = [];
189
+ for (const scope of args.scopes) {
190
+ const query = composeRecallQuery(args.messages, {
191
+ roles: args.config.recall.roles,
192
+ contextTurns: args.config.recall.contextTurns,
193
+ maxQueryChars: args.config.recall.maxQueryChars,
194
+ preamble: preambleForScope(args.config, scope),
195
+ includeDate: args.config.recall.includeDateInQuery,
196
+ hints: args.cwd ? queryHints(args.cwd, args.config, scope) : [],
197
+ });
198
+ try {
199
+ const response = await withTimeout("hindsight recall", args.config.recall.timeoutMs, () =>
200
+ args.client.recall(scope.bankId, query, {
201
+ budget: args.config.recall.budget,
202
+ maxTokens: maxTokensForScope(args.config, scope),
203
+ types: args.config.recall.types,
204
+ preferObservations: args.config.recall.preferObservations,
205
+ ...(args.config.recall.includeSourceFacts
206
+ ? {
207
+ includeSourceFacts: true,
208
+ maxSourceFactsTokens: args.config.recall.maxSourceFactsTokens,
209
+ }
210
+ : {}),
211
+ ...(args.config.recall.queryTimestamp
212
+ ? { queryTimestamp: args.config.recall.queryTimestamp }
213
+ : {}),
214
+ ...(scope.tagGroups?.length ? { tagGroups: scope.tagGroups } : {}),
215
+ }),
216
+ );
217
+ const results = filterRecallQuality(
218
+ textFromRecallResponse(response),
219
+ args.config.recall.minScores,
220
+ ).items;
221
+ blocks.push({
222
+ bankId: scope.bankId,
223
+ query,
224
+ results,
225
+ memoryCount: results.length,
226
+ rendered: "",
227
+ });
228
+ } catch (error) {
229
+ failures.push({
230
+ bankId: scope.bankId,
231
+ query,
232
+ error: redactError(error),
233
+ ...(scope.kind ? { kind: scope.kind } : {}),
234
+ ...(scope.tagGroups?.length ? { tagGroups: scope.tagGroups } : {}),
235
+ });
236
+ }
237
+ }
238
+ const recallRendered = renderRecallBlocks(blocks, args.config.recall.topK);
239
+ const cwd = args.cwd ?? process.cwd();
240
+ const identity = createMemoryIdentity(cwd, args.config);
241
+ const mental = await loadMentalModelsForScopes({
242
+ client: args.client,
243
+ config: args.config,
244
+ bankIds: args.scopes.map((scope) => scope.bankId),
245
+ bankKinds: args.scopes.map((scope) => (scope.kind === "global" ? "user" : "project")),
246
+ projectId: identity.projectId,
247
+ });
248
+ const rendered = [mental.rendered, recallRendered].filter(Boolean).join("\n\n");
249
+ return {
250
+ rendered,
251
+ blocks: blocks.map((block) => ({ ...block, rendered: recallRendered })),
252
+ failed: failures.length,
253
+ failures,
254
+ };
255
+ }
256
+
257
+ export type RecallQualityDropReason =
258
+ | "blank-memory"
259
+ | "recall-contamination"
260
+ | "duplicate-memory"
261
+ | "below-score-floor";
262
+
263
+ export interface RecallQualityDecision {
264
+ item: RecallResultItem;
265
+ decision: "keep" | "drop";
266
+ reasons: RecallQualityDropReason[];
267
+ }
268
+
269
+ function normalizedMemoryText(item: RecallResultItem): string {
270
+ return itemText(item).replace(/\s+/g, " ").trim().toLowerCase();
271
+ }
272
+
273
+ function isRecallContamination(text: string): boolean {
274
+ return /<\/?hindsight[-_]memor(?:y|ies)>|<\/?hindsight-mental-models>|<\/?mental_models>|customType["']?\s*:\s*["']hindsight-(?:recall|mental-models)|last-recall\.json/.test(
275
+ text,
276
+ );
277
+ }
278
+
279
+ function isBelowScoreFloor(item: RecallResultItem, minScores?: RecallMinScores): boolean {
280
+ if (!minScores) return false;
281
+ const scores = item.scores;
282
+ // Fail open when Hindsight omits scores so BM25-only hits and passthrough rerankers
283
+ // are not silently discarded by local policy.
284
+ if (!scores) return false;
285
+ for (const field of RECALL_SCORE_FIELDS) {
286
+ const floor = minScores[field];
287
+ const value = scores[field];
288
+ if (typeof floor === "number" && typeof value === "number" && value < floor) return true;
289
+ }
290
+ return false;
291
+ }
292
+
293
+ export function classifyRecallQuality(
294
+ item: RecallResultItem,
295
+ seen: Set<string>,
296
+ minScores?: RecallMinScores,
297
+ ): RecallQualityDecision {
298
+ const text = normalizedMemoryText(item);
299
+ const reasons: RecallQualityDropReason[] = [];
300
+ if (!text) reasons.push("blank-memory");
301
+ if (isRecallContamination(itemText(item))) reasons.push("recall-contamination");
302
+ if (text && seen.has(text)) reasons.push("duplicate-memory");
303
+ if (isBelowScoreFloor(item, minScores)) reasons.push("below-score-floor");
304
+ if (!reasons.length) seen.add(text);
305
+ return { item, decision: reasons.length ? "drop" : "keep", reasons };
306
+ }
307
+
308
+ export function filterRecallQuality(
309
+ items: RecallResultItem[],
310
+ minScores?: RecallMinScores,
311
+ ): {
312
+ items: RecallResultItem[];
313
+ decisions: RecallQualityDecision[];
314
+ dropped: number;
315
+ reasonCounts: Partial<Record<RecallQualityDropReason, number>>;
316
+ } {
317
+ const seen = new Set<string>();
318
+ const decisions = items.map((item) => classifyRecallQuality(item, seen, minScores));
319
+ const reasonCounts: Partial<Record<RecallQualityDropReason, number>> = {};
320
+ for (const decision of decisions) {
321
+ for (const reason of decision.reasons) reasonCounts[reason] = (reasonCounts[reason] ?? 0) + 1;
322
+ }
323
+ return {
324
+ items: decisions
325
+ .filter((decision) => decision.decision === "keep")
326
+ .map((decision) => decision.item),
327
+ decisions,
328
+ dropped: decisions.filter((decision) => decision.decision === "drop").length,
329
+ reasonCounts,
330
+ };
331
+ }
@@ -0,0 +1,354 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
5
+
6
+ export const RETAIN_CURSOR_VERSION = 3 as const;
7
+ export const RETAIN_CURSOR_TAIL_WINDOW = 200;
8
+
9
+ export interface SessionRetainCursor {
10
+ index: number;
11
+ chainHash: string;
12
+ idChainHash: string;
13
+ tailStart: number;
14
+ messageIds: string[];
15
+ fingerprints: string[];
16
+ }
17
+
18
+ export interface SessionRetainState {
19
+ cursor?: SessionRetainCursor;
20
+ legacyFingerprints?: Set<string>;
21
+ }
22
+
23
+ interface RetainCursorStoreV3 {
24
+ version: typeof RETAIN_CURSOR_VERSION;
25
+ sessions: Record<string, SessionRetainCursor | LegacySessionEntry>;
26
+ }
27
+
28
+ interface LegacySessionEntry {
29
+ legacyFingerprints: string[];
30
+ }
31
+
32
+ type RetainCursorStoreV1 = {
33
+ sessions: Record<string, string[]>;
34
+ };
35
+
36
+ interface SessionRetainCursorV2 {
37
+ index: number;
38
+ chainHash: string;
39
+ idChainHash: string;
40
+ messageIds: string[];
41
+ fingerprints: string[];
42
+ }
43
+
44
+ export function retainCursorPath(cwd: string): string {
45
+ return join(cwd, ".pi", "hindsight", "retain-cursors.json");
46
+ }
47
+
48
+ export function messageFingerprint(message: AgentMessage): string {
49
+ const m = message as unknown as Record<string, unknown>;
50
+ const stable = {
51
+ id: m.id,
52
+ role: m.role,
53
+ timestamp: m.timestamp,
54
+ content: m.content,
55
+ toolName: m.toolName,
56
+ isError: m.isError,
57
+ model: m.model,
58
+ stopReason: m.stopReason,
59
+ };
60
+ return createHash("sha256").update(JSON.stringify(stable)).digest("hex");
61
+ }
62
+
63
+ function messageId(message: AgentMessage): string {
64
+ const id = (message as unknown as Record<string, unknown>).id;
65
+ if (typeof id === "string") return id;
66
+ if (typeof id === "number") return String(id);
67
+ return "";
68
+ }
69
+
70
+ export function chainHash(fingerprints: readonly string[], throughIndex: number): string {
71
+ let hash = "";
72
+ for (let i = 0; i <= throughIndex; i++) {
73
+ const fingerprint = fingerprints[i];
74
+ if (fingerprint === undefined) break;
75
+ hash = createHash("sha256")
76
+ .update(hash + fingerprint)
77
+ .digest("hex");
78
+ }
79
+ return hash;
80
+ }
81
+
82
+ export function idChainHash(messages: readonly AgentMessage[], throughIndex: number): string {
83
+ let hash = "";
84
+ for (let i = 0; i <= throughIndex; i++) {
85
+ const message = messages[i];
86
+ if (!message) break;
87
+ hash = createHash("sha256")
88
+ .update(hash + messageId(message))
89
+ .digest("hex");
90
+ }
91
+ return hash;
92
+ }
93
+
94
+ function buildCursor(messages: readonly AgentMessage[], throughIndex: number): SessionRetainCursor {
95
+ const tailStart = Math.max(0, throughIndex - RETAIN_CURSOR_TAIL_WINDOW + 1);
96
+ const tailMessages = messages.slice(tailStart, throughIndex + 1);
97
+ const prefixFingerprints = messages.slice(0, throughIndex + 1).map(messageFingerprint);
98
+ return {
99
+ index: throughIndex,
100
+ chainHash: chainHash(prefixFingerprints, throughIndex),
101
+ idChainHash: idChainHash(messages, throughIndex),
102
+ tailStart,
103
+ messageIds: tailMessages.map(messageId),
104
+ fingerprints: tailMessages.map(messageFingerprint),
105
+ };
106
+ }
107
+
108
+ function migrateCursorToV3(cursor: SessionRetainCursorV2): SessionRetainCursor {
109
+ const tailStart = Math.max(0, cursor.index - RETAIN_CURSOR_TAIL_WINDOW + 1);
110
+ return {
111
+ index: cursor.index,
112
+ chainHash: cursor.chainHash,
113
+ idChainHash: cursor.idChainHash,
114
+ tailStart,
115
+ messageIds: cursor.messageIds.slice(tailStart),
116
+ fingerprints: cursor.fingerprints.slice(tailStart),
117
+ };
118
+ }
119
+
120
+ function isLegacySessionEntry(value: unknown): value is LegacySessionEntry {
121
+ return (
122
+ !!value &&
123
+ typeof value === "object" &&
124
+ !Array.isArray(value) &&
125
+ Array.isArray((value as LegacySessionEntry).legacyFingerprints)
126
+ );
127
+ }
128
+
129
+ function isSessionRetainCursorV2(value: unknown): value is SessionRetainCursorV2 {
130
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
131
+ const cursor = value as SessionRetainCursorV2;
132
+ return (
133
+ typeof cursor.index === "number" &&
134
+ typeof cursor.chainHash === "string" &&
135
+ typeof cursor.idChainHash === "string" &&
136
+ Array.isArray(cursor.messageIds) &&
137
+ Array.isArray(cursor.fingerprints) &&
138
+ !("tailStart" in cursor)
139
+ );
140
+ }
141
+
142
+ function isSessionRetainCursor(value: unknown): value is SessionRetainCursor {
143
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
144
+ const cursor = value as SessionRetainCursor;
145
+ return (
146
+ typeof cursor.index === "number" &&
147
+ typeof cursor.chainHash === "string" &&
148
+ typeof cursor.idChainHash === "string" &&
149
+ typeof cursor.tailStart === "number" &&
150
+ Array.isArray(cursor.messageIds) &&
151
+ Array.isArray(cursor.fingerprints)
152
+ );
153
+ }
154
+
155
+ function normalizeLegacyFingerprints(value: unknown): string[] {
156
+ if (!Array.isArray(value)) return [];
157
+ return value.filter((item): item is string => typeof item === "string");
158
+ }
159
+
160
+ function readLegacyStore(parsed: RetainCursorStoreV1): Record<string, LegacySessionEntry> {
161
+ const sessions: Record<string, LegacySessionEntry> = {};
162
+ for (const [key, value] of Object.entries(parsed.sessions ?? {})) {
163
+ const fingerprints = normalizeLegacyFingerprints(value);
164
+ if (fingerprints.length > 0) sessions[key] = { legacyFingerprints: fingerprints };
165
+ }
166
+ return sessions;
167
+ }
168
+
169
+ async function readRetainCursorStore(path: string): Promise<RetainCursorStoreV3> {
170
+ try {
171
+ const parsed = JSON.parse(await readFile(path, "utf8")) as unknown;
172
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
173
+ return { version: RETAIN_CURSOR_VERSION, sessions: {} };
174
+ }
175
+
176
+ const version = (parsed as { version?: unknown }).version;
177
+ const sessions = (parsed as { sessions?: unknown }).sessions;
178
+ if (!sessions || typeof sessions !== "object" || Array.isArray(sessions)) {
179
+ return { version: RETAIN_CURSOR_VERSION, sessions: {} };
180
+ }
181
+
182
+ if (version === RETAIN_CURSOR_VERSION) {
183
+ const normalized: RetainCursorStoreV3["sessions"] = {};
184
+ for (const [key, value] of Object.entries(sessions)) {
185
+ if (isSessionRetainCursor(value)) normalized[key] = value;
186
+ else if (isLegacySessionEntry(value)) normalized[key] = value;
187
+ }
188
+ return { version: RETAIN_CURSOR_VERSION, sessions: normalized };
189
+ }
190
+
191
+ if (version === 2) {
192
+ const normalized: RetainCursorStoreV3["sessions"] = {};
193
+ for (const [key, value] of Object.entries(sessions)) {
194
+ if (isSessionRetainCursorV2(value)) normalized[key] = migrateCursorToV3(value);
195
+ else if (isLegacySessionEntry(value)) normalized[key] = value;
196
+ }
197
+ return { version: RETAIN_CURSOR_VERSION, sessions: normalized };
198
+ }
199
+
200
+ return {
201
+ version: RETAIN_CURSOR_VERSION,
202
+ sessions: readLegacyStore(parsed as RetainCursorStoreV1),
203
+ };
204
+ } catch (error) {
205
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
206
+ return { version: RETAIN_CURSOR_VERSION, sessions: {} };
207
+ }
208
+ throw error;
209
+ }
210
+ }
211
+
212
+ async function writeRetainCursorStore(path: string, store: RetainCursorStoreV3): Promise<void> {
213
+ await mkdir(dirname(path), { recursive: true });
214
+ const tmp = `${path}.tmp`;
215
+ await writeFile(tmp, `${JSON.stringify(store, null, 2)}\n`, "utf8");
216
+ await rename(tmp, path);
217
+ }
218
+
219
+ export async function readSessionRetainState(
220
+ cwd: string,
221
+ sessionId: string,
222
+ ): Promise<SessionRetainState | undefined> {
223
+ const store = await readRetainCursorStore(retainCursorPath(cwd));
224
+ const entry = store.sessions[sessionId];
225
+ if (!entry) return undefined;
226
+ if (isLegacySessionEntry(entry)) {
227
+ return { legacyFingerprints: new Set(entry.legacyFingerprints) };
228
+ }
229
+ return { cursor: entry };
230
+ }
231
+
232
+ function legacyRetainStart(messages: readonly AgentMessage[], legacy: ReadonlySet<string>): number {
233
+ for (let i = messages.length - 1; i >= 0; i--) {
234
+ const message = messages[i];
235
+ if (!message) continue;
236
+ if (legacy.has(messageFingerprint(message))) return i + 1;
237
+ }
238
+ return 0;
239
+ }
240
+
241
+ function prefixChainMatches(
242
+ messages: readonly AgentMessage[],
243
+ cursor: SessionRetainCursor,
244
+ ): boolean {
245
+ const fingerprints: string[] = [];
246
+ for (let i = 0; i <= cursor.index; i++) {
247
+ const message = messages[i];
248
+ if (!message) return false;
249
+ fingerprints.push(messageFingerprint(message));
250
+ }
251
+ return chainHash(fingerprints, cursor.index) === cursor.chainHash;
252
+ }
253
+
254
+ function filterWithCursor(
255
+ messages: readonly AgentMessage[],
256
+ cursor: SessionRetainCursor,
257
+ ): AgentMessage[] {
258
+ if (messages.length === 0) return [];
259
+
260
+ if (cursor.index < messages.length && prefixChainMatches(messages, cursor)) {
261
+ return messages.slice(cursor.index + 1);
262
+ }
263
+
264
+ const retained: AgentMessage[] = [];
265
+ let branched = false;
266
+
267
+ for (let i = 0; i < messages.length; i++) {
268
+ const message = messages[i];
269
+ if (!message) continue;
270
+
271
+ if (i > cursor.index) {
272
+ retained.push(message);
273
+ continue;
274
+ }
275
+
276
+ if (i < cursor.tailStart) {
277
+ if (branched) retained.push(message);
278
+ continue;
279
+ }
280
+
281
+ const offset = i - cursor.tailStart;
282
+ const idMatches = messageId(message) === cursor.messageIds[offset];
283
+ const fingerprintMatches = messageFingerprint(message) === cursor.fingerprints[offset];
284
+
285
+ if (!idMatches) {
286
+ branched = true;
287
+ retained.push(message);
288
+ continue;
289
+ }
290
+
291
+ if (branched) {
292
+ retained.push(message);
293
+ continue;
294
+ }
295
+
296
+ if (!fingerprintMatches) {
297
+ retained.push(message);
298
+ }
299
+ }
300
+
301
+ return retained;
302
+ }
303
+
304
+ export function filterNewRetainMessages(
305
+ messages: readonly AgentMessage[],
306
+ state: SessionRetainState | undefined,
307
+ ): AgentMessage[] {
308
+ if (!state) return [...messages];
309
+ if (state.cursor) return filterWithCursor(messages, state.cursor);
310
+ if (state.legacyFingerprints?.size) {
311
+ return messages.slice(legacyRetainStart(messages, state.legacyFingerprints));
312
+ }
313
+ return [...messages];
314
+ }
315
+
316
+ export async function advanceRetainCursor(
317
+ cwd: string,
318
+ sessionId: string,
319
+ allMessages: readonly AgentMessage[],
320
+ retainedThroughIndex: number,
321
+ ): Promise<void> {
322
+ if (retainedThroughIndex < 0 || retainedThroughIndex >= allMessages.length) return;
323
+
324
+ const path = retainCursorPath(cwd);
325
+ const store = await readRetainCursorStore(path);
326
+ const existing = store.sessions[sessionId];
327
+ const lastIndex = allMessages.length - 1;
328
+ let effectiveIndex = retainedThroughIndex;
329
+ if (isSessionRetainCursor(existing)) {
330
+ effectiveIndex = Math.max(existing.index, retainedThroughIndex);
331
+ }
332
+ effectiveIndex = Math.min(lastIndex, effectiveIndex);
333
+ store.sessions[sessionId] = buildCursor(allMessages, effectiveIndex);
334
+ await writeRetainCursorStore(path, store);
335
+ }
336
+
337
+ export function retainedThroughIndex(
338
+ allMessages: readonly AgentMessage[],
339
+ retainedMessages: readonly AgentMessage[],
340
+ ): number {
341
+ if (retainedMessages.length === 0) return -1;
342
+ const lastRetained = retainedMessages[retainedMessages.length - 1];
343
+ if (!lastRetained) return -1;
344
+ const lastId = messageId(lastRetained);
345
+ const lastFingerprint = messageFingerprint(lastRetained);
346
+ for (let i = 0; i < allMessages.length; i++) {
347
+ const message = allMessages[i];
348
+ if (!message) continue;
349
+ if (messageId(message) === lastId && messageFingerprint(message) === lastFingerprint) {
350
+ return i;
351
+ }
352
+ }
353
+ return -1;
354
+ }