@ouro.bot/cli 0.1.0-alpha.35 → 0.1.0-alpha.350

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 (324) hide show
  1. package/README.md +188 -187
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
  4. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  5. package/changelog.json +2088 -0
  6. package/dist/arc/attention-types.js +8 -0
  7. package/dist/arc/cares.js +140 -0
  8. package/dist/arc/episodes.js +117 -0
  9. package/dist/arc/intentions.js +133 -0
  10. package/dist/arc/json-store.js +117 -0
  11. package/dist/arc/obligations.js +237 -0
  12. package/dist/arc/packets.js +193 -0
  13. package/dist/arc/presence.js +185 -0
  14. package/dist/arc/task-lifecycle.js +65 -0
  15. package/dist/heart/active-work.js +832 -0
  16. package/dist/heart/agent-entry.js +37 -2
  17. package/dist/heart/attachments/image-normalize.js +194 -0
  18. package/dist/heart/attachments/materialize.js +97 -0
  19. package/dist/heart/attachments/originals.js +88 -0
  20. package/dist/heart/attachments/render.js +29 -0
  21. package/dist/heart/attachments/sources/adapter.js +2 -0
  22. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  23. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  24. package/dist/heart/attachments/sources/index.js +16 -0
  25. package/dist/heart/attachments/store.js +103 -0
  26. package/dist/heart/attachments/types.js +93 -0
  27. package/dist/heart/auth/auth-flow.js +463 -0
  28. package/dist/heart/bridges/manager.js +358 -0
  29. package/dist/heart/bridges/state-machine.js +135 -0
  30. package/dist/heart/bridges/store.js +123 -0
  31. package/dist/heart/bundle-state.js +168 -0
  32. package/dist/heart/commitments.js +111 -0
  33. package/dist/heart/config-registry.js +304 -0
  34. package/dist/heart/config.js +53 -21
  35. package/dist/heart/core.js +744 -252
  36. package/dist/heart/cross-chat-delivery.js +131 -0
  37. package/dist/heart/daemon/agent-config-check.js +561 -0
  38. package/dist/heart/daemon/agent-discovery.js +79 -3
  39. package/dist/heart/daemon/agent-service.js +360 -0
  40. package/dist/heart/daemon/agentic-repair.js +170 -0
  41. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  42. package/dist/heart/daemon/cadence.js +70 -0
  43. package/dist/heart/daemon/cli-defaults.js +591 -0
  44. package/dist/heart/daemon/cli-exec.js +2633 -0
  45. package/dist/heart/daemon/cli-help.js +306 -0
  46. package/dist/heart/daemon/cli-parse.js +913 -0
  47. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  48. package/dist/heart/daemon/cli-render.js +512 -0
  49. package/dist/heart/daemon/cli-types.js +8 -0
  50. package/dist/heart/daemon/daemon-cli.js +30 -1171
  51. package/dist/heart/daemon/daemon-entry.js +358 -3
  52. package/dist/heart/daemon/daemon-health.js +141 -0
  53. package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
  54. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  55. package/dist/heart/daemon/daemon.js +751 -58
  56. package/dist/heart/daemon/doctor-types.js +8 -0
  57. package/dist/heart/daemon/doctor.js +465 -0
  58. package/dist/heart/daemon/health-monitor.js +79 -1
  59. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  60. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  61. package/dist/heart/daemon/http-health-probe.js +80 -0
  62. package/dist/heart/daemon/inner-status.js +89 -0
  63. package/dist/heart/daemon/interactive-repair.js +91 -0
  64. package/dist/heart/daemon/launchd.js +46 -9
  65. package/dist/heart/daemon/log-tailer.js +82 -12
  66. package/dist/heart/daemon/logs-prune.js +105 -0
  67. package/dist/heart/daemon/message-router.js +17 -8
  68. package/dist/heart/daemon/os-cron-deps.js +134 -0
  69. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  70. package/dist/heart/daemon/process-manager.js +201 -0
  71. package/dist/heart/daemon/provider-discovery.js +105 -0
  72. package/dist/heart/daemon/pulse.js +463 -0
  73. package/dist/heart/daemon/run-hooks.js +2 -0
  74. package/dist/heart/daemon/runtime-logging.js +67 -16
  75. package/dist/heart/daemon/runtime-metadata.js +101 -0
  76. package/dist/heart/daemon/runtime-mode.js +67 -0
  77. package/dist/heart/daemon/safe-mode.js +161 -0
  78. package/dist/heart/daemon/sense-manager.js +72 -3
  79. package/dist/heart/daemon/session-id-resolver.js +131 -0
  80. package/dist/heart/daemon/skill-management-installer.js +94 -0
  81. package/dist/heart/daemon/socket-client.js +307 -0
  82. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  83. package/dist/heart/daemon/startup-tui.js +237 -0
  84. package/dist/heart/daemon/task-scheduler.js +3 -25
  85. package/dist/heart/daemon/thoughts.js +510 -0
  86. package/dist/heart/daemon/up-progress.js +135 -0
  87. package/dist/heart/delegation.js +62 -0
  88. package/dist/heart/habits/habit-migration.js +181 -0
  89. package/dist/heart/habits/habit-parser.js +140 -0
  90. package/dist/heart/habits/habit-scheduler.js +371 -0
  91. package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -120
  92. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  93. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  94. package/dist/heart/{daemon → hatch}/specialist-tools.js +56 -10
  95. package/dist/heart/identity.js +154 -59
  96. package/dist/heart/kept-notes.js +357 -0
  97. package/dist/heart/kicks.js +2 -20
  98. package/dist/heart/machine-identity.js +161 -0
  99. package/dist/heart/mcp/mcp-server.js +653 -0
  100. package/dist/heart/migrate-config.js +127 -0
  101. package/dist/heart/model-capabilities.js +59 -0
  102. package/dist/heart/outlook/outlook-http-hooks.js +64 -0
  103. package/dist/heart/outlook/outlook-http-response.js +7 -0
  104. package/dist/heart/outlook/outlook-http-routes.js +232 -0
  105. package/dist/heart/outlook/outlook-http-static.js +99 -0
  106. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  107. package/dist/heart/outlook/outlook-http.js +99 -0
  108. package/dist/heart/outlook/outlook-read.js +28 -0
  109. package/dist/heart/outlook/outlook-types.js +27 -0
  110. package/dist/heart/outlook/outlook-view.js +194 -0
  111. package/dist/heart/outlook/readers/agent-machine.js +355 -0
  112. package/dist/heart/outlook/readers/continuity-readers.js +332 -0
  113. package/dist/heart/outlook/readers/runtime-readers.js +660 -0
  114. package/dist/heart/outlook/readers/sessions.js +231 -0
  115. package/dist/heart/outlook/readers/shared.js +111 -0
  116. package/dist/heart/progress-story.js +42 -0
  117. package/dist/heart/provider-attempt.js +133 -0
  118. package/dist/heart/provider-binding-resolver.js +240 -0
  119. package/dist/heart/provider-credential-pool.js +395 -0
  120. package/dist/heart/provider-failover.js +135 -0
  121. package/dist/heart/provider-models.js +81 -0
  122. package/dist/heart/provider-ping.js +258 -0
  123. package/dist/heart/provider-state.js +208 -0
  124. package/dist/heart/providers/anthropic-token.js +163 -0
  125. package/dist/heart/providers/anthropic.js +171 -50
  126. package/dist/heart/providers/azure.js +97 -11
  127. package/dist/heart/providers/error-classification.js +63 -0
  128. package/dist/heart/providers/github-copilot.js +135 -0
  129. package/dist/heart/providers/minimax-vlm.js +189 -0
  130. package/dist/heart/providers/minimax.js +23 -6
  131. package/dist/heart/providers/openai-codex.js +33 -23
  132. package/dist/heart/session-activity.js +190 -0
  133. package/dist/heart/session-events.js +727 -0
  134. package/dist/heart/session-transcript.js +162 -0
  135. package/dist/heart/start-of-turn-packet.js +341 -0
  136. package/dist/heart/streaming.js +36 -27
  137. package/dist/heart/sync.js +332 -0
  138. package/dist/heart/target-resolution.js +127 -0
  139. package/dist/heart/tempo.js +93 -0
  140. package/dist/heart/temporal-view.js +41 -0
  141. package/dist/heart/tool-activity-callbacks.js +36 -0
  142. package/dist/heart/tool-description.js +135 -0
  143. package/dist/heart/tool-friction.js +55 -0
  144. package/dist/heart/tool-loop.js +200 -0
  145. package/dist/heart/turn-context.js +358 -0
  146. package/dist/heart/turn-coordinator.js +28 -0
  147. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  148. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  149. package/dist/heart/versioning/ouro-path-installer.js +296 -0
  150. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  151. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  152. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  153. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  154. package/dist/mind/bundle-manifest.js +7 -1
  155. package/dist/mind/context.js +140 -94
  156. package/dist/mind/diary-integrity.js +60 -0
  157. package/dist/mind/{memory.js → diary.js} +84 -96
  158. package/dist/mind/embedding-provider.js +60 -0
  159. package/dist/mind/file-state.js +179 -0
  160. package/dist/mind/first-impressions.js +14 -1
  161. package/dist/mind/friends/channel.js +56 -0
  162. package/dist/mind/friends/group-context.js +144 -0
  163. package/dist/mind/friends/resolver.js +38 -1
  164. package/dist/mind/friends/store-file.js +58 -3
  165. package/dist/mind/friends/trust-explanation.js +74 -0
  166. package/dist/mind/friends/types.js +9 -1
  167. package/dist/mind/journal-index.js +161 -0
  168. package/dist/mind/note-search.js +268 -0
  169. package/dist/mind/obligation-steering.js +221 -0
  170. package/dist/mind/pending.js +74 -7
  171. package/dist/mind/prompt.js +1000 -112
  172. package/dist/mind/provenance-trust.js +26 -0
  173. package/dist/mind/scrutiny.js +173 -0
  174. package/dist/mind/token-estimate.js +8 -12
  175. package/dist/nerves/cli-logging.js +7 -1
  176. package/dist/nerves/coverage/audit-rules.js +15 -6
  177. package/dist/nerves/coverage/audit.js +28 -2
  178. package/dist/nerves/coverage/cli.js +1 -1
  179. package/dist/nerves/coverage/file-completeness.js +83 -5
  180. package/dist/nerves/coverage/run-artifacts.js +1 -1
  181. package/dist/nerves/event-buffer.js +111 -0
  182. package/dist/nerves/index.js +224 -4
  183. package/dist/nerves/observation.js +20 -0
  184. package/dist/nerves/redact.js +79 -0
  185. package/dist/nerves/runtime.js +5 -1
  186. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  187. package/dist/outlook-ui/assets/index-xTdv64BV.js +61 -0
  188. package/dist/outlook-ui/index.html +15 -0
  189. package/dist/repertoire/ado-client.js +15 -56
  190. package/dist/repertoire/ado-semantic.js +11 -10
  191. package/dist/repertoire/api-client.js +97 -0
  192. package/dist/repertoire/bitwarden-store.js +319 -0
  193. package/dist/repertoire/bundle-templates.js +72 -0
  194. package/dist/repertoire/bw-installer.js +79 -0
  195. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  196. package/dist/repertoire/coding/context-pack.js +330 -0
  197. package/dist/repertoire/coding/feedback.js +197 -30
  198. package/dist/repertoire/coding/manager.js +158 -9
  199. package/dist/repertoire/coding/spawner.js +55 -9
  200. package/dist/repertoire/coding/tools.js +170 -7
  201. package/dist/repertoire/commerce-errors.js +109 -0
  202. package/dist/repertoire/commerce-self-test.js +156 -0
  203. package/dist/repertoire/credential-access.js +527 -0
  204. package/dist/repertoire/duffel-client.js +185 -0
  205. package/dist/repertoire/github-client.js +14 -55
  206. package/dist/repertoire/graph-client.js +11 -52
  207. package/dist/repertoire/guardrails.js +375 -0
  208. package/dist/repertoire/mcp-client.js +255 -0
  209. package/dist/repertoire/mcp-manager.js +305 -0
  210. package/dist/repertoire/mcp-tools.js +63 -0
  211. package/dist/repertoire/shell-sessions.js +133 -0
  212. package/dist/repertoire/skills.js +15 -24
  213. package/dist/repertoire/stripe-client.js +131 -0
  214. package/dist/repertoire/tasks/board.js +43 -5
  215. package/dist/repertoire/tasks/fix.js +182 -0
  216. package/dist/repertoire/tasks/index.js +28 -10
  217. package/dist/repertoire/tasks/lifecycle.js +2 -2
  218. package/dist/repertoire/tasks/parser.js +3 -2
  219. package/dist/repertoire/tasks/scanner.js +194 -37
  220. package/dist/repertoire/tasks/transitions.js +16 -79
  221. package/dist/repertoire/tool-results.js +29 -0
  222. package/dist/repertoire/tools-attachments.js +316 -0
  223. package/dist/repertoire/tools-base.js +45 -771
  224. package/dist/repertoire/tools-bluebubbles.js +1 -0
  225. package/dist/repertoire/tools-bridge.js +141 -0
  226. package/dist/repertoire/tools-bundle.js +984 -0
  227. package/dist/repertoire/tools-config.js +185 -0
  228. package/dist/repertoire/tools-continuity.js +248 -0
  229. package/dist/repertoire/tools-credential.js +182 -0
  230. package/dist/repertoire/tools-files.js +342 -0
  231. package/dist/repertoire/tools-flight.js +224 -0
  232. package/dist/repertoire/tools-flow.js +105 -0
  233. package/dist/repertoire/tools-github.js +1 -7
  234. package/dist/repertoire/tools-notes.js +376 -0
  235. package/dist/repertoire/tools-session.js +739 -0
  236. package/dist/repertoire/tools-shell.js +120 -0
  237. package/dist/repertoire/tools-stripe.js +180 -0
  238. package/dist/repertoire/tools-surface.js +243 -0
  239. package/dist/repertoire/tools-teams.js +12 -62
  240. package/dist/repertoire/tools-travel.js +125 -0
  241. package/dist/repertoire/tools-user-profile.js +144 -0
  242. package/dist/repertoire/tools-vault.js +110 -0
  243. package/dist/repertoire/tools.js +144 -138
  244. package/dist/repertoire/travel-api-client.js +360 -0
  245. package/dist/repertoire/user-profile.js +118 -0
  246. package/dist/repertoire/vault-setup.js +241 -0
  247. package/dist/scripts/claude-code-hook.js +41 -0
  248. package/dist/scripts/claude-code-stop-hook.js +47 -0
  249. package/dist/senses/attention-queue.js +116 -0
  250. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  251. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  252. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +225 -9
  253. package/dist/senses/bluebubbles/entry.js +13 -0
  254. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  255. package/dist/senses/bluebubbles/index.js +1590 -0
  256. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  257. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
  258. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  259. package/dist/senses/bluebubbles/replay.js +129 -0
  260. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  261. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  262. package/dist/senses/cli/bracketed-paste.js +82 -0
  263. package/dist/senses/cli/image-paste.js +287 -0
  264. package/dist/senses/cli/image-ref-navigation.js +75 -0
  265. package/dist/senses/cli/ink-app.js +156 -0
  266. package/dist/senses/cli/inline-diff.js +64 -0
  267. package/dist/senses/cli/input-keys.js +174 -0
  268. package/dist/senses/cli/kill-ring.js +86 -0
  269. package/dist/senses/cli/message-list.js +51 -0
  270. package/dist/senses/cli/ouro-tui.js +605 -0
  271. package/dist/senses/cli/spinner-imperative.js +135 -0
  272. package/dist/senses/cli/spinner.js +101 -0
  273. package/dist/senses/cli/status-line.js +60 -0
  274. package/dist/senses/cli/streaming-markdown.js +526 -0
  275. package/dist/senses/cli/tool-display.js +83 -0
  276. package/dist/senses/cli/tool-render.js +85 -0
  277. package/dist/senses/cli/tui-store.js +240 -0
  278. package/dist/senses/cli/virtual-list.js +35 -0
  279. package/dist/senses/cli-entry.js +1 -1
  280. package/dist/senses/cli-layout.js +187 -0
  281. package/dist/senses/cli.js +603 -246
  282. package/dist/senses/commands.js +65 -1
  283. package/dist/senses/continuity.js +94 -0
  284. package/dist/senses/habit-turn-message.js +108 -0
  285. package/dist/senses/inner-dialog-worker.js +112 -19
  286. package/dist/senses/inner-dialog.js +633 -86
  287. package/dist/senses/pipeline.js +567 -0
  288. package/dist/senses/shared-turn.js +199 -0
  289. package/dist/senses/surface-tool.js +68 -0
  290. package/dist/senses/teams.js +665 -160
  291. package/dist/senses/trust-gate.js +112 -2
  292. package/package.json +29 -7
  293. package/skills/agent-commerce.md +106 -0
  294. package/skills/browser-navigation.md +110 -0
  295. package/skills/commerce-setup-guide.md +116 -0
  296. package/skills/commerce-setup.md +84 -0
  297. package/skills/configure-dev-tools.md +81 -0
  298. package/skills/travel-planning.md +138 -0
  299. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  300. package/dist/heart/daemon/subagent-installer.js +0 -134
  301. package/dist/mind/associative-recall.js +0 -197
  302. package/dist/senses/bluebubbles-entry.js +0 -11
  303. package/dist/senses/bluebubbles.js +0 -548
  304. package/dist/senses/debug-activity.js +0 -127
  305. package/subagents/README.md +0 -73
  306. package/subagents/work-doer.md +0 -235
  307. package/subagents/work-merger.md +0 -618
  308. package/subagents/work-planner.md +0 -382
  309. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  310. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  311. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  312. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  313. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  314. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  315. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  316. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  317. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  318. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  319. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  320. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  321. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  322. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  323. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  324. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,727 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.normalizeContinuityState = normalizeContinuityState;
