@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,1010 @@
1
+ import type { HindsightLikeClient, ResolvedConfig, RetainJob, UpdateMode } from "../types.js";
2
+ import type { ImportBranch, ParsedSession } from "./import-parse.js";
3
+ import { baseTags } from "../banks/banking.js";
4
+ import { buildDurableRetainJob } from "../lifecycle/retain.js";
5
+ import { createHash } from "node:crypto";
6
+ import { createMemoryIdentity } from "../operations/memory-identity.js";
7
+ import {
8
+ enqueueRetain,
9
+ flushRetain,
10
+ readQueuedRetains,
11
+ removeQueuedRetains,
12
+ } from "../queue/queue.js";
13
+ import { expandObservationScopes } from "../lifecycle/observation-scopes.js";
14
+ import { hashImportContent, type ImportManifestEntry } from "./import-plan.js";
15
+ import { importDocumentId, stableSessionId } from "../utils/session.js";
16
+ import { isInjectedHindsightMemory, projectMessage, projectMessages } from "../utils/messages.js";
17
+ import { redactSecrets } from "../utils/sanitize.js";
18
+
19
+ export interface ImportRetainIdentity {
20
+ bankId: string;
21
+ documentId: string;
22
+ updateMode: UpdateMode;
23
+ sourceFile: string;
24
+ cwd: string;
25
+ sessionId: string;
26
+ leafId: string;
27
+ includeBranches: "current-only" | "all-leaves";
28
+ importMode?: "curated" | "raw" | "forensic" | undefined;
29
+ toolResults?: "errors-only" | "summary" | "content" | undefined;
30
+ importQualityProfile?: "compatible" | "strict" | undefined;
31
+ projectionVersion?: string | undefined;
32
+ importProfile?: string | undefined;
33
+ chunkIndex?: number | undefined;
34
+ messageRange?: { start: number; end: number } | undefined;
35
+ contentHash: string;
36
+ }
37
+
38
+ const IMPORT_RETAIN_METADATA_KEYS = [
39
+ "pi_session_file",
40
+ "imported",
41
+ "cwd",
42
+ "session_id",
43
+ "branch_leaf_id",
44
+ "import_mode",
45
+ "import_quality_profile",
46
+ "projection_version",
47
+ "import_profile",
48
+ "chunk_index",
49
+ "message_range_start",
50
+ "message_range_end",
51
+ "content_hash",
52
+ "include_branches",
53
+ "tool_results",
54
+ ] as const;
55
+
56
+ function identityMetadata(identity: ImportRetainIdentity): Record<string, string | undefined> {
57
+ return {
58
+ pi_session_file: identity.sourceFile,
59
+ imported: "true",
60
+ cwd: identity.cwd,
61
+ session_id: identity.sessionId,
62
+ branch_leaf_id: identity.leafId,
63
+ import_mode: identity.importMode,
64
+ import_quality_profile: identity.importQualityProfile,
65
+ projection_version: identity.projectionVersion,
66
+ import_profile: identity.importProfile,
67
+ ...(identity.chunkIndex !== undefined ? { chunk_index: String(identity.chunkIndex) } : {}),
68
+ ...(identity.messageRange
69
+ ? {
70
+ message_range_start: String(identity.messageRange.start),
71
+ message_range_end: String(identity.messageRange.end),
72
+ }
73
+ : {}),
74
+ content_hash: identity.contentHash,
75
+ include_branches: identity.includeBranches,
76
+ tool_results: identity.toolResults,
77
+ };
78
+ }
79
+
80
+ function recordEqual(
81
+ left: Record<string, string> | undefined,
82
+ right: Record<string, string> | undefined,
83
+ ): boolean {
84
+ const leftEntries = Object.entries(left ?? {}).sort(([a], [b]) => a.localeCompare(b));
85
+ const rightEntries = Object.entries(right ?? {}).sort(([a], [b]) => a.localeCompare(b));
86
+ return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
87
+ }
88
+
89
+ function stableEqual(left: unknown, right: unknown): boolean {
90
+ return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
91
+ }
92
+
93
+ export function isImportRetainJob(job: RetainJob): boolean {
94
+ return (
95
+ job.item.metadata?.source === "pi-hindsight" &&
96
+ job.item.metadata.retainSource === "import" &&
97
+ job.item.metadata.imported === "true"
98
+ );
99
+ }
100
+
101
+ export function importRetainJobMatchesReference(job: RetainJob, reference: RetainJob): boolean {
102
+ return (
103
+ job.bankId === reference.bankId &&
104
+ job.documentId === reference.documentId &&
105
+ job.updateMode === reference.updateMode &&
106
+ isImportRetainJob(job) &&
107
+ isImportRetainJob(reference) &&
108
+ job.item.content === reference.item.content &&
109
+ job.item.context === reference.item.context &&
110
+ recordEqual(job.item.metadata, reference.item.metadata) &&
111
+ stableEqual(job.item.tags, reference.item.tags) &&
112
+ stableEqual(job.item.observationScopes, reference.item.observationScopes)
113
+ );
114
+ }
115
+
116
+ export function staleImportRetainJobForReference(job: RetainJob, reference: RetainJob): boolean {
117
+ return (
118
+ job.bankId === reference.bankId &&
119
+ job.documentId === reference.documentId &&
120
+ isImportRetainJob(job) &&
121
+ !importRetainJobMatchesReference(job, reference)
122
+ );
123
+ }
124
+
125
+ export function importRetainJobMatchesIdentity(
126
+ job: RetainJob,
127
+ identity: ImportRetainIdentity,
128
+ ): boolean {
129
+ if (
130
+ job.bankId !== identity.bankId ||
131
+ job.documentId !== identity.documentId ||
132
+ job.updateMode !== identity.updateMode ||
133
+ !isImportRetainJob(job)
134
+ ) {
135
+ return false;
136
+ }
137
+ const expected = identityMetadata(identity);
138
+ const metadata = job.item.metadata ?? {};
139
+ return IMPORT_RETAIN_METADATA_KEYS.every((key) => metadata[key] === expected[key]);
140
+ }
141
+
142
+ export interface ImportRetainDeliveryArgs {
143
+ cwd: string;
144
+ config: ResolvedConfig;
145
+ client: HindsightLikeClient;
146
+ bankId: string;
147
+ content: string;
148
+ context: string;
149
+ documentId: string;
150
+ updateMode: UpdateMode;
151
+ tags: string[];
152
+ metadata?: Record<string, string>;
153
+ observationScopes?: string[][];
154
+ }
155
+
156
+ export interface ImportRetainDeliveryResult {
157
+ queueJobId: string;
158
+ enqueued: boolean;
159
+ delivered: boolean;
160
+ sent: number;
161
+ remaining: number;
162
+ deadLettered: number;
163
+ }
164
+
165
+ function stillQueued(jobs: RetainJob[], jobId: string): boolean {
166
+ return jobs.some((job) => job.id === jobId);
167
+ }
168
+
169
+ export async function deliverImportRetain(
170
+ args: ImportRetainDeliveryArgs,
171
+ ): Promise<ImportRetainDeliveryResult> {
172
+ const job = buildDurableRetainJob({
173
+ cwd: args.cwd,
174
+ config: args.config,
175
+ bankId: args.bankId,
176
+ content: args.content,
177
+ context: args.context,
178
+ documentId: args.documentId,
179
+ updateMode: args.updateMode,
180
+ tags: args.tags,
181
+ source: "import",
182
+ ...(args.metadata ? { metadata: args.metadata } : {}),
183
+ ...(args.observationScopes?.length ? { observationScopes: args.observationScopes } : {}),
184
+ });
185
+ await removeQueuedRetains(args.cwd, args.config, (queued) =>
186
+ staleImportRetainJobForReference(queued, job),
187
+ );
188
+ const existing = await readQueuedRetains(args.cwd, args.config);
189
+ const existingJob = existing.find((queued) => importRetainJobMatchesReference(queued, job));
190
+ if (!existingJob) await enqueueRetain(args.cwd, args.config, job);
191
+ const result = await flushRetain(args.cwd, args.config, args.client, {
192
+ stopOnFirstFailure: true,
193
+ });
194
+ const queued = await readQueuedRetains(args.cwd, args.config);
195
+ const queueJobId = existingJob?.id ?? job.id;
196
+ return {
197
+ queueJobId,
198
+ enqueued: true,
199
+ delivered: !stillQueued(queued, queueJobId),
200
+ sent: result.sent,
201
+ remaining: result.remaining,
202
+ deadLettered: result.deadLettered,
203
+ };
204
+ }
205
+
206
+ export const STRICT_SUCCESSFUL_TOOL_RESULT_MAX_BYTES = 2 * 1024;
207
+
208
+ export type ImportNoiseDropReason =
209
+ | "successful-tool-output"
210
+ | "tool-filter-excluded"
211
+ | "tool-result-empty"
212
+ | "recalled-memory"
213
+ | "empty-projection"
214
+ | "ui-noise"
215
+ | "process-noise"
216
+ | "oversized-output"
217
+ | "repeated-output";
218
+
219
+ export interface ImportToolNoisePolicy {
220
+ qualityProfile: ResolvedConfig["import"]["qualityProfile"];
221
+ dropSuccessful: boolean;
222
+ summaryMaxChars: number;
223
+ strictSuccessfulToolResultMaxBytes: number;
224
+ }
225
+
226
+ export interface StrictImportNoiseState {
227
+ seenSuccessfulToolResults: Set<string>;
228
+ }
229
+
230
+ export function createStrictImportNoiseState(): StrictImportNoiseState {
231
+ return { seenSuccessfulToolResults: new Set<string>() };
232
+ }
233
+
234
+ export function resolveImportToolNoisePolicy(config: ResolvedConfig): ImportToolNoisePolicy {
235
+ return {
236
+ qualityProfile: config.import.qualityProfile,
237
+ dropSuccessful: config.import.toolResults === "errors-only",
238
+ summaryMaxChars: config.import.toolResultSummaryMaxChars,
239
+ strictSuccessfulToolResultMaxBytes: STRICT_SUCCESSFUL_TOOL_RESULT_MAX_BYTES,
240
+ };
241
+ }
242
+
243
+ export function importToolAllowed(
244
+ name: string,
245
+ filter: { include?: string[]; exclude?: string[] },
246
+ ): boolean {
247
+ if (filter.include && !filter.include.includes(name)) return false;
248
+ return !filter.exclude?.includes(name);
249
+ }
250
+
251
+ function textFromToolResultContent(content: unknown): string {
252
+ if (typeof content === "string") return content;
253
+ if (typeof content === "number" || typeof content === "boolean" || typeof content === "bigint")
254
+ return String(content);
255
+ if (!Array.isArray(content)) return JSON.stringify(content ?? "");
256
+ return content
257
+ .map((part) => {
258
+ if (typeof part === "string") return part;
259
+ if (typeof part === "number" || typeof part === "boolean" || typeof part === "bigint")
260
+ return String(part);
261
+ if (part && typeof part === "object") {
262
+ const record = part as Record<string, unknown>;
263
+ if (typeof record.text === "string") return record.text;
264
+ if (typeof record.content === "string") return record.content;
265
+ if (typeof record.output === "string") return record.output;
266
+ }
267
+ return JSON.stringify(part ?? "");
268
+ })
269
+ .filter(Boolean)
270
+ .join("\n");
271
+ }
272
+
273
+ function normalizedName(value: unknown): string {
274
+ return typeof value === "string" ? value.trim().toLowerCase() : "";
275
+ }
276
+
277
+ function matchesAny(value: string, patterns: RegExp[]): boolean {
278
+ return patterns.some((pattern) => pattern.test(value));
279
+ }
280
+
281
+ const UI_NOISE_NAMES = new Set([
282
+ "ui",
283
+ "progress",
284
+ "spinner",
285
+ "toast",
286
+ "notification",
287
+ "render",
288
+ "message_update",
289
+ "extension_ui_request",
290
+ ]);
291
+
292
+ const PROCESS_NOISE_NAMES = new Set([
293
+ "process",
294
+ "process-status",
295
+ "status",
296
+ "watcher",
297
+ "watch",
298
+ "ci-status",
299
+ "check-status",
300
+ ]);
301
+
302
+ const UI_NOISE_PATTERNS = [/\bspinner\b/i, /\btoast\b/i, /\bprogress ui\b/i, /\bmessage update\b/i];
303
+ const PROCESS_NOISE_PATTERNS = [
304
+ /\brefreshing checks status\b/i,
305
+ /\bchecks? (still )?pending\b/i,
306
+ /\bwatcher (pending|continues|running)\b/i,
307
+ /\bwaiting for ci\b/i,
308
+ /\bprocess status\b/i,
309
+ ];
310
+
311
+ function messageTypeNames(message: Record<string, unknown>): string[] {
312
+ return [message.type, message.customType, message.event, message.kind]
313
+ .map(normalizedName)
314
+ .filter(Boolean);
315
+ }
316
+
317
+ export function summarizeToolResultContent(content: unknown, maxChars: number): string {
318
+ const text = typeof content === "string" ? content : JSON.stringify(content ?? "");
319
+ return text.length > maxChars ? `${text.slice(0, maxChars)}…` : text;
320
+ }
321
+
322
+ function successfulToolResultFingerprint(name: string, contentText: string): string {
323
+ return createHash("sha256")
324
+ .update(name)
325
+ .update("\0")
326
+ .update(contentText.trim().replace(/\s+/g, " "))
327
+ .digest("hex");
328
+ }
329
+
330
+ export function strictSuccessfulToolResultDropReason(
331
+ message: Record<string, unknown>,
332
+ policy: ImportToolNoisePolicy,
333
+ state: StrictImportNoiseState,
334
+ ): ImportNoiseDropReason | undefined {
335
+ if (policy.qualityProfile !== "strict") return undefined;
336
+ if (message.role !== "toolResult" || message.isError === true) return undefined;
337
+
338
+ const toolName = normalizedName(message.name ?? message.toolName ?? message.tool);
339
+ const typeNames = messageTypeNames(message);
340
+ const contentText = textFromToolResultContent(message.content);
341
+ const contentBytes = Buffer.byteLength(contentText, "utf8");
342
+ const combined = `${toolName}\n${typeNames.join("\n")}\n${contentText}`;
343
+
344
+ if (UI_NOISE_NAMES.has(toolName) || typeNames.some((type) => UI_NOISE_NAMES.has(type)))
345
+ return "ui-noise";
346
+ if (
347
+ PROCESS_NOISE_NAMES.has(toolName) ||
348
+ typeNames.some((type) => PROCESS_NOISE_NAMES.has(type)) ||
349
+ matchesAny(combined, PROCESS_NOISE_PATTERNS)
350
+ ) {
351
+ return "process-noise";
352
+ }
353
+ if (matchesAny(combined, UI_NOISE_PATTERNS)) return "ui-noise";
354
+ if (!contentText.trim()) return "tool-result-empty";
355
+ if (contentBytes > policy.strictSuccessfulToolResultMaxBytes) return "oversized-output";
356
+
357
+ const fingerprint = successfulToolResultFingerprint(toolName, contentText);
358
+ if (state.seenSuccessfulToolResults.has(fingerprint)) return "repeated-output";
359
+ state.seenSuccessfulToolResults.add(fingerprint);
360
+ return undefined;
361
+ }
362
+
363
+ export type CuratedImportKeepReason =
364
+ | "user-text"
365
+ | "assistant-text"
366
+ | "tool-error-kept"
367
+ | "message-kept";
368
+
369
+ export type CuratedImportDropReason = ImportNoiseDropReason;
370
+
371
+ export type CuratedImportReason = CuratedImportKeepReason | CuratedImportDropReason;
372
+
373
+ export interface CuratedImportClassification {
374
+ original: Record<string, unknown>;
375
+ stableMessage: Record<string, unknown>;
376
+ projectedMessages: Record<string, unknown>[];
377
+ decision: "keep" | "drop";
378
+ reasons: CuratedImportReason[];
379
+ rawBytes: number;
380
+ projectedBytes: number;
381
+ toolName?: string;
382
+ toolResultDropped?: boolean;
383
+ toolErrorKept?: boolean;
384
+ }
385
+
386
+ export interface CuratedImportProjection {
387
+ messages: Record<string, unknown>[];
388
+ rawBytes: number;
389
+ projectedBytes: number;
390
+ droppedToolResultCount: number;
391
+ droppedToolResultBytes: number;
392
+ droppedTools: Array<{ name: string; count: number; bytes: number }>;
393
+ topDroppedTools: Array<{ name: string; count: number; bytes: number }>;
394
+ keptToolErrorCount: number;
395
+ keptToolErrorBytes: number;
396
+ estimatedChunkCount: number;
397
+ classificationReasonCounts: Partial<Record<CuratedImportReason, number>>;
398
+ classifications: CuratedImportClassification[];
399
+ }
400
+
401
+ export function importByteLength(value: unknown): number {
402
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
403
+ }
404
+
405
+ export function importToolName(message: Record<string, unknown>): string {
406
+ const name = message.name ?? message.toolName ?? message.tool;
407
+ return typeof name === "string" && name.trim() ? name : "unknown";
408
+ }
409
+
410
+ export function stableProjectionMessage(message: Record<string, unknown>): Record<string, unknown> {
411
+ const timestamp = message.timestamp;
412
+ const parsedTimestamp = typeof timestamp === "string" ? Date.parse(timestamp) : undefined;
413
+ return {
414
+ ...message,
415
+ timestamp:
416
+ typeof timestamp === "number"
417
+ ? timestamp
418
+ : typeof parsedTimestamp === "number" && Number.isFinite(parsedTimestamp)
419
+ ? parsedTimestamp
420
+ : 0,
421
+ };
422
+ }
423
+
424
+ function mergeImportProvenance(
425
+ original: Record<string, unknown>,
426
+ projectedMessages: Record<string, unknown>[],
427
+ ): Record<string, unknown>[] {
428
+ return projectedMessages.map((message) => ({
429
+ ...(original.id !== undefined ? { id: original.id } : {}),
430
+ ...(original.parentId !== undefined ? { parentId: original.parentId } : {}),
431
+ ...message,
432
+ }));
433
+ }
434
+
435
+ function keepReason(message: Record<string, unknown>): CuratedImportKeepReason {
436
+ if (message.role === "user") return "user-text";
437
+ if (message.role === "assistant") return "assistant-text";
438
+ if (message.role === "toolResult" && message.isError === true) return "tool-error-kept";
439
+ return "message-kept";
440
+ }
441
+
442
+ function dropReasons(
443
+ message: Record<string, unknown>,
444
+ projectedMessages: Record<string, unknown>[],
445
+ config: ResolvedConfig,
446
+ strictDropReason?: ImportNoiseDropReason,
447
+ ): CuratedImportDropReason[] {
448
+ if (isInjectedHindsightMemory(message)) return ["recalled-memory"];
449
+ if (strictDropReason) return [strictDropReason];
450
+ if (!projectedMessages.length && message.role === "toolResult" && message.isError !== true) {
451
+ if (!importToolAllowed(importToolName(message), config.retain.toolFilter.toolResult))
452
+ return ["tool-filter-excluded"];
453
+ return ["successful-tool-output"];
454
+ }
455
+ return ["empty-projection"];
456
+ }
457
+
458
+ interface CuratedImportProjectionDecision {
459
+ projectedMessages: Record<string, unknown>[];
460
+ strictDropReason?: ImportNoiseDropReason;
461
+ }
462
+
463
+ function projectCuratedImportMessage(
464
+ stableMessage: Record<string, unknown>,
465
+ config: ResolvedConfig,
466
+ strictState: StrictImportNoiseState,
467
+ ): CuratedImportProjectionDecision {
468
+ if (stableMessage.role !== "toolResult" || stableMessage.isError === true) {
469
+ return { projectedMessages: projectMessages([stableMessage as never], config) };
470
+ }
471
+ const policy = resolveImportToolNoisePolicy(config);
472
+ if (!importToolAllowed(importToolName(stableMessage), config.retain.toolFilter.toolResult)) {
473
+ return { projectedMessages: [] };
474
+ }
475
+ const strictDropReason = strictSuccessfulToolResultDropReason(stableMessage, policy, strictState);
476
+ if (strictDropReason || policy.dropSuccessful)
477
+ return { projectedMessages: [], ...(strictDropReason ? { strictDropReason } : {}) };
478
+ const projected = projectMessage(stableMessage as never, {
479
+ ...config,
480
+ retain: {
481
+ ...config.retain,
482
+ content: {
483
+ ...config.retain.content,
484
+ toolResult: config.import.toolResults === "content" ? ["content"] : ["summary"],
485
+ },
486
+ },
487
+ });
488
+ return {
489
+ projectedMessages: [
490
+ config.import.toolResults === "summary"
491
+ ? {
492
+ ...projected,
493
+ content: summarizeToolResultContent(stableMessage.content, policy.summaryMaxChars),
494
+ }
495
+ : projected,
496
+ ],
497
+ };
498
+ }
499
+
500
+ export function classifyCuratedImportMessage(
501
+ message: Record<string, unknown>,
502
+ config: ResolvedConfig,
503
+ strictState: StrictImportNoiseState = createStrictImportNoiseState(),
504
+ ): CuratedImportClassification {
505
+ const stableMessage = stableProjectionMessage(message);
506
+ const projectionDecision = projectCuratedImportMessage(stableMessage, config, strictState);
507
+ const projectedMessages = mergeImportProvenance(
508
+ stableMessage,
509
+ projectionDecision.projectedMessages,
510
+ );
511
+ const decision = projectedMessages.length ? "keep" : "drop";
512
+ const reasons =
513
+ decision === "keep"
514
+ ? [keepReason(stableMessage)]
515
+ : dropReasons(stableMessage, projectedMessages, config, projectionDecision.strictDropReason);
516
+ const rawBytes = importByteLength(stableMessage);
517
+ const projectedBytes = importByteLength(projectedMessages);
518
+ return {
519
+ original: message,
520
+ stableMessage,
521
+ projectedMessages,
522
+ decision,
523
+ reasons,
524
+ rawBytes,
525
+ projectedBytes,
526
+ ...(stableMessage.role === "toolResult" ? { toolName: importToolName(stableMessage) } : {}),
527
+ ...(stableMessage.role === "toolResult" && decision === "drop"
528
+ ? { toolResultDropped: true }
529
+ : {}),
530
+ ...(stableMessage.role === "toolResult" && stableMessage.isError === true && decision === "keep"
531
+ ? { toolErrorKept: true }
532
+ : {}),
533
+ };
534
+ }
535
+
536
+ function incrementReasonCounts(
537
+ counts: Partial<Record<CuratedImportReason, number>>,
538
+ reasons: CuratedImportReason[],
539
+ ): void {
540
+ for (const reason of reasons) counts[reason] = (counts[reason] ?? 0) + 1;
541
+ }
542
+
543
+ export function buildCuratedImportProjection(
544
+ messages: Array<{ data: Record<string, unknown> }>,
545
+ config: ResolvedConfig,
546
+ ): CuratedImportProjection {
547
+ const droppedTools = new Map<string, { count: number; bytes: number }>();
548
+ const classificationReasonCounts: Partial<Record<CuratedImportReason, number>> = {};
549
+ const strictState = createStrictImportNoiseState();
550
+ const classifications = messages.map((message) =>
551
+ classifyCuratedImportMessage(message.data, config, strictState),
552
+ );
553
+ const projected = classifications.flatMap((classification) => {
554
+ incrementReasonCounts(classificationReasonCounts, classification.reasons);
555
+ if (classification.toolResultDropped) {
556
+ const name = classification.toolName ?? "unknown";
557
+ const current = droppedTools.get(name) ?? { count: 0, bytes: 0 };
558
+ droppedTools.set(name, {
559
+ count: current.count + 1,
560
+ bytes: current.bytes + classification.rawBytes,
561
+ });
562
+ }
563
+ return classification.projectedMessages;
564
+ });
565
+ const keptToolErrors = classifications.filter((classification) => classification.toolErrorKept);
566
+ return {
567
+ messages: projected,
568
+ rawBytes: classifications.reduce((total, classification) => total + classification.rawBytes, 0),
569
+ projectedBytes: importByteLength(projected),
570
+ droppedToolResultCount: [...droppedTools.values()].reduce(
571
+ (total, tool) => total + tool.count,
572
+ 0,
573
+ ),
574
+ droppedToolResultBytes: [...droppedTools.values()].reduce(
575
+ (total, tool) => total + tool.bytes,
576
+ 0,
577
+ ),
578
+ keptToolErrorCount: keptToolErrors.length,
579
+ keptToolErrorBytes: keptToolErrors.reduce(
580
+ (total, classification) => total + classification.rawBytes,
581
+ 0,
582
+ ),
583
+ estimatedChunkCount: Math.max(1, Math.ceil(importByteLength(projected) / 8_000)),
584
+ droppedTools: [...droppedTools]
585
+ .map(([name, value]) => ({ name, ...value }))
586
+ .sort(
587
+ (left, right) =>
588
+ right.bytes - left.bytes ||
589
+ right.count - left.count ||
590
+ left.name.localeCompare(right.name),
591
+ ),
592
+ topDroppedTools: [...droppedTools]
593
+ .map(([name, value]) => ({ name, ...value }))
594
+ .sort(
595
+ (left, right) =>
596
+ right.bytes - left.bytes ||
597
+ right.count - left.count ||
598
+ left.name.localeCompare(right.name),
599
+ )
600
+ .slice(0, 5),
601
+ classificationReasonCounts,
602
+ classifications,
603
+ };
604
+ }
605
+
606
+ export interface ImportRetainArgs {
607
+ sessionFile: string;
608
+ bankId: string;
609
+ client: HindsightLikeClient;
610
+ config: ResolvedConfig;
611
+ parsed: ParsedSession;
612
+ cwd: string;
613
+ sessionId: string;
614
+ leaves: string[];
615
+ branch: ImportBranch;
616
+ }
617
+
618
+ export interface ImportDocumentPreview {
619
+ documentId: string;
620
+ leafId: string;
621
+ messageCount: number;
622
+ rawMessageCount?: number;
623
+ projectedMessageCount?: number;
624
+ rawBytes?: number;
625
+ projectedBytes?: number;
626
+ droppedToolResultCount?: number;
627
+ droppedToolResultBytes?: number;
628
+ droppedTools?: Array<{ name: string; count: number; bytes: number }>;
629
+ topDroppedTools?: Array<{ name: string; count: number; bytes: number }>;
630
+ keptToolErrorCount?: number;
631
+ keptToolErrorBytes?: number;
632
+ classificationReasonCounts?: Record<string, number>;
633
+ estimatedDocumentCount?: number;
634
+ estimatedChunkCount?: number;
635
+ importMode?: ResolvedConfig["import"]["mode"];
636
+ importQualityProfile?: ResolvedConfig["import"]["qualityProfile"];
637
+ projectionVersion?: string;
638
+ importProfile?: string;
639
+ chunkIndex?: number;
640
+ messageRange?: { start: number; end: number };
641
+ contentHash: string;
642
+ contentBytes: number;
643
+ tags: string[];
644
+ updateMode: "append" | "replace";
645
+ bankId: string;
646
+ wouldWrite: boolean;
647
+ status: "pending" | "queued" | "completed" | "failed" | "skipped";
648
+ skipReason?: "already-imported" | "empty-curated-projection";
649
+ error?: string;
650
+ }
651
+
652
+ export interface ImportRetainResult {
653
+ document: ImportDocumentPreview;
654
+ manifestEntry: ImportManifestEntry;
655
+ }
656
+
657
+ export class ImportRetainQueuedError extends Error {
658
+ readonly code = "IMPORT_RETAIN_QUEUED";
659
+
660
+ constructor(message: string) {
661
+ super(message);
662
+ this.name = "ImportRetainQueuedError";
663
+ }
664
+ }
665
+
666
+ export function isImportRetainQueuedError(error: unknown): error is ImportRetainQueuedError {
667
+ return error instanceof ImportRetainQueuedError;
668
+ }
669
+
670
+ interface ImportBranchBuildResult extends ImportRetainResult {
671
+ content: string;
672
+ context: string;
673
+ observationScopes: string[][];
674
+ }
675
+
676
+ function resolvedParentSessionId(parsed: ParsedSession, cwd: string): string | undefined {
677
+ return (
678
+ parsed.parentSessionId ??
679
+ (parsed.parentSessionFile ? stableSessionId(parsed.parentSessionFile, cwd) : undefined)
680
+ );
681
+ }
682
+
683
+ const CURATED_PROJECTION_VERSION = "curated-turns-v1";
684
+
685
+ interface ImportChunk {
686
+ index: number;
687
+ messages: Array<{ message: ImportBranch["messages"][number]; originalIndex: number }>;
688
+ start: number;
689
+ end: number;
690
+ }
691
+
692
+ function chunkCuratedMessages(
693
+ messages: ImportBranch["messages"],
694
+ config: ResolvedConfig,
695
+ ): ImportChunk[] {
696
+ const indexed = messages.map((message, originalIndex) => ({ message, originalIndex }));
697
+ const turns: ImportChunk[] = [];
698
+ let current: ImportChunk["messages"] = [];
699
+ for (const entry of indexed) {
700
+ if (entry.message.data.role === "user" && current.length) {
701
+ turns.push({
702
+ index: turns.length,
703
+ messages: current,
704
+ start: current[0]!.originalIndex,
705
+ end: current.at(-1)!.originalIndex,
706
+ });
707
+ current = [];
708
+ }
709
+ current.push(entry);
710
+ }
711
+ if (current.length)
712
+ turns.push({
713
+ index: turns.length,
714
+ messages: current,
715
+ start: current[0]!.originalIndex,
716
+ end: current.at(-1)!.originalIndex,
717
+ });
718
+ const chunks: ImportChunk[] = [];
719
+ let chunk: ImportChunk["messages"] = [];
720
+ let turnCount = 0;
721
+ const flush = () => {
722
+ if (!chunk.length) return;
723
+ chunks.push({
724
+ index: chunks.length,
725
+ messages: chunk,
726
+ start: chunk[0]!.originalIndex,
727
+ end: chunk.at(-1)!.originalIndex,
728
+ });
729
+ chunk = [];
730
+ turnCount = 0;
731
+ };
732
+ for (const turn of turns) {
733
+ const nextMessages = [...chunk, ...turn.messages];
734
+ const wouldOverflowTurns = turnCount >= config.import.turnsPerDocument;
735
+ const wouldOverflowBytes =
736
+ chunk.length > 0 &&
737
+ importByteLength(nextMessages.map((entry) => entry.message.data)) >
738
+ config.import.maxDocumentBytes;
739
+ if (wouldOverflowTurns || wouldOverflowBytes) flush();
740
+ chunk.push(...turn.messages);
741
+ turnCount += 1;
742
+ }
743
+ flush();
744
+ return chunks.length ? chunks : [{ index: 0, messages: [], start: 0, end: 0 }];
745
+ }
746
+
747
+ function curatedImportProfile(config: ResolvedConfig): string {
748
+ return `turns-${config.import.turnsPerDocument}-bytes-${config.import.maxDocumentBytes}`;
749
+ }
750
+
751
+ function shouldSurfaceImportQualityProfile(config: ResolvedConfig): boolean {
752
+ return config.import.mode === "curated" && config.import.qualityProfile !== "compatible";
753
+ }
754
+
755
+ function importChunkDocumentId(args: {
756
+ sessionId: string;
757
+ leafId: string;
758
+ mode: ResolvedConfig["import"]["mode"];
759
+ profile?: string;
760
+ chunk?: ImportChunk;
761
+ }): string {
762
+ if (args.mode !== "curated" || !args.chunk || !args.profile)
763
+ return importDocumentId(args.sessionId, args.leafId);
764
+ return `${importDocumentId(args.sessionId, args.leafId)}:${args.profile}:${CURATED_PROJECTION_VERSION}:chunk-${args.chunk.index}-${args.chunk.start}-${args.chunk.end}`;
765
+ }
766
+
767
+ function buildImportBranch(args: Omit<ImportRetainArgs, "client">): ImportBranchBuildResult[] {
768
+ const leafId = args.branch.leafId;
769
+ const parentSessionId = resolvedParentSessionId(args.parsed, args.cwd);
770
+ const mode = args.config.import.mode;
771
+ const branchMessages =
772
+ mode === "forensic"
773
+ ? args.branch.messages
774
+ : args.branch.messages.filter((message) => !isInjectedHindsightMemory(message.data));
775
+ const chunks =
776
+ mode === "curated"
777
+ ? chunkCuratedMessages(branchMessages, args.config)
778
+ : [
779
+ {
780
+ index: 0,
781
+ messages: branchMessages.map((message, originalIndex) => ({ message, originalIndex })),
782
+ start: 0,
783
+ end: Math.max(0, branchMessages.length - 1),
784
+ },
785
+ ];
786
+ const updateMode = args.config.import.replaceExistingImportedDocs ? "replace" : "append";
787
+ const identity = createMemoryIdentity(args.cwd, args.config, args.sessionFile);
788
+ const observationScopes = args.config.observations.enabled
789
+ ? expandObservationScopes(args.config.observations.scopes, {
790
+ ...identity,
791
+ sessionId: args.sessionId,
792
+ projectBankId: args.bankId,
793
+ })
794
+ : [];
795
+
796
+ return chunks.map((chunk) => {
797
+ const chunkMessages = chunk.messages.map((entry) => entry.message);
798
+ const projection =
799
+ mode === "curated"
800
+ ? buildCuratedImportProjection(chunkMessages, args.config)
801
+ : {
802
+ messages: chunkMessages.map((message) => stableProjectionMessage(message.data)),
803
+ rawBytes: chunkMessages.reduce(
804
+ (total, message) => total + importByteLength(message.data),
805
+ 0,
806
+ ),
807
+ projectedBytes: importByteLength(chunkMessages.map((message) => message.data)),
808
+ droppedToolResultCount: 0,
809
+ droppedToolResultBytes: 0,
810
+ droppedTools: [],
811
+ topDroppedTools: [],
812
+ keptToolErrorCount: chunkMessages.filter(
813
+ (message) => message.data.role === "toolResult" && message.data.isError === true,
814
+ ).length,
815
+ keptToolErrorBytes: chunkMessages
816
+ .filter(
817
+ (message) => message.data.role === "toolResult" && message.data.isError === true,
818
+ )
819
+ .reduce((total, message) => total + importByteLength(message.data), 0),
820
+ estimatedChunkCount: Math.max(
821
+ 1,
822
+ Math.ceil(importByteLength(chunkMessages.map((message) => message.data)) / 8_000),
823
+ ),
824
+ };
825
+ const importProfile = mode === "curated" ? curatedImportProfile(args.config) : undefined;
826
+ const documentId = importChunkDocumentId({
827
+ sessionId: args.sessionId,
828
+ leafId,
829
+ mode,
830
+ ...(importProfile ? { profile: importProfile } : {}),
831
+ chunk,
832
+ });
833
+ const projectionVersion = mode === "curated" ? CURATED_PROJECTION_VERSION : undefined;
834
+ const messageRange = { start: chunk.start, end: chunk.end };
835
+ const contentRaw = JSON.stringify(
836
+ {
837
+ source: "pi-session-import",
838
+ sessionFile: args.sessionFile,
839
+ cwd: args.cwd,
840
+ sessionId: args.sessionId,
841
+ ...(parentSessionId ? { parentSessionId } : {}),
842
+ ...(args.parsed.parentSessionFile
843
+ ? { parentSessionFile: args.parsed.parentSessionFile }
844
+ : {}),
845
+ branchLeafId: leafId,
846
+ projection:
847
+ mode === "curated"
848
+ ? projectionVersion
849
+ : mode === "forensic"
850
+ ? "forensic-raw-v1"
851
+ : "raw-v1",
852
+ ...(shouldSurfaceImportQualityProfile(args.config)
853
+ ? { importQualityProfile: args.config.import.qualityProfile }
854
+ : {}),
855
+ ...(mode === "curated" ? { chunkIndex: chunk.index, messageRange } : {}),
856
+ projectedMessageCount: projection.messages.length,
857
+ messages:
858
+ mode === "curated" ? projection.messages : chunkMessages.map((message) => message.data),
859
+ },
860
+ null,
861
+ 2,
862
+ );
863
+ const content = args.config.retain.redactSecrets ? redactSecrets(contentRaw) : contentRaw;
864
+ const contentHash = hashImportContent(content);
865
+ const tags = [
866
+ ...baseTags(args.cwd, args.sessionId, leafId, args.config),
867
+ "import:historical",
868
+ "imported:true",
869
+ `document:${documentId}`,
870
+ ];
871
+ if (parentSessionId) tags.push(`parent:${parentSessionId}`);
872
+ if (args.leaves.length > 1) tags.push("forked:true");
873
+ const document: ImportDocumentPreview = {
874
+ documentId,
875
+ leafId,
876
+ messageCount: chunkMessages.length,
877
+ rawMessageCount: chunkMessages.length,
878
+ projectedMessageCount: projection.messages.length,
879
+ rawBytes: projection.rawBytes,
880
+ projectedBytes: projection.projectedBytes,
881
+ droppedToolResultCount: projection.droppedToolResultCount,
882
+ droppedToolResultBytes: projection.droppedToolResultBytes,
883
+ ...("droppedTools" in projection ? { droppedTools: projection.droppedTools } : {}),
884
+ topDroppedTools: projection.topDroppedTools,
885
+ keptToolErrorCount: projection.keptToolErrorCount,
886
+ keptToolErrorBytes: projection.keptToolErrorBytes,
887
+ ...("classificationReasonCounts" in projection
888
+ ? { classificationReasonCounts: projection.classificationReasonCounts }
889
+ : {}),
890
+ estimatedDocumentCount: chunks.length,
891
+ estimatedChunkCount: projection.estimatedChunkCount,
892
+ importMode: mode,
893
+ ...(shouldSurfaceImportQualityProfile(args.config)
894
+ ? { importQualityProfile: args.config.import.qualityProfile }
895
+ : {}),
896
+ ...(projectionVersion ? { projectionVersion } : {}),
897
+ ...(importProfile ? { importProfile } : {}),
898
+ ...(mode === "curated" ? { chunkIndex: chunk.index, messageRange } : {}),
899
+ contentHash,
900
+ contentBytes: Buffer.byteLength(content, "utf8"),
901
+ tags,
902
+ updateMode,
903
+ bankId: args.bankId,
904
+ wouldWrite: !(mode === "curated" && projection.messages.length === 0),
905
+ status:
906
+ mode === "curated" && projection.messages.length === 0
907
+ ? ("skipped" as const)
908
+ : ("pending" as const),
909
+ ...(mode === "curated" && projection.messages.length === 0
910
+ ? { skipReason: "empty-curated-projection" as const }
911
+ : {}),
912
+ };
913
+ const manifestEntry: ImportManifestEntry = {
914
+ documentId,
915
+ bankId: args.bankId,
916
+ sourceFile: args.sessionFile,
917
+ importedAt: new Date().toISOString(),
918
+ contentHash,
919
+ messageCount: chunkMessages.length,
920
+ leafId,
921
+ sessionId: args.sessionId,
922
+ cwd: args.cwd,
923
+ includeBranches: args.config.import.includeBranches,
924
+ importMode: mode,
925
+ toolResults: args.config.import.toolResults,
926
+ ...(shouldSurfaceImportQualityProfile(args.config)
927
+ ? { importQualityProfile: args.config.import.qualityProfile }
928
+ : {}),
929
+ ...(projectionVersion ? { projectionVersion } : {}),
930
+ ...(importProfile ? { importProfile } : {}),
931
+ ...(mode === "curated" ? { chunkIndex: chunk.index, messageRange } : {}),
932
+ updateMode,
933
+ };
934
+ return {
935
+ document,
936
+ manifestEntry,
937
+ content,
938
+ context: `Historical Pi session import from ${args.sessionFile}, branch ${leafId}, chunk ${chunk.index}`,
939
+ observationScopes,
940
+ };
941
+ });
942
+ }
943
+
944
+ export function previewImportBranch(args: Omit<ImportRetainArgs, "client">): ImportRetainResult[] {
945
+ return buildImportBranch(args).map((built) => ({
946
+ document: { ...built.document, wouldWrite: false },
947
+ manifestEntry: built.manifestEntry,
948
+ }));
949
+ }
950
+
951
+ export async function retainImportBranch(
952
+ args: ImportRetainArgs & { documentId: string },
953
+ ): Promise<ImportRetainResult> {
954
+ const built = buildImportBranch(args).find(
955
+ (candidate) => candidate.document.documentId === args.documentId,
956
+ );
957
+ if (!built)
958
+ throw new Error(`Import document ${args.documentId} no longer exists in import plan.`);
959
+ const parentSessionId = resolvedParentSessionId(args.parsed, args.cwd);
960
+ const retainResult = await deliverImportRetain({
961
+ cwd: args.cwd,
962
+ config: args.config,
963
+ client: args.client,
964
+ bankId: args.bankId,
965
+ content: built.content,
966
+ context: built.context,
967
+ documentId: built.document.documentId,
968
+ updateMode: built.document.updateMode,
969
+ tags: built.document.tags,
970
+ metadata: {
971
+ pi_session_file: args.sessionFile,
972
+ imported: "true",
973
+ cwd: args.cwd,
974
+ session_id: args.sessionId,
975
+ ...(parentSessionId ? { parent_session_id: parentSessionId } : {}),
976
+ ...(args.parsed.parentSessionFile
977
+ ? { parent_session_file: args.parsed.parentSessionFile }
978
+ : {}),
979
+ branch_leaf_id: built.document.leafId,
980
+ import_mode: args.config.import.mode,
981
+ ...(shouldSurfaceImportQualityProfile(args.config)
982
+ ? { import_quality_profile: args.config.import.qualityProfile }
983
+ : {}),
984
+ ...(built.document.projectionVersion
985
+ ? { projection_version: built.document.projectionVersion }
986
+ : {}),
987
+ ...(built.document.importProfile ? { import_profile: built.document.importProfile } : {}),
988
+ ...(built.document.chunkIndex !== undefined
989
+ ? { chunk_index: String(built.document.chunkIndex) }
990
+ : {}),
991
+ ...(built.document.messageRange
992
+ ? {
993
+ message_range_start: String(built.document.messageRange.start),
994
+ message_range_end: String(built.document.messageRange.end),
995
+ }
996
+ : {}),
997
+ content_hash: built.document.contentHash,
998
+ include_branches: args.config.import.includeBranches,
999
+ tool_results: args.config.import.toolResults,
1000
+ ...(args.parsed.sessionTimestamp ? { session_timestamp: args.parsed.sessionTimestamp } : {}),
1001
+ },
1002
+ ...(built.observationScopes.length ? { observationScopes: built.observationScopes } : {}),
1003
+ });
1004
+ if (!retainResult.delivered) {
1005
+ throw new ImportRetainQueuedError(
1006
+ `Hindsight import retain queued as ${retainResult.queueJobId}; ${retainResult.remaining} retain job${retainResult.remaining === 1 ? "" : "s"} pending`,
1007
+ );
1008
+ }
1009
+ return { document: built.document, manifestEntry: built.manifestEntry };
1010
+ }