@herbertgao/pi-extensions 2026.9.6 → 2026.9.7

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 (240) hide show
  1. package/README.md +3 -8
  2. package/THIRD_PARTY_NOTICES.md +0 -4
  3. package/node_modules/@narumitw/pi-btw/README.md +2 -0
  4. package/node_modules/@narumitw/pi-btw/dist/index.ts +284 -365
  5. package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
  6. package/node_modules/@narumitw/pi-btw/package.json +52 -53
  7. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +494 -547
  8. package/node_modules/@narumitw/pi-btw/src/btw.ts +814 -856
  9. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +673 -674
  10. package/node_modules/@narumitw/pi-btw/src/keybindings.ts +228 -270
  11. package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +309 -318
  12. package/node_modules/@narumitw/pi-btw/src/menu.ts +416 -454
  13. package/node_modules/@narumitw/pi-btw/src/settings.ts +203 -219
  14. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +173 -201
  15. package/node_modules/@narumitw/pi-btw/src/text.ts +21 -23
  16. package/node_modules/@narumitw/pi-btw/src/transcript-markdown.ts +65 -0
  17. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +611 -662
  18. package/node_modules/pi-lens/CHANGELOG.md +239 -0
  19. package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -0
  20. package/node_modules/pi-lens/dist/clients/actionable-warnings.js +30 -4
  21. package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +2 -0
  22. package/node_modules/pi-lens/dist/clients/bash-file-access.js +26 -0
  23. package/node_modules/pi-lens/dist/clients/biome-client.js +1 -1
  24. package/node_modules/pi-lens/dist/clients/blocker-freshness.js +403 -35
  25. package/node_modules/pi-lens/dist/clients/bootstrap.js +6 -1
  26. package/node_modules/pi-lens/dist/clients/cascade-format.js +200 -4
  27. package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +10 -2
  28. package/node_modules/pi-lens/dist/clients/degradation-ledger.js +16 -1
  29. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +12 -1
  30. package/node_modules/pi-lens/dist/clients/dispatch/ast-grep-catalog.js +65 -0
  31. package/node_modules/pi-lens/dist/clients/dispatch/auxiliary-lsp.js +28 -0
  32. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +9 -2
  33. package/node_modules/pi-lens/dist/clients/dispatch/finding-policy.js +222 -0
  34. package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +389 -13
  35. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +175 -25
  36. package/node_modules/pi-lens/dist/clients/dispatch/rule-ignores.js +62 -0
  37. package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +21 -41
  38. package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +6 -1
  39. package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +7 -7
  40. package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -12
  41. package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +2 -21
  42. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +1 -1
  43. package/node_modules/pi-lens/dist/clients/dispatch/types.js +4 -1
  44. package/node_modules/pi-lens/dist/clients/disposition-publish.js +1 -0
  45. package/node_modules/pi-lens/dist/clients/effective-config.js +1 -1
  46. package/node_modules/pi-lens/dist/clients/file-kinds.js +26 -1
  47. package/node_modules/pi-lens/dist/clients/file-role.js +21 -21
  48. package/node_modules/pi-lens/dist/clients/file-utils.js +156 -10
  49. package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +56 -9
  50. package/node_modules/pi-lens/dist/clients/format-service.js +39 -21
  51. package/node_modules/pi-lens/dist/clients/formatters.js +60 -13
  52. package/node_modules/pi-lens/dist/clients/generation-guard.js +2 -2
  53. package/node_modules/pi-lens/dist/clients/git-guard.js +11 -3
  54. package/node_modules/pi-lens/dist/clients/indent-retarget.js +78 -8
  55. package/node_modules/pi-lens/dist/clients/installer/index.js +333 -114
  56. package/node_modules/pi-lens/dist/clients/instance-reaper-state.js +7 -0
  57. package/node_modules/pi-lens/dist/clients/instance-reaper.js +20 -10
  58. package/node_modules/pi-lens/dist/clients/language-profile.js +23 -1
  59. package/node_modules/pi-lens/dist/clients/language-registry.js +5 -1
  60. package/node_modules/pi-lens/dist/clients/latency-logger.js +4 -1
  61. package/node_modules/pi-lens/dist/clients/lens-engine.js +12 -3
  62. package/node_modules/pi-lens/dist/clients/lens-events.js +2 -1
  63. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +79 -0
  64. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +2 -1
  65. package/node_modules/pi-lens/dist/clients/lsp/client.js +25 -21
  66. package/node_modules/pi-lens/dist/clients/lsp/config.js +2 -2
  67. package/node_modules/pi-lens/dist/clients/lsp/index.js +7 -0
  68. package/node_modules/pi-lens/dist/clients/lsp/launch.js +16 -4
  69. package/node_modules/pi-lens/dist/clients/lsp/server.js +19 -2
  70. package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +2 -1
  71. package/node_modules/pi-lens/dist/clients/lsp-mutation.js +5 -3
  72. package/node_modules/pi-lens/dist/clients/map-with-concurrency.js +12 -8
  73. package/node_modules/pi-lens/dist/clients/mcp/session.js +36 -4
  74. package/node_modules/pi-lens/dist/clients/ndjson-logger.js +24 -7
  75. package/node_modules/pi-lens/dist/clients/observed-mutation.js +40 -2
  76. package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +23 -3
  77. package/node_modules/pi-lens/dist/clients/opengrep-client.js +110 -15
  78. package/node_modules/pi-lens/dist/clients/path-utils.js +123 -3
  79. package/node_modules/pi-lens/dist/clients/performance-report.js +5 -1
  80. package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +1 -1
  81. package/node_modules/pi-lens/dist/clients/pipeline.js +74 -5
  82. package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +37 -5
  83. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +145 -18
  84. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +4 -1
  85. package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +59 -3
  86. package/node_modules/pi-lens/dist/clients/project-lens-config.js +45 -2
  87. package/node_modules/pi-lens/dist/clients/read-guard.js +70 -11
  88. package/node_modules/pi-lens/dist/clients/resource-sampler.js +147 -9
  89. package/node_modules/pi-lens/dist/clients/ruff-client.js +1 -1
  90. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +33 -5
  91. package/node_modules/pi-lens/dist/clients/runtime-context.js +6 -2
  92. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +77 -7
  93. package/node_modules/pi-lens/dist/clients/runtime-session.js +21 -2
  94. package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +43 -12
  95. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +330 -85
  96. package/node_modules/pi-lens/dist/clients/runtime-turn.js +147 -17
  97. package/node_modules/pi-lens/dist/clients/safe-spawn.js +258 -5
  98. package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +6 -0
  99. package/node_modules/pi-lens/dist/clients/session-event-guard.js +50 -5
  100. package/node_modules/pi-lens/dist/clients/sg-runner.js +23 -11
  101. package/node_modules/pi-lens/dist/clients/sgconfig.js +79 -2
  102. package/node_modules/pi-lens/dist/clients/test-runner-client.js +6 -41
  103. package/node_modules/pi-lens/dist/clients/test-runner-delivery.js +43 -0
  104. package/node_modules/pi-lens/dist/clients/tool-agreement.js +423 -0
  105. package/node_modules/pi-lens/dist/clients/tool-config.js +50 -0
  106. package/node_modules/pi-lens/dist/clients/tool-cwd.js +47 -76
  107. package/node_modules/pi-lens/dist/clients/tool-policy.js +163 -7
  108. package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +120 -0
  109. package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +127 -189
  110. package/node_modules/pi-lens/dist/clients/widget-state.js +98 -0
  111. package/node_modules/pi-lens/dist/clients/word-index.js +5 -2
  112. package/node_modules/pi-lens/dist/clients/write-ordering-guard.js +7 -0
  113. package/node_modules/pi-lens/dist/index.js +27416 -24153
  114. package/node_modules/pi-lens/dist/mcp/server.js +5 -2
  115. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +7 -1
  116. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +168 -53
  117. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +244 -72
  118. package/node_modules/pi-lens/docs/audit1.md +5 -5
  119. package/node_modules/pi-lens/docs/dependencies.md +11 -0
  120. package/node_modules/pi-lens/docs/dispositions.md +52 -0
  121. package/node_modules/pi-lens/docs/environment-variables.md +2 -2
  122. package/node_modules/pi-lens/docs/features.md +4 -4
  123. package/node_modules/pi-lens/docs/globalconfig.md +3 -1
  124. package/node_modules/pi-lens/docs/language-coverage.md +1 -0
  125. package/node_modules/pi-lens/docs/pi-lens-fixer.md +53 -94
  126. package/node_modules/pi-lens/docs/pi-lens-investigator.md +43 -38
  127. package/node_modules/pi-lens/docs/pi-lens-retro.md +97 -0
  128. package/node_modules/pi-lens/docs/pi-lens-reviewer.md +53 -66
  129. package/node_modules/pi-lens/docs/release-qa-baseline.md +22 -0
  130. package/node_modules/pi-lens/docs/settings.md +7 -4
  131. package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
  132. package/node_modules/pi-lens/docs/usage.md +2 -0
  133. package/node_modules/pi-lens/package.json +1 -1
  134. package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +24 -12
  135. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +40 -0
  136. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +15 -0
  137. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +7 -0
  138. package/package.json +5 -11
  139. package/node_modules/@luxusai/pi-hindsight/CHANGELOG.md +0 -376
  140. package/node_modules/@luxusai/pi-hindsight/README.md +0 -92
  141. package/node_modules/@luxusai/pi-hindsight/docs/adr/001-memory-lifecycle-and-scope.md +0 -56
  142. package/node_modules/@luxusai/pi-hindsight/docs/adr/002-explicit-routing-strategy-seam.md +0 -169
  143. package/node_modules/@luxusai/pi-hindsight/docs/adr/003-tui-memory-mode-vocabulary.md +0 -107
  144. package/node_modules/@luxusai/pi-hindsight/docs/adr/004-lifeos-dual-bank-design.md +0 -89
  145. package/node_modules/@luxusai/pi-hindsight/docs/adr/005-domain-banks-and-agent-first-surface.md +0 -189
  146. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark.webp +0 -0
  147. package/node_modules/@luxusai/pi-hindsight/docs/assets/logos/pi-hindsight-logo-dark@2x.webp +0 -0
  148. package/node_modules/@luxusai/pi-hindsight/docs/coding-memory-evaluation.md +0 -54
  149. package/node_modules/@luxusai/pi-hindsight/docs/compatibility.md +0 -75
  150. package/node_modules/@luxusai/pi-hindsight/docs/hindsight-core-functions.md +0 -300
  151. package/node_modules/@luxusai/pi-hindsight/docs/mission-and-mental-model-quality.md +0 -158
  152. package/node_modules/@luxusai/pi-hindsight/docs/next-opt-out-design.md +0 -164
  153. package/node_modules/@luxusai/pi-hindsight/docs/risky-memory-modes.md +0 -139
  154. package/node_modules/@luxusai/pi-hindsight/docs/starter-mental-model-suggestions.md +0 -74
  155. package/node_modules/@luxusai/pi-hindsight/docs/surface-reference.md +0 -261
  156. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-operations.ts +0 -151
  157. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-selection.ts +0 -18
  158. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-settings-presenter.ts +0 -46
  159. package/node_modules/@luxusai/pi-hindsight/extensions/banks/bank-templates.ts +0 -383
  160. package/node_modules/@luxusai/pi-hindsight/extensions/banks/banking.ts +0 -240
  161. package/node_modules/@luxusai/pi-hindsight/extensions/banks/knowledge-page-seed.ts +0 -176
  162. package/node_modules/@luxusai/pi-hindsight/extensions/banks/retain-strategies.ts +0 -178
  163. package/node_modules/@luxusai/pi-hindsight/extensions/client/client-retry.ts +0 -41
  164. package/node_modules/@luxusai/pi-hindsight/extensions/client/client.ts +0 -386
  165. package/node_modules/@luxusai/pi-hindsight/extensions/client/fetch-compat.ts +0 -39
  166. package/node_modules/@luxusai/pi-hindsight/extensions/client/timeout.ts +0 -34
  167. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-defaults.ts +0 -140
  168. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-model.ts +0 -61
  169. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-editing-registry.ts +0 -925
  170. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-field-paths.ts +0 -247
  171. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-normalize.ts +0 -547
  172. package/node_modules/@luxusai/pi-hindsight/extensions/config/config-writer.ts +0 -480
  173. package/node_modules/@luxusai/pi-hindsight/extensions/config/config.ts +0 -182
  174. package/node_modules/@luxusai/pi-hindsight/extensions/config/setup-gate.ts +0 -86
  175. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-execute.ts +0 -1010
  176. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-parse.ts +0 -175
  177. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-plan.ts +0 -425
  178. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-presentation.ts +0 -210
  179. package/node_modules/@luxusai/pi-hindsight/extensions/imports/import-sessions.ts +0 -817
  180. package/node_modules/@luxusai/pi-hindsight/extensions/index.ts +0 -25
  181. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/git-seed.ts +0 -319
  182. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-recall.ts +0 -196
  183. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-retain.ts +0 -189
  184. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle-runtime.ts +0 -65
  185. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/memory-lifecycle.ts +0 -260
  186. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/mental-models.ts +0 -244
  187. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/observation-scopes.ts +0 -59
  188. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-cleanup.ts +0 -82
  189. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall-visibility.ts +0 -45
  190. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/recall.ts +0 -331
  191. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-cursor.ts +0 -354
  192. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-job-builder.ts +0 -66
  193. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain-receipts.ts +0 -132
  194. package/node_modules/@luxusai/pi-hindsight/extensions/lifecycle/retain.ts +0 -228
  195. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-bank-template-operations.ts +0 -74
  196. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-config-operations.ts +0 -16
  197. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-control-operations.ts +0 -648
  198. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-diagnostics-operations.ts +0 -105
  199. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-identity.ts +0 -68
  200. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-service.ts +0 -71
  201. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-operation-types.ts +0 -13
  202. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-recall-operations.ts +0 -162
  203. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-retain-operations.ts +0 -131
  204. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-scope.ts +0 -104
  205. package/node_modules/@luxusai/pi-hindsight/extensions/operations/memory-session-operations.ts +0 -37
  206. package/node_modules/@luxusai/pi-hindsight/extensions/operations/operation-catalog.ts +0 -893
  207. package/node_modules/@luxusai/pi-hindsight/extensions/operations/reflect-presenter.ts +0 -35
  208. package/node_modules/@luxusai/pi-hindsight/extensions/operations/scope-migrate.ts +0 -177
  209. package/node_modules/@luxusai/pi-hindsight/extensions/operations/tools.ts +0 -7
  210. package/node_modules/@luxusai/pi-hindsight/extensions/queue/flush-presenter.ts +0 -21
  211. package/node_modules/@luxusai/pi-hindsight/extensions/queue/jsonl-queue-store.ts +0 -112
  212. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-delivery.ts +0 -111
  213. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-lock.ts +0 -214
  214. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue-operations.ts +0 -70
  215. package/node_modules/@luxusai/pi-hindsight/extensions/queue/queue.ts +0 -392
  216. package/node_modules/@luxusai/pi-hindsight/extensions/tui/bank-template-presentation.ts +0 -45
  217. package/node_modules/@luxusai/pi-hindsight/extensions/tui/commands.ts +0 -9
  218. package/node_modules/@luxusai/pi-hindsight/extensions/tui/guided-setup.ts +0 -860
  219. package/node_modules/@luxusai/pi-hindsight/extensions/tui/prefill-input.ts +0 -105
  220. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-flow.ts +0 -189
  221. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-server-probe.ts +0 -299
  222. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-actions.ts +0 -201
  223. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-facts.ts +0 -47
  224. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-render.ts +0 -239
  225. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui-types.ts +0 -50
  226. package/node_modules/@luxusai/pi-hindsight/extensions/tui/setup-tui.ts +0 -259
  227. package/node_modules/@luxusai/pi-hindsight/extensions/tui/tool-presenters.ts +0 -77
  228. package/node_modules/@luxusai/pi-hindsight/extensions/types.ts +0 -534
  229. package/node_modules/@luxusai/pi-hindsight/extensions/utils/diagnostics.ts +0 -319
  230. package/node_modules/@luxusai/pi-hindsight/extensions/utils/messages.ts +0 -325
  231. package/node_modules/@luxusai/pi-hindsight/extensions/utils/sanitize.ts +0 -42
  232. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-memory-meta.ts +0 -244
  233. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session-operations.ts +0 -56
  234. package/node_modules/@luxusai/pi-hindsight/extensions/utils/session.ts +0 -50
  235. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-fields.ts +0 -167
  236. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status-health.ts +0 -207
  237. package/node_modules/@luxusai/pi-hindsight/extensions/utils/status.ts +0 -120
  238. package/node_modules/@luxusai/pi-hindsight/extensions/version.ts +0 -8
  239. package/node_modules/@luxusai/pi-hindsight/package.json +0 -118
  240. package/node_modules/@luxusai/pi-hindsight/skills/hindsight-memory-doctor/SKILL.md +0 -75