37
+ exports.validateSessionMessages = validateSessionMessages;
38
+ exports.repairSessionMessages = repairSessionMessages;
39
+ exports.migrateToolNames = migrateToolNames;
40
+ exports.sanitizeProviderMessages = sanitizeProviderMessages;
41
+ exports.stampIngressTime = stampIngressTime;
42
+ exports.getIngressTime = getIngressTime;
43
+ exports.projectProviderMessages = projectProviderMessages;
44
+ exports.annotateMessageTimestamps = annotateMessageTimestamps;
45
+ exports.bestEventTimestamp = bestEventTimestamp;
46
+ exports.formatSessionEventTimestamp = formatSessionEventTimestamp;
47
+ exports.extractEventText = extractEventText;
48
+ exports.deriveSessionChronology = deriveSessionChronology;
49
+ exports.describeCurrentSessionTiming = describeCurrentSessionTiming;
50
+ exports.migrateLegacySessionEnvelope = migrateLegacySessionEnvelope;
51
+ exports.parseSessionEnvelope = parseSessionEnvelope;
52
+ exports.loadSessionEnvelopeFile = loadSessionEnvelopeFile;
53
+ exports.buildCanonicalSessionEnvelope = buildCanonicalSessionEnvelope;
54
+ exports.appendSyntheticAssistantEvent = appendSyntheticAssistantEvent;
55
+ const fs = __importStar(require("fs"));
56
+ const runtime_1 = require("../nerves/runtime");
57
+ function formatElapsed(ms) {
58
+ const minutes = Math.max(0, Math.floor(ms / 60000));
59
+ if (minutes < 60)
60
+ return `${minutes}m ago`;
61
+ const hours = Math.floor(minutes / 60);
62
+ if (hours < 24)
63
+ return `${hours}h ago`;
64
+ const days = Math.floor(hours / 24);
65
+ return `${days}d ago`;
66
+ }
67
+ const LEGACY_WRITTEN_NOTE_PREFIX = "mem" + "ory";
68
+ const TOOL_NAME_MIGRATIONS = {
69
+ final_answer: "settle",
70
+ no_response: "observe",
71
+ go_inward: "ponder",
72
+ descend: "ponder",
73
+ [`${LEGACY_WRITTEN_NOTE_PREFIX}_save`]: "diary_write",
74
+ [`${LEGACY_WRITTEN_NOTE_PREFIX}_search`]: "search_notes",
75
+ };
76
+ function normalizeUsage(usage) {
77
+ if (!usage || typeof usage !== "object")
78
+ return null;
79
+ const record = usage;
80
+ if (typeof record.input_tokens !== "number"
81
+ || typeof record.output_tokens !== "number"
82
+ || typeof record.reasoning_tokens !== "number"
83
+ || typeof record.total_tokens !== "number") {
84
+ return null;
85
+ }
86
+ return {
87
+ input_tokens: record.input_tokens,
88
+ output_tokens: record.output_tokens,
89
+ reasoning_tokens: record.reasoning_tokens,
90
+ total_tokens: record.total_tokens,
91
+ };
92
+ }
93
+ function normalizeContinuityState(state) {
94
+ const record = state && typeof state === "object"
95
+ ? state
96
+ : null;
97
+ return {
98
+ mustResolveBeforeHandoff: record?.mustResolveBeforeHandoff === true,
99
+ lastFriendActivityAt: typeof record?.lastFriendActivityAt === "string" ? record.lastFriendActivityAt : null,
100
+ };
101
+ }
102
+ function normalizeContent(content) {
103
+ if (content == null)
104
+ return null;
105
+ if (typeof content === "string")
106
+ return content;
107
+ if (!Array.isArray(content))
108
+ return null;
109
+ return content
110
+ .filter((part) => part != null && typeof part === "object")
111
+ .map((part) => ({ ...part }));
112
+ }
113
+ function normalizeToolCalls(rawToolCalls) {
114
+ if (!Array.isArray(rawToolCalls))
115
+ return [];
116
+ return rawToolCalls
117
+ .filter((call) => call != null && typeof call === "object")
118
+ .map((call) => {
119
+ const fn = call.function;
120
+ const originalName = typeof fn?.name === "string" ? fn.name : "unknown";
121
+ const migratedName = TOOL_NAME_MIGRATIONS[originalName] ?? originalName;
122
+ return {
123
+ id: typeof call.id === "string" ? call.id : "",
124
+ type: typeof call.type === "string" ? call.type : "function",
125
+ function: {
126
+ name: migratedName,
127
+ arguments: typeof fn?.arguments === "string" ? fn.arguments : JSON.stringify(fn?.arguments ?? ""),
128
+ },
129
+ };
130
+ });
131
+ }
132
+ function normalizeRole(role) {
133
+ if (role === "developer")
134
+ return "system";
135
+ return role === "system" || role === "user" || role === "assistant" || role === "tool"
136
+ ? role
137
+ : "user";
138
+ }
139
+ function normalizeMessage(message) {
140
+ const record = message;
141
+ const role = normalizeRole(record.role);
142
+ if (role === "assistant") {
143
+ return {
144
+ role,
145
+ content: normalizeContent(record.content),
146
+ name: typeof record.name === "string" ? record.name : null,
147
+ toolCallId: null,
148
+ toolCalls: normalizeToolCalls(record.tool_calls),
149
+ hadToolCallsField: Array.isArray(record.tool_calls),
150
+ };
151
+ }
152
+ if (role === "tool") {
153
+ return {
154
+ role,
155
+ content: typeof record.content === "string" ? record.content : "",
156
+ name: null,
157
+ toolCallId: typeof record.tool_call_id === "string" ? record.tool_call_id : null,
158
+ toolCalls: [],
159
+ hadToolCallsField: false,
160
+ };
161
+ }
162
+ return {
163
+ role,
164
+ content: normalizeContent(record.content) ?? "",
165
+ name: typeof record.name === "string" ? record.name : null,
166
+ toolCallId: null,
167
+ toolCalls: [],
168
+ hadToolCallsField: false,
169
+ };
170
+ }
171
+ function contentText(content) {
172
+ if (typeof content === "string")
173
+ return content;
174
+ if (!Array.isArray(content))
175
+ return "";
176
+ return content
177
+ .map((part) => (part.type === "text" && typeof part.text === "string"
178
+ ? part.text
179
+ : ""))
180
+ .filter((text) => text.length > 0)
181
+ .join("");
182
+ }
183
+ function toProviderMessage(message) {
184
+ if (message.role === "assistant") {
185
+ const assistant = {
186
+ role: "assistant",
187
+ content: message.content,
188
+ };
189
+ if (message.name)
190
+ assistant.name = message.name;
191
+ if (message.hadToolCallsField || message.toolCalls.length > 0) {
192
+ assistant.tool_calls = message.toolCalls.map((call) => ({
193
+ id: call.id,
194
+ type: call.type,
195
+ function: {
196
+ name: call.function.name,
197
+ arguments: call.function.arguments,
198
+ },
199
+ }));
200
+ }
201
+ return assistant;
202
+ }
203
+ if (message.role === "tool") {
204
+ return {
205
+ role: "tool",
206
+ content: typeof message.content === "string" ? message.content : contentText(message.content),
207
+ tool_call_id: message.toolCallId ?? "",
208
+ };
209
+ }
210
+ if (message.role === "system") {
211
+ return {
212
+ role: "system",
213
+ content: typeof message.content === "string" ? message.content : contentText(message.content),
214
+ ...(message.name ? { name: message.name } : {}),
215
+ };
216
+ }
217
+ return {
218
+ role: "user",
219
+ content: (typeof message.content === "string" || Array.isArray(message.content) ? message.content : ""),
220
+ ...(message.name ? { name: message.name } : {}),
221
+ };
222
+ }
223
+ function messageFingerprint(message) {
224
+ const normalized = normalizeMessage(message);
225
+ return JSON.stringify({
226
+ role: normalized.role,
227
+ content: normalized.content,
228
+ name: normalized.name,
229
+ tool_call_id: normalized.toolCallId,
230
+ tool_calls: normalized.toolCalls,
231
+ });
232
+ }
233
+ function makeEventId(sequence) {
234
+ return `evt-${String(sequence).padStart(6, "0")}`;
235
+ }
236
+ function validateSessionMessages(messages) {
237
+ const violations = [];
238
+ let prevNonToolRole = null;
239
+ let prevAssistantHadToolCalls = false;
240
+ let sawToolResultSincePrevAssistant = false;
241
+ for (let i = 0; i < messages.length; i++) {
242
+ const msg = normalizeMessage(messages[i]);
243
+ if (msg.role === "system")
244
+ continue;
245
+ if (msg.role === "tool") {
246
+ sawToolResultSincePrevAssistant = true;
247
+ continue;
248
+ }
249
+ if (msg.role === "assistant" && prevNonToolRole === "assistant") {
250
+ if (!(prevAssistantHadToolCalls && sawToolResultSincePrevAssistant)) {
251
+ violations.push(`back-to-back assistant at index ${i}`);
252
+ }
253
+ }
254
+ prevAssistantHadToolCalls = msg.role === "assistant" && msg.toolCalls.length > 0;
255
+ sawToolResultSincePrevAssistant = false;
256
+ prevNonToolRole = msg.role;
257
+ }
258
+ return violations;
259
+ }
260
+ function repairSessionMessages(messages) {
261
+ const normalized = messages.map(normalizeMessage);
262
+ const violations = validateSessionMessages(messages);
263
+ if (violations.length === 0)
264
+ return normalized.map(toProviderMessage);
265
+ const result = [];
266
+ for (const msg of normalized) {
267
+ if (msg.role === "assistant" && result.length > 0) {
268
+ const prev = result[result.length - 1];
269
+ if (prev.role === "assistant" && prev.toolCalls.length === 0) {
270
+ const prevContent = contentText(prev.content);
271
+ const curContent = contentText(msg.content);
272
+ prev.content = `${prevContent}\n\n${curContent}`;
273
+ continue;
274
+ }
275
+ }
276
+ result.push(msg);
277
+ }
278
+ (0, runtime_1.emitNervesEvent)({
279
+ level: "info",
280
+ event: "mind.session_invariant_repair",
281
+ component: "mind",
282
+ message: "repaired session invariant violations",
283
+ meta: { violations },
284
+ });
285
+ return result.map(toProviderMessage);
286
+ }
287
+ function stripOrphanedToolResults(messages) {
288
+ const normalized = messages.map(normalizeMessage);
289
+ const validCallIds = new Set();
290
+ for (const msg of normalized) {
291
+ if (msg.role !== "assistant")
292
+ continue;
293
+ for (const toolCall of msg.toolCalls)
294
+ validCallIds.add(toolCall.id);
295
+ }
296
+ let removed = 0;
297
+ const repaired = normalized.filter((msg) => {
298
+ if (msg.role !== "tool")
299
+ return true;
300
+ const keep = msg.toolCallId !== null && validCallIds.has(msg.toolCallId);
301
+ if (!keep)
302
+ removed++;
303
+ return keep;
304
+ });
305
+ if (removed > 0) {
306
+ (0, runtime_1.emitNervesEvent)({
307
+ level: "info",
308
+ event: "mind.session_orphan_tool_result_repair",
309
+ component: "mind",
310
+ message: "removed orphaned tool results from session history",
311
+ meta: { removed },
312
+ });
313
+ }
314
+ return repaired.map(toProviderMessage);
315
+ }
316
+ function migrateToolNames(messages) {
317
+ const safeMessages = messages.filter((message) => Boolean(message) && typeof message === "object");
318
+ let migrated = 0;
319
+ for (const message of safeMessages) {
320
+ const record = message;
321
+ if (record.role !== "assistant" || !Array.isArray(record.tool_calls))
322
+ continue;
323
+ for (const toolCall of record.tool_calls) {
324
+ if (!toolCall || typeof toolCall !== "object")
325
+ continue;
326
+ const toolRecord = toolCall;
327
+ if (toolRecord.type !== "function")
328
+ continue;
329
+ const originalName = toolRecord.function?.name;
330
+ if (typeof originalName !== "string")
331
+ continue;
332
+ if (TOOL_NAME_MIGRATIONS[originalName])
333
+ migrated += 1;
334
+ }
335
+ }
336
+ if (migrated > 0) {
337
+ (0, runtime_1.emitNervesEvent)({
338
+ level: "info",
339
+ event: "mind.session_tool_name_migration",
340
+ component: "mind",
341
+ message: "migrated deprecated tool names in session history",
342
+ meta: { migrated },
343
+ });
344
+ }
345
+ return safeMessages.map(normalizeMessage).map(toProviderMessage);
346
+ }
347
+ function sanitizeProviderMessages(messages) {
348
+ const safeMessages = messages.filter((message) => Boolean(message) && typeof message === "object");
349
+ const normalized = safeMessages.map(normalizeMessage);
350
+ const violations = validateSessionMessages(safeMessages);
351
+ if (violations.length > 0) {
352
+ (0, runtime_1.emitNervesEvent)({
353
+ level: "info",
354
+ event: "mind.session_invariant_violation",
355
+ component: "mind",
356
+ message: "session invariant violated",
357
+ meta: { violations },
358
+ });
359
+ }
360
+ return migrateToolNames(stripOrphanedToolResults(repairSessionMessages(normalized.map(toProviderMessage))));
361
+ }
362
+ function stampIngressTime(msg) {
363
+ msg._ingressAt = new Date().toISOString();
364
+ }
365
+ function getIngressTime(msg) {
366
+ const value = msg._ingressAt;
367
+ return typeof value === "string" ? value : null;
368
+ }
369
+ function createEventTime(role, recordedAt, captureKind, ingressAt) {
370
+ if (captureKind === "migration") {
371
+ return {
372
+ authoredAt: null,
373
+ authoredAtSource: "migration",
374
+ observedAt: null,
375
+ observedAtSource: "migration",
376
+ recordedAt,
377
+ recordedAtSource: "migration",
378
+ };
379
+ }
380
+ if (role === "user") {
381
+ return {
382
+ authoredAt: null,
383
+ authoredAtSource: "unknown",
384
+ observedAt: ingressAt ?? recordedAt,
385
+ observedAtSource: "ingest",
386
+ recordedAt,
387
+ recordedAtSource: "save",
388
+ };
389
+ }
390
+ return {
391
+ authoredAt: recordedAt,
392
+ authoredAtSource: "local",
393
+ observedAt: recordedAt,
394
+ observedAtSource: "local",
395
+ recordedAt,
396
+ recordedAtSource: "save",
397
+ };
398
+ }
399
+ function buildEventFromMessage(message, sequence, recordedAt, captureKind, sourceMessageIndex, legacyVersion, ingressAt) {
400
+ const normalized = normalizeMessage(message);
401
+ const role = normalized.role;
402
+ return {
403
+ id: makeEventId(sequence),
404
+ sequence,
405
+ role,
406
+ content: normalized.content,
407
+ name: normalized.name,
408
+ toolCallId: role === "tool" ? normalized.toolCallId : null,
409
+ toolCalls: role === "assistant" ? normalized.toolCalls : [],
410
+ attachments: [],
411
+ time: createEventTime(role, recordedAt, captureKind, ingressAt),
412
+ relations: {
413
+ replyToEventId: null,
414
+ threadRootEventId: null,
415
+ references: [],
416
+ toolCallId: role === "tool" ? normalized.toolCallId : null,
417
+ supersedesEventId: null,
418
+ redactsEventId: null,
419
+ },
420
+ provenance: {
421
+ captureKind,
422
+ legacyVersion,
423
+ sourceMessageIndex,
424
+ },
425
+ };
426
+ }
427
+ function projectProviderMessages(envelope) {
428
+ const eventIds = envelope.projection.eventIds.length > 0
429
+ ? envelope.projection.eventIds
430
+ : envelope.events.map((event) => event.id);
431
+ const byId = new Map(envelope.events.map((event) => [event.id, event]));
432
+ return eventIds
433
+ .map((id) => byId.get(id))
434
+ .filter((event) => Boolean(event))
435
+ .map((event) => toProviderMessage({
436
+ role: event.role,
437
+ content: event.content,
438
+ name: event.name,
439
+ toolCallId: event.toolCallId,
440
+ toolCalls: event.toolCalls,
441
+ hadToolCallsField: event.toolCalls.length > 0,
442
+ }));
443
+ }
444
+ /**
445
+ * Annotate user and assistant messages with a relative time offset tag.
446
+ * System and tool messages are untouched.
447
+ */
448
+ function annotateMessageTimestamps(envelope, messages, nowMs = Date.now()) {
449
+ const eventIds = envelope.projection.eventIds.length > 0
450
+ ? envelope.projection.eventIds
451
+ : envelope.events.map((event) => event.id);
452
+ const byId = new Map(envelope.events.map((event) => [event.id, event]));
453
+ const events = eventIds
454
+ .map((id) => byId.get(id))
455
+ .filter((event) => Boolean(event));
456
+ return messages.map((msg, i) => {
457
+ const event = events[i];
458
+ if (!event)
459
+ return msg;
460
+ if (event.role !== "user" && event.role !== "assistant")
461
+ return msg;
462
+ const ts = bestEventTimestamp(event);
463
+ const elapsed = nowMs - Date.parse(ts);
464
+ if (elapsed < 0)
465
+ return msg;
466
+ const tag = elapsed < 60000 ? "[just now]" : `[-${formatElapsedCompact(elapsed)}]`;
467
+ if (typeof msg.content === "string" && msg.content.length > 0) {
468
+ return { ...msg, content: `${tag} ${msg.content}` };
469
+ }
470
+ return msg;
471
+ });
472
+ }
473
+ /** Compact elapsed format for message annotations: "3m", "2h", "1d". */
474
+ function formatElapsedCompact(ms) {
475
+ const minutes = Math.max(1, Math.floor(ms / 60000));
476
+ if (minutes < 60)
477
+ return `${minutes}m`;
478
+ const hours = Math.floor(minutes / 60);
479
+ if (hours < 24)
480
+ return `${hours}h`;
481
+ const days = Math.floor(hours / 24);
482
+ return `${days}d`;
483
+ }
484
+ function bestEventTimestamp(event) {
485
+ return event.time.authoredAt ?? event.time.observedAt ?? event.time.recordedAt;
486
+ }
487
+ function formatSessionEventTimestamp(event) {
488
+ const iso = bestEventTimestamp(event);
489
+ const date = new Date(iso);
490
+ const year = date.getUTCFullYear();
491
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
492
+ const day = String(date.getUTCDate()).padStart(2, "0");
493
+ const hour = String(date.getUTCHours()).padStart(2, "0");
494
+ const minute = String(date.getUTCMinutes()).padStart(2, "0");
495
+ return `${year}-${month}-${day} ${hour}:${minute}`;
496
+ }
497
+ function extractEventText(event) {
498
+ return contentText(event.content);
499
+ }
500
+ function deriveSessionChronology(events) {
501
+ let lastInboundAt = null;
502
+ let lastOutboundAt = null;
503
+ let lastActivityAt = null;
504
+ let lastAssistantSequence = -1;
505
+ for (const event of events) {
506
+ if (event.role === "system")
507
+ continue;
508
+ const at = bestEventTimestamp(event);
509
+ lastActivityAt = at;
510
+ if (event.role === "user") {
511
+ lastInboundAt = at;
512
+ }
513
+ if (event.role === "assistant") {
514
+ lastOutboundAt = at;
515
+ lastAssistantSequence = event.sequence;
516
+ }
517
+ }
518
+ const unansweredInboundCount = events.filter((event) => event.role === "user" && event.sequence > lastAssistantSequence).length;
519
+ return {
520
+ lastInboundAt,
521
+ lastOutboundAt,
522
+ lastActivityAt,
523
+ unansweredInboundCount,
524
+ };
525
+ }
526
+ function describeCurrentSessionTiming(events, nowMs = Date.now()) {
527
+ const chronology = deriveSessionChronology(events);
528
+ const parts = [];
529
+ if (chronology.lastInboundAt) {
530
+ parts.push(`last inbound ${formatElapsed(nowMs - Date.parse(chronology.lastInboundAt))}`);
531
+ }
532
+ if (chronology.lastOutboundAt) {
533
+ parts.push(`i last replied ${formatElapsed(nowMs - Date.parse(chronology.lastOutboundAt))}`);
534
+ }
535
+ if (chronology.unansweredInboundCount > 0) {
536
+ const count = chronology.unansweredInboundCount;
537
+ parts.push(`${count} unanswered inbound message${count === 1 ? "" : "s"}`);
538
+ }
539
+ return parts.length > 0 ? `current thread: ${parts.join("; ")}` : "";
540
+ }
541
+ function migrateLegacySessionEnvelope(raw, options) {
542
+ if (!raw || typeof raw !== "object")
543
+ return null;
544
+ const legacy = raw;
545
+ const looksLegacy = legacy.version === 1
546
+ || (legacy.version == null && ("messages" in legacy || "lastUsage" in legacy || "state" in legacy));
547
+ if (!looksLegacy)
548
+ return null;
549
+ const messages = Array.isArray(legacy.messages)
550
+ ? sanitizeProviderMessages(legacy.messages)
551
+ : [];
552
+ const recordedAt = options.fileMtimeAt ?? options.recordedAt;
553
+ const events = messages.map((message, index) => buildEventFromMessage(message, index + 1, recordedAt, "migration", index, 1));
554
+ return {
555
+ version: 2,
556
+ events,
557
+ projection: {
558
+ eventIds: events.map((event) => event.id),
559
+ trimmed: false,
560
+ maxTokens: null,
561
+ contextMargin: null,
562
+ inputTokens: null,
563
+ projectedAt: recordedAt,
564
+ },
565
+ lastUsage: normalizeUsage(legacy.lastUsage),
566
+ state: normalizeContinuityState(legacy.state),
567
+ };
568
+ }
569
+ function parseSessionEnvelope(raw, options = {}) {
570
+ const recordedAt = options.recordedAt ?? new Date().toISOString();
571
+ const fileMtimeAt = options.fileMtimeAt ?? null;
572
+ const migrated = migrateLegacySessionEnvelope(raw, { recordedAt, fileMtimeAt });
573
+ if (migrated)
574
+ return migrated;
575
+ if (!raw || typeof raw !== "object")
576
+ return null;
577
+ const record = raw;
578
+ if (record.version !== 2 || !Array.isArray(record.events) || !record.projection || typeof record.projection !== "object") {
579
+ return null;
580
+ }
581
+ const events = record.events
582
+ .filter((event) => event != null && typeof event === "object")
583
+ .map((event, index) => {
584
+ const role = normalizeRole(event.role);
585
+ const time = event.time;
586
+ const relations = event.relations;
587
+ const provenance = event.provenance;
588
+ return {
589
+ id: typeof event.id === "string" ? event.id : makeEventId(index + 1),
590
+ sequence: typeof event.sequence === "number" ? event.sequence : index + 1,
591
+ role,
592
+ content: normalizeContent(event.content),
593
+ name: typeof event.name === "string" ? event.name : null,
594
+ toolCallId: typeof event.toolCallId === "string" ? event.toolCallId : null,
595
+ toolCalls: normalizeToolCalls(event.toolCalls),
596
+ attachments: Array.isArray(event.attachments) ? event.attachments.filter((item) => typeof item === "string") : [],
597
+ time: {
598
+ authoredAt: typeof time?.authoredAt === "string" ? time.authoredAt : null,
599
+ authoredAtSource: typeof time?.authoredAtSource === "string" ? time.authoredAtSource : "unknown",
600
+ observedAt: typeof time?.observedAt === "string" ? time.observedAt : null,
601
+ observedAtSource: typeof time?.observedAtSource === "string" ? time.observedAtSource : "unknown",
602
+ recordedAt: typeof time?.recordedAt === "string" ? time.recordedAt : recordedAt,
603
+ recordedAtSource: typeof time?.recordedAtSource === "string" ? time.recordedAtSource : "save",
604
+ },
605
+ relations: {
606
+ replyToEventId: typeof relations?.replyToEventId === "string" ? relations.replyToEventId : null,
607
+ threadRootEventId: typeof relations?.threadRootEventId === "string" ? relations.threadRootEventId : null,
608
+ references: Array.isArray(relations?.references) ? relations.references.filter((item) => typeof item === "string") : [],
609
+ toolCallId: typeof relations?.toolCallId === "string" ? relations.toolCallId : null,
610
+ supersedesEventId: typeof relations?.supersedesEventId === "string" ? relations.supersedesEventId : null,
611
+ redactsEventId: typeof relations?.redactsEventId === "string" ? relations.redactsEventId : null,
612
+ },
613
+ provenance: {
614
+ captureKind: typeof provenance?.captureKind === "string" ? provenance.captureKind : "live",
615
+ legacyVersion: typeof provenance?.legacyVersion === "number" ? provenance.legacyVersion : null,
616
+ sourceMessageIndex: typeof provenance?.sourceMessageIndex === "number" ? provenance.sourceMessageIndex : null,
617
+ },
618
+ };
619
+ });
620
+ const projection = record.projection;
621
+ return {
622
+ version: 2,
623
+ events,
624
+ projection: {
625
+ eventIds: Array.isArray(projection.eventIds) ? projection.eventIds.filter((item) => typeof item === "string") : [],
626
+ trimmed: projection.trimmed === true,
627
+ maxTokens: typeof projection.maxTokens === "number" ? projection.maxTokens : null,
628
+ contextMargin: typeof projection.contextMargin === "number" ? projection.contextMargin : null,
629
+ inputTokens: typeof projection.inputTokens === "number" ? projection.inputTokens : null,
630
+ projectedAt: typeof projection.projectedAt === "string" ? projection.projectedAt : null,
631
+ },
632
+ lastUsage: normalizeUsage(record.lastUsage),
633
+ state: normalizeContinuityState(record.state),
634
+ };
635
+ }
636
+ function loadSessionEnvelopeFile(filePath) {
637
+ try {
638
+ const raw = fs.readFileSync(filePath, "utf-8");
639
+ let mtime;
640
+ try {
641
+ mtime = fs.statSync(filePath).mtime.toISOString();
642
+ }
643
+ catch {
644
+ mtime = new Date().toISOString();
645
+ }
646
+ return parseSessionEnvelope(JSON.parse(raw), {
647
+ recordedAt: mtime,
648
+ fileMtimeAt: mtime,
649
+ });
650
+ }
651
+ catch {
652
+ return null;
653
+ }
654
+ }
655
+ function findCommonPrefixLength(a, b) {
656
+ const max = Math.min(a.length, b.length);
657
+ for (let i = 0; i < max; i++) {
658
+ if (messageFingerprint(a[i]) !== messageFingerprint(b[i]))
659
+ return i;
660
+ }
661
+ return max;
662
+ }
663
+ function selectProjectedEventIds(currentMessages, currentEventIds, trimmedMessages) {
664
+ if (trimmedMessages.length === 0)
665
+ return [];
666
+ const trimmedFingerprints = trimmedMessages.map(messageFingerprint);
667
+ const result = [];
668
+ let needle = 0;
669
+ for (let i = 0; i < currentMessages.length && needle < trimmedFingerprints.length; i++) {
670
+ if (messageFingerprint(currentMessages[i]) !== trimmedFingerprints[needle])
671
+ continue;
672
+ result.push(currentEventIds[i]);
673
+ needle++;
674
+ }
675
+ return result;
676
+ }
677
+ function buildCanonicalSessionEnvelope(options) {
678
+ const existing = options.existing;
679
+ // Callers pass pre-sanitized messages + pre-captured ingress times.
680
+ const currentIngressTimes = options.currentIngressTimes ?? options.currentMessages.map(getIngressTime);
681
+ const previousMessages = options.previousMessages;
682
+ const currentMessages = options.currentMessages;
683
+ const trimmedMessages = options.trimmedMessages;
684
+ const previousProjectionIds = existing?.projection.eventIds.length
685
+ ? [...existing.projection.eventIds]
686
+ : existing?.events.map((event) => event.id) ?? [];
687
+ const commonPrefix = findCommonPrefixLength(previousMessages, currentMessages);
688
+ const appendFrom = previousMessages.length === commonPrefix ? previousMessages.length : commonPrefix;
689
+ const newMessages = currentMessages.slice(appendFrom);
690
+ const newIngressTimes = currentIngressTimes.slice(appendFrom);
691
+ const baseSequence = existing?.events.length ?? 0;
692
+ const newEvents = newMessages.map((message, index) => buildEventFromMessage(message, baseSequence + index + 1, options.recordedAt, "live", null, null, newIngressTimes[index]));
693
+ const events = [...(existing?.events ?? []), ...newEvents];
694
+ const currentEventIds = [
695
+ ...previousProjectionIds.slice(0, appendFrom),
696
+ ...newEvents.map((event) => event.id),
697
+ ];
698
+ const projectionEventIds = selectProjectedEventIds(currentMessages, currentEventIds, trimmedMessages);
699
+ return {
700
+ version: 2,
701
+ events,
702
+ projection: {
703
+ eventIds: projectionEventIds,
704
+ trimmed: projectionEventIds.length < currentEventIds.length,
705
+ maxTokens: options.projectionBasis.maxTokens,
706
+ contextMargin: options.projectionBasis.contextMargin,
707
+ inputTokens: options.projectionBasis.inputTokens,
708
+ projectedAt: options.recordedAt,
709
+ },
710
+ lastUsage: normalizeUsage(options.lastUsage),
711
+ state: normalizeContinuityState(options.state),
712
+ };
713
+ }
714
+ function appendSyntheticAssistantEvent(envelope, content, recordedAt) {
715
+ const sequence = envelope.events.length + 1;
716
+ const event = buildEventFromMessage({ role: "assistant", content }, sequence, recordedAt, "synthetic", null, null);
717
+ return {
718
+ ...envelope,
719
+ events: [...envelope.events, event],
720
+ projection: {
721
+ ...envelope.projection,
722
+ eventIds: [...envelope.projection.eventIds, event.id],
723
+ projectedAt: recordedAt,
724
+ trimmed: false,
725
+ },
726
+ };
727
+ }