@@ -1,817 +0,0 @@
1
- import type { HindsightLikeClient, ResolvedConfig } from "../types.js";
2
- import { basename, dirname, extname, join, resolve } from "node:path";
3
- import {
4
- buildImportPlan,
5
- createImportCheckpoint,
6
- hashImportContent,
7
- type ImportCheckpoint,
8
- type ImportCheckpointDocument,
9
- type ImportDocumentStatus,
10
- type ImportPlan,
11
- readImportCheckpointSafe,
12
- readImportManifestSafe,
13
- upsertImportManifestEntries,
14
- writeImportCheckpoint,
15
- } from "./import-plan.js";
16
- import { createHash } from "node:crypto";
17
- import {
18
- deliverImportRetain,
19
- type ImportDocumentPreview,
20
- importRetainJobMatchesIdentity,
21
- ImportRetainQueuedError,
22
- type ImportRetainResult,
23
- isImportRetainQueuedError,
24
- previewImportBranch,
25
- retainImportBranch,
26
- } from "./import-execute.js";
27
- import { importDocumentId } from "../utils/session.js";
28
- import { readdir, readFile, stat } from "node:fs/promises";
29
- import { redactError, redactSecrets } from "../utils/sanitize.js";
30
- import { removeQueuedRetains } from "../queue/queue.js";
31
- import { resolveOperationBank } from "../banks/bank-selection.js";
32
- import { type ParsedSession, parseImportSessionJsonl } from "./import-parse.js";
33
- export { parseImportSessionJsonl, parsePiSessionJsonl } from "./import-parse.js";
34
- export { selectImportBranches } from "./import-parse.js";
35
- export type { ImportBranch } from "./import-parse.js";
36
- export type { ParsedMessage, ParsedSession } from "./import-parse.js";
37
-
38
- export interface ImportSessionDocumentResult extends ImportDocumentPreview {}
39
-
40
- export interface ImportExecutionResult {
41
- documents: ImportSessionDocumentResult[];
42
- messageCount: number;
43
- retained: boolean;
44
- }
45
-
46
- function checkpointDocument(args: {
47
- document: ImportDocumentPreview;
48
- status: ImportDocumentStatus;
49
- toolResults: ImportPlan["importConfig"]["import"]["toolResults"];
50
- updatedAt: string;
51
- error?: string;
52
- }): ImportCheckpointDocument {
53
- return {
54
- documentId: args.document.documentId,
55
- leafId: args.document.leafId,
56
- contentHash: args.document.contentHash,
57
- messageCount: args.document.messageCount,
58
- ...(args.document.importMode ? { importMode: args.document.importMode } : {}),
59
- toolResults: args.toolResults,
60
- ...(args.document.importQualityProfile
61
- ? { importQualityProfile: args.document.importQualityProfile }
62
- : {}),
63
- ...(args.document.projectionVersion
64
- ? { projectionVersion: args.document.projectionVersion }
65
- : {}),
66
- ...(args.document.importProfile ? { importProfile: args.document.importProfile } : {}),
67
- ...(args.document.chunkIndex !== undefined ? { chunkIndex: args.document.chunkIndex } : {}),
68
- ...(args.document.messageRange ? { messageRange: args.document.messageRange } : {}),
69
- status: args.status,
70
- ...(args.document.skipReason ? { skipReason: args.document.skipReason } : {}),
71
- updatedAt: args.updatedAt,
72
- ...(args.error ? { error: args.error } : {}),
73
- };
74
- }
75
-
76
- export async function executeImportPlan(args: {
77
- client: HindsightLikeClient;
78
- parsed: ParsedSession;
79
- plan: ImportPlan;
80
- dryRun?: boolean;
81
- onProgress?: ImportProgressReporter;
82
- }): Promise<ImportExecutionResult> {
83
- const {
84
- sessionFile,
85
- bankId,
86
- cwd,
87
- sessionId,
88
- leaves,
89
- includeBranches,
90
- branches,
91
- manifestPath,
92
- checkpointPath,
93
- updateMode,
94
- runId,
95
- importConfig,
96
- } = args.plan;
97
- const now = new Date().toISOString();
98
- const existingCheckpoint = importConfig.import.resume
99
- ? (await readImportCheckpointSafe(checkpointPath)).checkpoint
100
- : undefined;
101
- let checkpoint: ImportCheckpoint =
102
- existingCheckpoint?.runId === runId
103
- ? existingCheckpoint
104
- : createImportCheckpoint({
105
- runId,
106
- sourceFile: sessionFile,
107
- bankId,
108
- sessionId,
109
- cwd,
110
- includeBranches,
111
- importMode: importConfig.import.mode,
112
- toolResults: importConfig.import.toolResults,
113
- importQualityProfile: importConfig.import.qualityProfile,
114
- updateMode,
115
- now,
116
- });
117
- checkpoint = {
118
- ...checkpoint,
119
- updatedAt: now,
120
- toolResults: importConfig.import.toolResults,
121
- importQualityProfile: importConfig.import.qualityProfile,
122
- };
123
-
124
- const results = [];
125
- for (const branch of branches) {
126
- const common = {
127
- sessionFile,
128
- bankId,
129
- config: importConfig,
130
- parsed: args.parsed,
131
- cwd,
132
- sessionId,
133
- leaves,
134
- branch,
135
- };
136
- args.onProgress?.({
137
- phase: "previewing",
138
- message: `Projecting branch ${branch.leafId}`,
139
- sessionFile,
140
- });
141
- const previews = previewImportBranch(common);
142
- for (const [index, preview] of previews.entries()) {
143
- args.onProgress?.({
144
- phase: args.dryRun ? "previewing" : "retaining",
145
- message: `${args.dryRun ? "Previewing" : "Importing"} document ${index + 1}/${previews.length} for branch ${branch.leafId}`,
146
- sessionFile,
147
- current: index + 1,
148
- total: previews.length,
149
- });
150
- const previous = checkpoint.documents[preview.document.documentId];
151
- const canSkip =
152
- !args.dryRun &&
153
- importConfig.import.resume &&
154
- previous?.status === "completed" &&
155
- previous.contentHash === preview.document.contentHash;
156
- if (args.dryRun || canSkip) {
157
- if (canSkip && previous)
158
- await removeQueuedRetains(cwd, importConfig, (job) =>
159
- importRetainJobMatchesIdentity(job, {
160
- bankId,
161
- documentId: preview.document.documentId,
162
- updateMode,
163
- sourceFile: sessionFile,
164
- cwd,
165
- sessionId,
166
- leafId: previous.leafId,
167
- includeBranches,
168
- ...(previous.importMode ? { importMode: previous.importMode } : {}),
169
- ...(previous.toolResults ? { toolResults: previous.toolResults } : {}),
170
- ...(previous.importQualityProfile
171
- ? { importQualityProfile: previous.importQualityProfile }
172
- : {}),
173
- ...(previous.projectionVersion
174
- ? { projectionVersion: previous.projectionVersion }
175
- : {}),
176
- ...(previous.importProfile ? { importProfile: previous.importProfile } : {}),
177
- ...(previous.chunkIndex !== undefined ? { chunkIndex: previous.chunkIndex } : {}),
178
- ...(previous.messageRange ? { messageRange: previous.messageRange } : {}),
179
- contentHash: previous.contentHash,
180
- }),
181
- );
182
- results.push({
183
- ...preview,
184
- document: {
185
- ...preview.document,
186
- wouldWrite: false,
187
- status: canSkip ? ("skipped" as const) : preview.document.status,
188
- ...(canSkip ? { skipReason: "already-imported" as const } : {}),
189
- },
190
- });
191
- continue;
192
- }
193
-
194
- if (preview.document.status === "skipped") {
195
- const skippedAt = new Date().toISOString();
196
- checkpoint.documents[preview.document.documentId] = checkpointDocument({
197
- document: preview.document,
198
- status: "skipped",
199
- toolResults: importConfig.import.toolResults,
200
- updatedAt: skippedAt,
201
- });
202
- checkpoint.updatedAt = skippedAt;
203
- await writeImportCheckpoint(checkpointPath, checkpoint);
204
- results.push({
205
- ...preview,
206
- document: { ...preview.document, wouldWrite: false, status: "skipped" as const },
207
- });
208
- continue;
209
- }
210
-
211
- checkpoint.documents[preview.document.documentId] = checkpointDocument({
212
- document: preview.document,
213
- status: "pending",
214
- toolResults: importConfig.import.toolResults,
215
- updatedAt: new Date().toISOString(),
216
- });
217
- await writeImportCheckpoint(checkpointPath, checkpoint);
218
-
219
- let completed: ImportRetainResult | undefined;
220
- try {
221
- const retained = await retainImportBranch({
222
- ...common,
223
- client: args.client,
224
- documentId: preview.document.documentId,
225
- });
226
- const completedAt = new Date().toISOString();
227
- checkpoint.documents[retained.document.documentId] = checkpointDocument({
228
- document: retained.document,
229
- status: "completed",
230
- toolResults: importConfig.import.toolResults,
231
- updatedAt: completedAt,
232
- });
233
- checkpoint.updatedAt = completedAt;
234
- await writeImportCheckpoint(checkpointPath, checkpoint);
235
- completed = retained;
236
- } catch (error) {
237
- const failedAt = new Date().toISOString();
238
- const message = redactError(error);
239
- const status = isImportRetainQueuedError(error) ? "queued" : "failed";
240
- checkpoint.documents[preview.document.documentId] = checkpointDocument({
241
- document: preview.document,
242
- status,
243
- toolResults: importConfig.import.toolResults,
244
- updatedAt: failedAt,
245
- error: message,
246
- });
247
- checkpoint.updatedAt = failedAt;
248
- await writeImportCheckpoint(checkpointPath, checkpoint);
249
- results.push({
250
- ...preview,
251
- document: { ...preview.document, status, error: message },
252
- });
253
- throw error;
254
- }
255
- if (!completed) throw new Error("Import retain completed without result.");
256
- await upsertImportManifestEntries(manifestPath, [completed.manifestEntry]);
257
- results.push({
258
- ...completed,
259
- document: { ...completed.document, status: "completed" as const },
260
- });
261
- }
262
- }
263
-
264
- const skippedResults = results.filter((result) => result.document.status === "skipped");
265
- if (!args.dryRun && skippedResults.length > 0) {
266
- const manifest = (await readImportManifestSafe(manifestPath)).manifest;
267
- const missingSkippedEntries = skippedResults
268
- .filter((result) => result.document.skipReason !== "empty-curated-projection")
269
- .filter((result) => !manifest.imports[result.document.documentId])
270
- .map((result) => result.manifestEntry);
271
- if (missingSkippedEntries.length > 0)
272
- await upsertImportManifestEntries(manifestPath, missingSkippedEntries);
273
- }
274
-
275
- const documents = results.map((result) => result.document);
276
- return {
277
- documents,
278
- messageCount: documents.reduce((count, document) => count + document.messageCount, 0),
279
- retained: !args.dryRun,
280
- };
281
- }
282
-
283
- const KEPT_CHAT_TRANSCRIPT_EVENTS = new Set(["user_message", "assistant_reply", "process_end"]);
284
-
285
- export interface ChatTranscriptEvent {
286
- type: string;
287
- timestamp?: string;
288
- content?: unknown;
289
- channel?: string;
290
- sessionId?: string;
291
- conversationId?: string;
292
- data: Record<string, unknown>;
293
- }
294
-
295
- export interface ChatTranscriptImportResult {
296
- sourceFile: string;
297
- documentId: string;
298
- retained: boolean;
299
- skipped: boolean;
300
- skipReason?: string;
301
- dryRun: boolean;
302
- bankId: string;
303
- keptEventCount: number;
304
- droppedEventCount: number;
305
- malformedLineCount: number;
306
- retainedTurnCount: number;
307
- droppedEventTypes: Array<{ type: string; count: number }>;
308
- contentHash: string;
309
- contentBytes: number;
310
- }
311
-
312
- function isRecord(value: unknown): value is Record<string, unknown> {
313
- return typeof value === "object" && value !== null && !Array.isArray(value);
314
- }
315
-
316
- function stringField(record: Record<string, unknown>, keys: string[]): string | undefined {
317
- for (const key of keys) {
318
- const value = record[key];
319
- if (typeof value === "string" && value.trim()) return value;
320
- }
321
- return undefined;
322
- }
323
-
324
- function eventType(record: Record<string, unknown>): string | undefined {
325
- return stringField(record, ["type", "event", "event_type", "kind"]);
326
- }
327
-
328
- function normalizeChatTranscriptEvent(
329
- record: Record<string, unknown>,
330
- ): ChatTranscriptEvent | undefined {
331
- const type = eventType(record);
332
- if (!type) return undefined;
333
- const content = record.content ?? record.text ?? record.message ?? record.output;
334
- const timestamp = stringField(record, ["timestamp", "created_at", "time"]);
335
- const channel = stringField(record, ["channel", "channel_id"]);
336
- const sessionId = stringField(record, ["session_id", "sessionId"]);
337
- const conversationId = stringField(record, ["conversation_id", "conversationId", "thread_id"]);
338
- return {
339
- type,
340
- ...(timestamp ? { timestamp } : {}),
341
- ...(content !== undefined ? { content } : {}),
342
- ...(channel ? { channel } : {}),
343
- ...(sessionId ? { sessionId } : {}),
344
- ...(conversationId ? { conversationId } : {}),
345
- data: record,
346
- };
347
- }
348
-
349
- export function parseChatTranscriptJsonl(text: string): {
350
- kept: ChatTranscriptEvent[];
351
- droppedEventTypes: Array<{ type: string; count: number }>;
352
- droppedEventCount: number;
353
- malformedLineCount: number;
354
- } {
355
- const kept: ChatTranscriptEvent[] = [];
356
- const dropped = new Map<string, number>();
357
- let malformedLineCount = 0;
358
- for (const line of text.split("\n")) {
359
- if (!line.trim()) continue;
360
- let parsed: unknown;
361
- try {
362
- parsed = JSON.parse(line);
363
- } catch {
364
- malformedLineCount += 1;
365
- continue;
366
- }
367
- if (!isRecord(parsed)) {
368
- malformedLineCount += 1;
369
- continue;
370
- }
371
- const event = normalizeChatTranscriptEvent(parsed);
372
- if (!event) {
373
- malformedLineCount += 1;
374
- continue;
375
- }
376
- if (KEPT_CHAT_TRANSCRIPT_EVENTS.has(event.type)) kept.push(event);
377
- else dropped.set(event.type, (dropped.get(event.type) ?? 0) + 1);
378
- }
379
- return {
380
- kept,
381
- droppedEventTypes: [...dropped]
382
- .map(([type, count]) => ({ type, count }))
383
- .sort((left, right) => right.count - left.count || left.type.localeCompare(right.type)),
384
- droppedEventCount: [...dropped.values()].reduce((total, count) => total + count, 0),
385
- malformedLineCount,
386
- };
387
- }
388
-
389
- function firstDefined(
390
- events: ChatTranscriptEvent[],
391
- key: "channel" | "sessionId" | "conversationId",
392
- ): string | undefined {
393
- return events.find((event) => event[key])?.[key];
394
- }
395
-
396
- function tagValue(value: string): string {
397
- return (
398
- value
399
- .trim()
400
- .toLowerCase()
401
- .replace(/[^a-z0-9._-]+/g, "-")
402
- .slice(0, 80) || "unknown"
403
- );
404
- }
405
-
406
- function documentId(sourceFile: string, kept: ChatTranscriptEvent[]): string {
407
- const basis = JSON.stringify({ sourceFile, first: kept[0]?.data, last: kept.at(-1)?.data });
408
- return `pi-gateway-import:${createHash("sha256").update(basis).digest("hex").slice(0, 16)}`;
409
- }
410
-
411
- export async function importChatTranscript(args: {
412
- sourceFile: string;
413
- cwd: string;
414
- bankId: string;
415
- client: HindsightLikeClient;
416
- config: ResolvedConfig;
417
- dryRun?: boolean;
418
- onProgress?: ImportProgressReporter;
419
- }): Promise<ChatTranscriptImportResult> {
420
- args.onProgress?.({
421
- phase: "reading",
422
- message: `Reading chat transcript ${args.sourceFile}`,
423
- sessionFile: args.sourceFile,
424
- });
425
- const parsed = parseChatTranscriptJsonl(await readFile(args.sourceFile, "utf8"));
426
- args.onProgress?.({
427
- phase: "planning",
428
- message: `Planning chat transcript import for ${parsed.kept.length} kept event${parsed.kept.length === 1 ? "" : "s"}`,
429
- sessionFile: args.sourceFile,
430
- });
431
- const docId = documentId(args.sourceFile, parsed.kept);
432
- const channel = firstDefined(parsed.kept, "channel");
433
- const sessionId = firstDefined(parsed.kept, "sessionId");
434
- const conversationId = firstDefined(parsed.kept, "conversationId");
435
- const contentRaw = JSON.stringify(
436
- {
437
- source: "chat-transcript-import",
438
- sourceFile: args.sourceFile,
439
- channel,
440
- sessionId,
441
- conversationId,
442
- events: parsed.kept.map((event) => event.data),
443
- droppedEventCount: parsed.droppedEventCount,
444
- droppedEventTypes: parsed.droppedEventTypes,
445
- },
446
- null,
447
- 2,
448
- );
449
- const content = args.config.retain.redactSecrets ? redactSecrets(contentRaw) : contentRaw;
450
- const contentHash = createHash("sha256").update(content).digest("hex");
451
- const result: ChatTranscriptImportResult = {
452
- sourceFile: args.sourceFile,
453
- documentId: docId,
454
- retained: false,
455
- skipped: parsed.kept.length === 0,
456
- ...(parsed.kept.length === 0
457
- ? { skipReason: "No high-signal chat transcript events found." }
458
- : {}),
459
- dryRun: Boolean(args.dryRun),
460
- bankId: args.bankId,
461
- keptEventCount: parsed.kept.length,
462
- droppedEventCount: parsed.droppedEventCount,
463
- malformedLineCount: parsed.malformedLineCount,
464
- retainedTurnCount: parsed.kept.filter((event) => event.type === "user_message").length,
465
- droppedEventTypes: parsed.droppedEventTypes,
466
- contentHash,
467
- contentBytes: Buffer.byteLength(content, "utf8"),
468
- };
469
- if (args.dryRun || parsed.kept.length === 0) return result;
470
- args.onProgress?.({
471
- phase: "retaining",
472
- message: `Importing chat transcript document ${docId}`,
473
- sessionFile: args.sourceFile,
474
- current: 1,
475
- total: 1,
476
- });
477
- const delivery = await deliverImportRetain({
478
- cwd: args.cwd,
479
- config: args.config,
480
- client: args.client,
481
- bankId: args.bankId,
482
- content,
483
- context: `Chat transcript import from ${basename(args.sourceFile)}`,
484
- documentId: docId,
485
- updateMode: "replace",
486
- tags: [
487
- "source:chat",
488
- "import:chat",
489
- "imported:true",
490
- ...(channel ? [`channel:${tagValue(channel)}`] : []),
491
- ...(sessionId ? [`session:${tagValue(sessionId)}`] : []),
492
- ...(conversationId ? [`conversation:${tagValue(conversationId)}`] : []),
493
- `document:${docId}`,
494
- ],
495
- metadata: {
496
- source_file: args.sourceFile,
497
- imported: "true",
498
- source: "chat-transcript",
499
- ...(channel ? { channel } : {}),
500
- ...(sessionId ? { session_id: sessionId } : {}),
501
- ...(conversationId ? { conversation_id: conversationId } : {}),
502
- content_hash: contentHash,
503
- },
504
- });
505
- if (!delivery.delivered) {
506
- throw new ImportRetainQueuedError(
507
- `Chat transcript import queued as ${delivery.queueJobId}; ${delivery.remaining} retain job${delivery.remaining === 1 ? "" : "s"} pending`,
508
- );
509
- }
510
- return { ...result, retained: true };
511
- }
512
-
513
- export type ImportOperationDeps = {
514
- getClient(): HindsightLikeClient;
515
- getConfig(): ResolvedConfig;
516
- getProjectBankId(): string;
517
- };
518
-
519
- export async function importMemorySession(
520
- args: {
521
- sessionFile: string;
522
- cwd?: string;
523
- bank?: string;
524
- dryRun?: boolean;
525
- includeBranches?: ResolvedConfig["import"]["includeBranches"];
526
- onProgress?: ImportProgressReporter;
527
- },
528
- deps: ImportOperationDeps,
529
- ) {
530
- const bankId = resolveOperationBank({
531
- requestedBank: args.bank,
532
- config: deps.getConfig(),
533
- projectBankId: deps.getProjectBankId(),
534
- });
535
- const result = await importPiSession({
536
- sessionFile: args.sessionFile,
537
- ...(args.cwd ? { cwd: args.cwd } : {}),
538
- bankId,
539
- client: deps.getClient(),
540
- config: deps.getConfig(),
541
- ...(args.dryRun !== undefined ? { dryRun: args.dryRun } : {}),
542
- ...(args.includeBranches ? { includeBranches: args.includeBranches } : {}),
543
- ...(args.onProgress ? { onProgress: args.onProgress } : {}),
544
- });
545
- return { bankId, ...result };
546
- }
547
-
548
- export async function importMemoryChatTranscript(
549
- args: {
550
- sourceFile: string;
551
- cwd: string;
552
- bank?: string;
553
- dryRun?: boolean;
554
- onProgress?: ImportProgressReporter;
555
- },
556
- deps: ImportOperationDeps,
557
- ) {
558
- const bankId = resolveOperationBank({
559
- requestedBank: args.bank ?? "global",
560
- config: deps.getConfig(),
561
- projectBankId: deps.getProjectBankId(),
562
- });
563
- return importChatTranscript({
564
- sourceFile: args.sourceFile,
565
- cwd: args.cwd,
566
- bankId,
567
- client: deps.getClient(),
568
- config: deps.getConfig(),
569
- ...(args.dryRun !== undefined ? { dryRun: args.dryRun } : {}),
570
- ...(args.onProgress ? { onProgress: args.onProgress } : {}),
571
- });
572
- }
573
-
574
- export async function importMemoryProjectSessions(
575
- args: {
576
- cwd: string;
577
- currentSessionFile?: string;
578
- searchDir?: string;
579
- bank?: string;
580
- dryRun?: boolean;
581
- includeBranches?: ResolvedConfig["import"]["includeBranches"];
582
- onProgress?: ImportProgressReporter;
583
- },
584
- deps: ImportOperationDeps,
585
- ) {
586
- const bankId = resolveOperationBank({
587
- requestedBank: args.bank,
588
- config: deps.getConfig(),
589
- projectBankId: deps.getProjectBankId(),
590
- });
591
- const result = await importProjectSessions({
592
- cwd: args.cwd,
593
- ...(args.currentSessionFile ? { currentSessionFile: args.currentSessionFile } : {}),
594
- ...(args.searchDir ? { searchDir: args.searchDir } : {}),
595
- bankId,
596
- client: deps.getClient(),
597
- config: deps.getConfig(),
598
- ...(args.dryRun !== undefined ? { dryRun: args.dryRun } : {}),
599
- ...(args.includeBranches ? { includeBranches: args.includeBranches } : {}),
600
- ...(args.onProgress ? { onProgress: args.onProgress } : {}),
601
- });
602
- return { bankId, ...result };
603
- }
604
-
605
- export type ImportProgressEvent = {
606
- phase:
607
- | "reading"
608
- | "planning"
609
- | "previewing"
610
- | "retaining"
611
- | "discovering"
612
- | "discovered"
613
- | "session";
614
- message: string;
615
- sessionFile?: string;
616
- current?: number;
617
- total?: number;
618
- };
619
-
620
- export type ImportProgressReporter = (event: ImportProgressEvent) => void;
621
-
622
- function sameProjectCwd(sessionCwd: string | undefined, cwd: string): boolean {
623
- if (!sessionCwd) return false;
624
- return resolve(sessionCwd) === resolve(cwd);
625
- }
626
-
627
- async function isFile(path: string): Promise<boolean> {
628
- try {
629
- return (await stat(path)).isFile();
630
- } catch {
631
- return false;
632
- }
633
- }
634
-
635
- function projectSessionCheckpointPath(basePath: string, sessionFile: string): string {
636
- return `${basePath}.${hashImportContent(sessionFile).slice(0, 12)}.json`;
637
- }
638
-
639
- export async function discoverProjectSessionFiles(args: {
640
- cwd: string;
641
- currentSessionFile?: string;
642
- searchDir?: string;
643
- }): Promise<ProjectSessionDiscoveryResult> {
644
- const searchDir =
645
- args.searchDir ?? (args.currentSessionFile ? dirname(args.currentSessionFile) : undefined);
646
- if (!searchDir) return { sessionFiles: [], scanned: 0 };
647
- const entries = await readdir(searchDir);
648
- const candidates = entries
649
- .filter((entry) => extname(entry) === ".jsonl")
650
- .map((entry) => join(searchDir, entry));
651
- const sessionFiles: string[] = [];
652
- let scanned = 0;
653
- for (const candidate of candidates) {
654
- if (!(await isFile(candidate))) continue;
655
- scanned += 1;
656
- try {
657
- const parsed = parseImportSessionJsonl(await readFile(candidate, "utf8"));
658
- if (sameProjectCwd(parsed.cwd, args.cwd)) sessionFiles.push(candidate);
659
- } catch {
660
- // Ignore unrelated or malformed JSONL files during project-scoped discovery.
661
- }
662
- }
663
- return { sessionFiles: sessionFiles.sort(), scanned };
664
- }
665
-
666
- export interface ProjectSessionDiscoveryResult {
667
- sessionFiles: string[];
668
- scanned: number;
669
- }
670
-
671
- export interface ImportProjectSessionsResult {
672
- sessionFiles: string[];
673
- scanned: number;
674
- imported: ImportSessionResult[];
675
- messageCount: number;
676
- documentCount: number;
677
- dryRun: boolean;
678
- malformedLineCount: number;
679
- }
680
-
681
- export interface ImportSessionResult {
682
- sessionFile: string;
683
- documentId: string;
684
- messageCount: number;
685
- retained: boolean;
686
- dryRun: boolean;
687
- manifestPath: string;
688
- checkpointPath: string;
689
- runId: string;
690
- malformedLineCount: number;
691
- documents: ImportSessionDocumentResult[];
692
- }
693
-
694
- export async function importPiSession(args: {
695
- sessionFile: string;
696
- cwd?: string;
697
- bankId: string;
698
- client: HindsightLikeClient;
699
- config: ResolvedConfig;
700
- dryRun?: boolean;
701
- requireMatchingCwd?: boolean;
702
- includeBranches?: ResolvedConfig["import"]["includeBranches"];
703
- onProgress?: ImportProgressReporter;
704
- }): Promise<ImportSessionResult> {
705
- args.onProgress?.({
706
- phase: "reading",
707
- message: `Reading session file ${args.sessionFile}`,
708
- sessionFile: args.sessionFile,
709
- });
710
- const text = await readFile(args.sessionFile, "utf8");
711
- const parsed = parseImportSessionJsonl(text);
712
- args.onProgress?.({
713
- phase: "planning",
714
- message: `Planning import for ${parsed.messages.length} message${parsed.messages.length === 1 ? "" : "s"}`,
715
- sessionFile: args.sessionFile,
716
- });
717
- const plan = buildImportPlan({
718
- sessionFile: args.sessionFile,
719
- parsed,
720
- bankId: args.bankId,
721
- config: args.config,
722
- ...(args.cwd ? { cwd: args.cwd } : {}),
723
- ...(args.requireMatchingCwd !== undefined
724
- ? { requireMatchingCwd: args.requireMatchingCwd }
725
- : {}),
726
- ...(args.includeBranches ? { includeBranches: args.includeBranches } : {}),
727
- });
728
- const execution = await executeImportPlan({
729
- client: args.client,
730
- parsed,
731
- plan,
732
- ...(args.dryRun !== undefined ? { dryRun: args.dryRun } : {}),
733
- ...(args.onProgress ? { onProgress: args.onProgress } : {}),
734
- });
735
-
736
- const first = execution.documents[0] ?? {
737
- documentId: importDocumentId(plan.sessionId, "root"),
738
- };
739
- return {
740
- sessionFile: args.sessionFile,
741
- documentId: first.documentId,
742
- messageCount: execution.messageCount,
743
- retained: execution.retained,
744
- dryRun: Boolean(args.dryRun),
745
- manifestPath: plan.manifestPath,
746
- checkpointPath: plan.checkpointPath,
747
- runId: plan.runId,
748
- malformedLineCount: parsed.malformedLineCount,
749
- documents: execution.documents,
750
- };
751
- }
752
-
753
- export async function importProjectSessions(args: {
754
- cwd: string;
755
- currentSessionFile?: string;
756
- searchDir?: string;
757
- bankId: string;
758
- client: HindsightLikeClient;
759
- config: ResolvedConfig;
760
- dryRun?: boolean;
761
- includeBranches?: ResolvedConfig["import"]["includeBranches"];
762
- onProgress?: ImportProgressReporter;
763
- }): Promise<ImportProjectSessionsResult> {
764
- args.onProgress?.({ phase: "discovering", message: "Scanning project session files" });
765
- const discovery = await discoverProjectSessionFiles({
766
- cwd: args.cwd,
767
- ...(args.currentSessionFile ? { currentSessionFile: args.currentSessionFile } : {}),
768
- ...(args.searchDir ? { searchDir: args.searchDir } : {}),
769
- });
770
- args.onProgress?.({
771
- phase: "discovered",
772
- message: `Found ${discovery.sessionFiles.length} project session${discovery.sessionFiles.length === 1 ? "" : "s"} after scanning ${discovery.scanned} file${discovery.scanned === 1 ? "" : "s"}`,
773
- current: discovery.sessionFiles.length,
774
- total: discovery.scanned,
775
- });
776
- const imported: ImportSessionResult[] = [];
777
- for (const [index, sessionFile] of discovery.sessionFiles.entries()) {
778
- args.onProgress?.({
779
- phase: "session",
780
- message: `Importing project session ${index + 1}/${discovery.sessionFiles.length}: ${sessionFile}`,
781
- sessionFile,
782
- current: index + 1,
783
- total: discovery.sessionFiles.length,
784
- });
785
- imported.push(
786
- await importPiSession({
787
- sessionFile,
788
- cwd: args.cwd,
789
- requireMatchingCwd: true,
790
- bankId: args.bankId,
791
- client: args.client,
792
- config: {
793
- ...args.config,
794
- import: {
795
- ...args.config.import,
796
- checkpointPath: projectSessionCheckpointPath(
797
- args.config.import.checkpointPath,
798
- sessionFile,
799
- ),
800
- },
801
- },
802
- ...(args.dryRun !== undefined ? { dryRun: args.dryRun } : {}),
803
- ...(args.includeBranches ? { includeBranches: args.includeBranches } : {}),
804
- ...(args.onProgress ? { onProgress: args.onProgress } : {}),
805
- }),
806
- );
807
- }
808
- return {
809
- sessionFiles: discovery.sessionFiles,
810
- scanned: discovery.scanned,
811
- imported,
812
- messageCount: imported.reduce((count, result) => count + result.messageCount, 0),
813
- documentCount: imported.reduce((count, result) => count + result.documents.length, 0),
814
- dryRun: Boolean(args.dryRun),
815
- malformedLineCount: imported.reduce((count, result) => count + result.malformedLineCount, 0),
816
- };
817
- }