@murphai/murph 1.3.1 → 1.3.2

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 (779) hide show
  1. package/CHANGELOG.md +808 -0
  2. package/config.schema.json +91 -40
  3. package/dist/bin.js +6 -4
  4. package/dist/bin.js.map +1 -1
  5. package/dist/cli-entry.d.ts +8 -1
  6. package/dist/cli-entry.d.ts.map +1 -1
  7. package/dist/cli-entry.js +79 -3
  8. package/dist/cli-entry.js.map +1 -1
  9. package/dist/commands/audit-command-helpers.d.ts.map +1 -1
  10. package/dist/commands/audit-command-helpers.js +7 -5
  11. package/dist/commands/audit-command-helpers.js.map +1 -1
  12. package/dist/commands/batch.d.ts +5 -5
  13. package/dist/commands/batch.d.ts.map +1 -1
  14. package/dist/commands/batch.js +2 -21
  15. package/dist/commands/batch.js.map +1 -1
  16. package/dist/commands/commons.d.ts.map +1 -1
  17. package/dist/commands/commons.js +2 -4
  18. package/dist/commands/commons.js.map +1 -1
  19. package/dist/commands/exercise.d.ts.map +1 -1
  20. package/dist/commands/exercise.js +16 -3
  21. package/dist/commands/exercise.js.map +1 -1
  22. package/dist/commands/experiment-progress-card-image.d.ts.map +1 -1
  23. package/dist/commands/experiment-progress-card-image.js +63 -12
  24. package/dist/commands/experiment-progress-card-image.js.map +1 -1
  25. package/dist/commands/experiment.d.ts.map +1 -1
  26. package/dist/commands/experiment.js +45 -5
  27. package/dist/commands/experiment.js.map +1 -1
  28. package/dist/commands/export-intake-read-helpers.d.ts.map +1 -1
  29. package/dist/commands/export-intake-read-helpers.js +2 -3
  30. package/dist/commands/export-intake-read-helpers.js.map +1 -1
  31. package/dist/commands/food.d.ts.map +1 -1
  32. package/dist/commands/food.js +1 -7
  33. package/dist/commands/food.js.map +1 -1
  34. package/dist/commands/habitat.d.ts.map +1 -1
  35. package/dist/commands/habitat.js +120 -9
  36. package/dist/commands/habitat.js.map +1 -1
  37. package/dist/commands/health-blood-test-save.d.ts.map +1 -1
  38. package/dist/commands/health-blood-test-save.js +42 -8
  39. package/dist/commands/health-blood-test-save.js.map +1 -1
  40. package/dist/commands/meal.d.ts.map +1 -1
  41. package/dist/commands/meal.js +4 -1
  42. package/dist/commands/meal.js.map +1 -1
  43. package/dist/commands/measurement.d.ts.map +1 -1
  44. package/dist/commands/measurement.js +3 -0
  45. package/dist/commands/measurement.js.map +1 -1
  46. package/dist/commands/murph-age.d.ts +9 -1
  47. package/dist/commands/murph-age.d.ts.map +1 -1
  48. package/dist/commands/murph-age.js +132 -7
  49. package/dist/commands/murph-age.js.map +1 -1
  50. package/dist/commands/public-validation-issue.d.ts +7 -0
  51. package/dist/commands/public-validation-issue.d.ts.map +1 -0
  52. package/dist/commands/public-validation-issue.js +7 -0
  53. package/dist/commands/public-validation-issue.js.map +1 -0
  54. package/dist/commands/scheduled-log.d.ts +12 -0
  55. package/dist/commands/scheduled-log.d.ts.map +1 -1
  56. package/dist/commands/scheduled-log.js +456 -106
  57. package/dist/commands/scheduled-log.js.map +1 -1
  58. package/dist/commands/wearables.d.ts +286 -0
  59. package/dist/commands/wearables.d.ts.map +1 -1
  60. package/dist/commands/wearables.js +33 -1
  61. package/dist/commands/wearables.js.map +1 -1
  62. package/dist/commands/workout-live.d.ts.map +1 -1
  63. package/dist/commands/workout-live.js +123 -42
  64. package/dist/commands/workout-live.js.map +1 -1
  65. package/dist/commands/workout-typed-options.d.ts +38 -0
  66. package/dist/commands/workout-typed-options.d.ts.map +1 -0
  67. package/dist/commands/workout-typed-options.js +90 -0
  68. package/dist/commands/workout-typed-options.js.map +1 -0
  69. package/dist/commands/workout.d.ts.map +1 -1
  70. package/dist/commands/workout.js +80 -73
  71. package/dist/commands/workout.js.map +1 -1
  72. package/dist/hosted-data-api-labels.d.ts.map +1 -1
  73. package/dist/hosted-data-api-labels.js +161 -30
  74. package/dist/hosted-data-api-labels.js.map +1 -1
  75. package/dist/incur-error-bridge.d.ts.map +1 -1
  76. package/dist/incur-error-bridge.js +7 -15
  77. package/dist/incur-error-bridge.js.map +1 -1
  78. package/dist/incur.generated.d.ts +22 -13
  79. package/dist/incur.generated.d.ts.map +1 -1
  80. package/dist/mapbox-address.d.ts.map +1 -1
  81. package/dist/mapbox-address.js +3 -4
  82. package/dist/mapbox-address.js.map +1 -1
  83. package/dist/mapbox-route-client.d.ts +2 -2
  84. package/dist/mapbox-route-client.d.ts.map +1 -1
  85. package/dist/mapbox-route-client.js +49 -12
  86. package/dist/mapbox-route-client.js.map +1 -1
  87. package/dist/mapbox-route-directions.d.ts.map +1 -1
  88. package/dist/mapbox-route-directions.js +0 -1
  89. package/dist/mapbox-route-directions.js.map +1 -1
  90. package/dist/mapbox-route-elevation.js +0 -1
  91. package/dist/mapbox-route-elevation.js.map +1 -1
  92. package/dist/mapbox-route-points.js +0 -2
  93. package/dist/mapbox-route-points.js.map +1 -1
  94. package/dist/mapbox-route.d.ts.map +1 -1
  95. package/dist/mapbox-route.js +1 -1
  96. package/dist/mapbox-route.js.map +1 -1
  97. package/dist/vault-cli-command-manifest.d.ts +304 -6
  98. package/dist/vault-cli-command-manifest.d.ts.map +1 -1
  99. package/dist/vault-cli-skill-hash.generated.d.ts +1 -1
  100. package/dist/vault-cli-skill-hash.generated.js +1 -1
  101. package/node_modules/@murphai/assistant-cli/dist/assistant/ui/ink.d.ts.map +1 -1
  102. package/node_modules/@murphai/assistant-cli/dist/assistant/ui/ink.js +6 -2
  103. package/node_modules/@murphai/assistant-cli/dist/assistant/ui/ink.js.map +1 -1
  104. package/node_modules/@murphai/assistant-engine/README.md +4 -3
  105. package/node_modules/@murphai/assistant-engine/dist/assistant/active-turn-input-controller.d.ts +15 -1
  106. package/node_modules/@murphai/assistant-engine/dist/assistant/active-turn-input-controller.d.ts.map +1 -1
  107. package/node_modules/@murphai/assistant-engine/dist/assistant/active-turn-input-controller.js +121 -24
  108. package/node_modules/@murphai/assistant-engine/dist/assistant/active-turn-input-controller.js.map +1 -1
  109. package/node_modules/@murphai/assistant-engine/dist/assistant/active-turn-input-journal.js +2 -2
  110. package/node_modules/@murphai/assistant-engine/dist/assistant/active-turn-input-journal.js.map +1 -1
  111. package/node_modules/@murphai/assistant-engine/dist/assistant/ask-continuation.d.ts.map +1 -1
  112. package/node_modules/@murphai/assistant-engine/dist/assistant/ask-continuation.js +1 -0
  113. package/node_modules/@murphai/assistant-engine/dist/assistant/ask-continuation.js.map +1 -1
  114. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/reply.d.ts.map +1 -1
  115. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/reply.js +55 -7
  116. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/reply.js.map +1 -1
  117. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/run-loop.d.ts.map +1 -1
  118. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/run-loop.js +9 -1
  119. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/run-loop.js.map +1 -1
  120. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/shared.d.ts +1 -0
  121. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/shared.d.ts.map +1 -1
  122. package/node_modules/@murphai/assistant-engine/dist/assistant/automation/shared.js.map +1 -1
  123. package/node_modules/@murphai/assistant-engine/dist/assistant/cli-surface-contract.generated.json +1 -1
  124. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-base-instructions.d.ts +1 -1
  125. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-base-instructions.d.ts.map +1 -1
  126. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-base-instructions.js +7 -7
  127. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-base-instructions.js.map +1 -1
  128. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-turn/planning.d.ts +1 -1
  129. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-turn/planning.d.ts.map +1 -1
  130. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-turn/planning.js +123 -22
  131. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-turn/planning.js.map +1 -1
  132. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-turn-runner.d.ts.map +1 -1
  133. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-turn-runner.js +48 -19
  134. package/node_modules/@murphai/assistant-engine/dist/assistant/codex-turn-runner.js.map +1 -1
  135. package/node_modules/@murphai/assistant-engine/dist/assistant/context-snapshot.js +3 -7
  136. package/node_modules/@murphai/assistant-engine/dist/assistant/context-snapshot.js.map +1 -1
  137. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/execution.d.ts +5 -0
  138. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/execution.d.ts.map +1 -1
  139. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/execution.js +24 -10
  140. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/execution.js.map +1 -1
  141. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/schedule.d.ts +2 -0
  142. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/schedule.d.ts.map +1 -1
  143. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/schedule.js +12 -0
  144. package/node_modules/@murphai/assistant-engine/dist/assistant/cron/schedule.js.map +1 -1
  145. package/node_modules/@murphai/assistant-engine/dist/assistant/delivery-service.d.ts +5 -0
  146. package/node_modules/@murphai/assistant-engine/dist/assistant/delivery-service.d.ts.map +1 -1
  147. package/node_modules/@murphai/assistant-engine/dist/assistant/delivery-service.js +9 -10
  148. package/node_modules/@murphai/assistant-engine/dist/assistant/delivery-service.js.map +1 -1
  149. package/node_modules/@murphai/assistant-engine/dist/assistant/execution-context.d.ts +41 -9
  150. package/node_modules/@murphai/assistant-engine/dist/assistant/execution-context.d.ts.map +1 -1
  151. package/node_modules/@murphai/assistant-engine/dist/assistant/execution-context.js +9 -0
  152. package/node_modules/@murphai/assistant-engine/dist/assistant/execution-context.js.map +1 -1
  153. package/node_modules/@murphai/assistant-engine/dist/assistant/experiment-support-automations.d.ts.map +1 -1
  154. package/node_modules/@murphai/assistant-engine/dist/assistant/experiment-support-automations.js +2 -0
  155. package/node_modules/@murphai/assistant-engine/dist/assistant/experiment-support-automations.js.map +1 -1
  156. package/node_modules/@murphai/assistant-engine/dist/assistant/hosted-tool-context.d.ts +3 -0
  157. package/node_modules/@murphai/assistant-engine/dist/assistant/hosted-tool-context.d.ts.map +1 -1
  158. package/node_modules/@murphai/assistant-engine/dist/assistant/hosted-tool-context.js +8 -0
  159. package/node_modules/@murphai/assistant-engine/dist/assistant/hosted-tool-context.js.map +1 -1
  160. package/node_modules/@murphai/assistant-engine/dist/assistant/issue-reporting.d.ts +3 -0
  161. package/node_modules/@murphai/assistant-engine/dist/assistant/issue-reporting.d.ts.map +1 -1
  162. package/node_modules/@murphai/assistant-engine/dist/assistant/issue-reporting.js +26 -1
  163. package/node_modules/@murphai/assistant-engine/dist/assistant/issue-reporting.js.map +1 -1
  164. package/node_modules/@murphai/assistant-engine/dist/assistant/local-service.d.ts.map +1 -1
  165. package/node_modules/@murphai/assistant-engine/dist/assistant/local-service.js +687 -370
  166. package/node_modules/@murphai/assistant-engine/dist/assistant/local-service.js.map +1 -1
  167. package/node_modules/@murphai/assistant-engine/dist/assistant/managed-automations.d.ts.map +1 -1
  168. package/node_modules/@murphai/assistant-engine/dist/assistant/managed-automations.js +54 -18
  169. package/node_modules/@murphai/assistant-engine/dist/assistant/managed-automations.js.map +1 -1
  170. package/node_modules/@murphai/assistant-engine/dist/assistant/model-behavior.d.ts +2 -3
  171. package/node_modules/@murphai/assistant-engine/dist/assistant/model-behavior.d.ts.map +1 -1
  172. package/node_modules/@murphai/assistant-engine/dist/assistant/model-behavior.js +10 -9
  173. package/node_modules/@murphai/assistant-engine/dist/assistant/model-behavior.js.map +1 -1
  174. package/node_modules/@murphai/assistant-engine/dist/assistant/notification-turn.d.ts +1 -1
  175. package/node_modules/@murphai/assistant-engine/dist/assistant/notification-turn.d.ts.map +1 -1
  176. package/node_modules/@murphai/assistant-engine/dist/assistant/notification-turn.js +146 -18
  177. package/node_modules/@murphai/assistant-engine/dist/assistant/notification-turn.js.map +1 -1
  178. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-followup-seed.d.ts +25 -0
  179. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-followup-seed.d.ts.map +1 -0
  180. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-followup-seed.js +72 -0
  181. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-followup-seed.js.map +1 -0
  182. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-state.d.ts +4 -0
  183. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-state.d.ts.map +1 -1
  184. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-state.js +14 -0
  185. package/node_modules/@murphai/assistant-engine/dist/assistant/onboarding-state.js.map +1 -1
  186. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox/store.d.ts +1 -1
  187. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox/store.d.ts.map +1 -1
  188. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox/store.js +8 -5
  189. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox/store.js.map +1 -1
  190. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox.d.ts +4 -0
  191. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox.d.ts.map +1 -1
  192. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox.js +8 -0
  193. package/node_modules/@murphai/assistant-engine/dist/assistant/outbox.js.map +1 -1
  194. package/node_modules/@murphai/assistant-engine/dist/assistant/private-completion-continuity.js +1 -0
  195. package/node_modules/@murphai/assistant-engine/dist/assistant/private-completion-continuity.js.map +1 -1
  196. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/codex-cli.d.ts.map +1 -1
  197. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/codex-cli.js +25 -2
  198. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/codex-cli.js.map +1 -1
  199. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/helpers.d.ts +11 -3
  200. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/helpers.d.ts.map +1 -1
  201. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/helpers.js +67 -176
  202. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/helpers.js.map +1 -1
  203. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/types.d.ts +17 -0
  204. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/types.d.ts.map +1 -1
  205. package/node_modules/@murphai/assistant-engine/dist/assistant/providers/types.js.map +1 -1
  206. package/node_modules/@murphai/assistant-engine/dist/assistant/reply-delivery-context.d.ts +1 -1
  207. package/node_modules/@murphai/assistant-engine/dist/assistant/reply-delivery-context.d.ts.map +1 -1
  208. package/node_modules/@murphai/assistant-engine/dist/assistant/reply-delivery-context.js +10 -0
  209. package/node_modules/@murphai/assistant-engine/dist/assistant/reply-delivery-context.js.map +1 -1
  210. package/node_modules/@murphai/assistant-engine/dist/assistant/runtime-residue.d.ts +19 -4
  211. package/node_modules/@murphai/assistant-engine/dist/assistant/runtime-residue.d.ts.map +1 -1
  212. package/node_modules/@murphai/assistant-engine/dist/assistant/runtime-residue.js +76 -40
  213. package/node_modules/@murphai/assistant-engine/dist/assistant/runtime-residue.js.map +1 -1
  214. package/node_modules/@murphai/assistant-engine/dist/assistant/service-contracts.d.ts +3 -1
  215. package/node_modules/@murphai/assistant-engine/dist/assistant/service-contracts.d.ts.map +1 -1
  216. package/node_modules/@murphai/assistant-engine/dist/assistant/service-usage.d.ts +4 -2
  217. package/node_modules/@murphai/assistant-engine/dist/assistant/service-usage.d.ts.map +1 -1
  218. package/node_modules/@murphai/assistant-engine/dist/assistant/service-usage.js +8 -7
  219. package/node_modules/@murphai/assistant-engine/dist/assistant/service-usage.js.map +1 -1
  220. package/node_modules/@murphai/assistant-engine/dist/assistant/shared.d.ts +1 -1
  221. package/node_modules/@murphai/assistant-engine/dist/assistant/shared.d.ts.map +1 -1
  222. package/node_modules/@murphai/assistant-engine/dist/assistant/shared.js +1 -1
  223. package/node_modules/@murphai/assistant-engine/dist/assistant/shared.js.map +1 -1
  224. package/node_modules/@murphai/assistant-engine/dist/assistant/store/types.d.ts +1 -0
  225. package/node_modules/@murphai/assistant-engine/dist/assistant/store/types.d.ts.map +1 -1
  226. package/node_modules/@murphai/assistant-engine/dist/assistant/store.d.ts.map +1 -1
  227. package/node_modules/@murphai/assistant-engine/dist/assistant/store.js +6 -0
  228. package/node_modules/@murphai/assistant-engine/dist/assistant/store.js.map +1 -1
  229. package/node_modules/@murphai/assistant-engine/dist/assistant/system-prompt.d.ts +3 -0
  230. package/node_modules/@murphai/assistant-engine/dist/assistant/system-prompt.d.ts.map +1 -1
  231. package/node_modules/@murphai/assistant-engine/dist/assistant/system-prompt.js +57 -29
  232. package/node_modules/@murphai/assistant-engine/dist/assistant/system-prompt.js.map +1 -1
  233. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-digest.d.ts +1 -0
  234. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-digest.d.ts.map +1 -1
  235. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-digest.js +17 -3
  236. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-digest.js.map +1 -1
  237. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-feedback.d.ts +4 -7
  238. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-feedback.d.ts.map +1 -1
  239. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-feedback.js +21 -6
  240. package/node_modules/@murphai/assistant-engine/dist/assistant/tool-validation-feedback.js.map +1 -1
  241. package/node_modules/@murphai/assistant-engine/dist/assistant/turn-finalizer.d.ts +1 -0
  242. package/node_modules/@murphai/assistant-engine/dist/assistant/turn-finalizer.d.ts.map +1 -1
  243. package/node_modules/@murphai/assistant-engine/dist/assistant/turn-finalizer.js +6 -0
  244. package/node_modules/@murphai/assistant-engine/dist/assistant/turn-finalizer.js.map +1 -1
  245. package/node_modules/@murphai/assistant-engine/dist/assistant/turn-input.d.ts +1 -1
  246. package/node_modules/@murphai/assistant-engine/dist/assistant/turn-input.d.ts.map +1 -1
  247. package/node_modules/@murphai/assistant-engine/dist/assistant/usage-attribution.d.ts +1 -1
  248. package/node_modules/@murphai/assistant-engine/dist/assistant/usage-attribution.d.ts.map +1 -1
  249. package/node_modules/@murphai/assistant-engine/dist/assistant/usage-attribution.js.map +1 -1
  250. package/node_modules/@murphai/assistant-engine/dist/assistant-ask.js +8 -0
  251. package/node_modules/@murphai/assistant-engine/dist/assistant-ask.js.map +1 -1
  252. package/node_modules/@murphai/assistant-engine/dist/assistant-cli-access.d.ts.map +1 -1
  253. package/node_modules/@murphai/assistant-engine/dist/assistant-cli-access.js +1 -0
  254. package/node_modules/@murphai/assistant-engine/dist/assistant-cli-access.js.map +1 -1
  255. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/analyze-video-tool.d.ts +49 -0
  256. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/analyze-video-tool.d.ts.map +1 -0
  257. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/analyze-video-tool.js +387 -0
  258. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/analyze-video-tool.js.map +1 -0
  259. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-protocol.d.ts +1 -1
  260. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-protocol.d.ts.map +1 -1
  261. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-protocol.js +12 -13
  262. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-protocol.js.map +1 -1
  263. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-requests.d.ts +1 -0
  264. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-requests.d.ts.map +1 -1
  265. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-requests.js +16 -5
  266. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-requests.js.map +1 -1
  267. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-rpc.d.ts +1 -1
  268. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-rpc.d.ts.map +1 -1
  269. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-rpc.js +2 -1
  270. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/app-server-rpc.js.map +1 -1
  271. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/command-family.d.ts +4 -0
  272. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/command-family.d.ts.map +1 -1
  273. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/command-family.js +26 -13
  274. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/command-family.js.map +1 -1
  275. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tool-catalog.d.ts +1024 -1091
  276. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tool-catalog.d.ts.map +1 -1
  277. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tool-catalog.js +354 -330
  278. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tool-catalog.js.map +1 -1
  279. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/analyze-video.d.ts +50 -0
  280. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/analyze-video.d.ts.map +1 -0
  281. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/analyze-video.js +79 -0
  282. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/analyze-video.js.map +1 -0
  283. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation-model-input-schema.d.ts +4 -0
  284. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation-model-input-schema.d.ts.map +1 -0
  285. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation-model-input-schema.js +153 -0
  286. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation-model-input-schema.js.map +1 -0
  287. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation.d.ts +4 -1
  288. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation.d.ts.map +1 -1
  289. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation.js +29 -35
  290. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/automation.js.map +1 -1
  291. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/device.d.ts +4 -1
  292. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/device.d.ts.map +1 -1
  293. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/device.js +43 -9
  294. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/device.js.map +1 -1
  295. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/dynamic-tool-wrapper.d.ts +6 -0
  296. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/dynamic-tool-wrapper.d.ts.map +1 -1
  297. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/dynamic-tool-wrapper.js.map +1 -1
  298. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/member-memory.d.ts +60 -0
  299. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/member-memory.d.ts.map +1 -0
  300. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/member-memory.js +91 -0
  301. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/member-memory.js.map +1 -0
  302. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/physical-notes.d.ts +41 -1
  303. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/physical-notes.d.ts.map +1 -1
  304. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/physical-notes.js +85 -9
  305. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools/physical-notes.js.map +1 -1
  306. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools.d.ts +25 -2
  307. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools.d.ts.map +1 -1
  308. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools.js +280 -89
  309. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/dynamic-tools.js.map +1 -1
  310. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/failures.js +1 -1
  311. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/response-card-validation-feedback.d.ts.map +1 -1
  312. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/response-card-validation-feedback.js +1 -3
  313. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/response-card-validation-feedback.js.map +1 -1
  314. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/workout-delivery-context.d.ts +19 -0
  315. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/workout-delivery-context.d.ts.map +1 -0
  316. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/workout-delivery-context.js +203 -0
  317. package/node_modules/@murphai/assistant-engine/dist/assistant-codex/workout-delivery-context.js.map +1 -0
  318. package/node_modules/@murphai/assistant-engine/dist/assistant-codex.d.ts +9 -1
  319. package/node_modules/@murphai/assistant-engine/dist/assistant-codex.d.ts.map +1 -1
  320. package/node_modules/@murphai/assistant-engine/dist/assistant-codex.js +220 -130
  321. package/node_modules/@murphai/assistant-engine/dist/assistant-codex.js.map +1 -1
  322. package/node_modules/@murphai/assistant-engine/dist/assistant-runtime-residue.d.ts +1 -1
  323. package/node_modules/@murphai/assistant-engine/dist/assistant-runtime-residue.d.ts.map +1 -1
  324. package/node_modules/@murphai/assistant-engine/dist/assistant-runtime-residue.js +1 -1
  325. package/node_modules/@murphai/assistant-engine/dist/assistant-runtime-residue.js.map +1 -1
  326. package/node_modules/@murphai/assistant-engine/dist/assistant-skill-assets.d.ts +1 -1
  327. package/node_modules/@murphai/assistant-engine/dist/assistant-skill-assets.d.ts.map +1 -1
  328. package/node_modules/@murphai/assistant-engine/dist/assistant-skill-assets.js +1 -1
  329. package/node_modules/@murphai/assistant-engine/dist/assistant-skill-assets.js.map +1 -1
  330. package/node_modules/@murphai/assistant-engine/dist/index.d.ts +2 -1
  331. package/node_modules/@murphai/assistant-engine/dist/index.d.ts.map +1 -1
  332. package/node_modules/@murphai/assistant-engine/dist/index.js +1 -0
  333. package/node_modules/@murphai/assistant-engine/dist/index.js.map +1 -1
  334. package/node_modules/@murphai/assistant-engine/skills/automatic-meal-capture/SKILL.md +111 -68
  335. package/node_modules/@murphai/assistant-engine/skills/behavior-followthrough/SKILL.md +0 -2
  336. package/node_modules/@murphai/assistant-engine/skills/daily-activity/SKILL.md +11 -4
  337. package/node_modules/@murphai/assistant-engine/skills/food-journal/SKILL.md +83 -22
  338. package/node_modules/@murphai/assistant-engine/skills/hosted-low-usage/SKILL.md +3 -3
  339. package/node_modules/@murphai/assistant-engine/skills/murph-family/SKILL.md +1 -1
  340. package/node_modules/@murphai/assistant-engine/skills/nutrition-strategy/SKILL.md +17 -11
  341. package/node_modules/@murphai/assistant-engine/skills/nutrition-strategy/references/daily-nutrition-card-goals.md +62 -34
  342. package/node_modules/@murphai/assistant-engine/skills/physical-notes/SKILL.md +47 -0
  343. package/node_modules/@murphai/assistant-engine/skills/shared/exercise-catalog-runtime.md +6 -0
  344. package/node_modules/@murphai/assistant-engine/skills/signup-link/SKILL.md +1 -1
  345. package/node_modules/@murphai/assistant-engine/skills/strength-training/SKILL.md +12 -5
  346. package/node_modules/@murphai/assistant-engine/skills/tracked-table/SKILL.md +156 -66
  347. package/node_modules/@murphai/core/dist/automation.d.ts.map +1 -1
  348. package/node_modules/@murphai/core/dist/automation.js +17 -9
  349. package/node_modules/@murphai/core/dist/automation.js.map +1 -1
  350. package/node_modules/@murphai/core/dist/bank/habitat.d.ts +2 -0
  351. package/node_modules/@murphai/core/dist/bank/habitat.d.ts.map +1 -1
  352. package/node_modules/@murphai/core/dist/bank/habitat.js +27 -0
  353. package/node_modules/@murphai/core/dist/bank/habitat.js.map +1 -1
  354. package/node_modules/@murphai/core/dist/bank/regimens.js +1 -1
  355. package/node_modules/@murphai/core/dist/bank/regimens.js.map +1 -1
  356. package/node_modules/@murphai/core/dist/domains/events/ledger.d.ts +21 -0
  357. package/node_modules/@murphai/core/dist/domains/events/ledger.d.ts.map +1 -1
  358. package/node_modules/@murphai/core/dist/domains/events/ledger.js +102 -8
  359. package/node_modules/@murphai/core/dist/domains/events/ledger.js.map +1 -1
  360. package/node_modules/@murphai/core/dist/domains/events.d.ts +2 -2
  361. package/node_modules/@murphai/core/dist/domains/events.d.ts.map +1 -1
  362. package/node_modules/@murphai/core/dist/domains/events.js +1 -1
  363. package/node_modules/@murphai/core/dist/domains/events.js.map +1 -1
  364. package/node_modules/@murphai/core/dist/history/api.d.ts.map +1 -1
  365. package/node_modules/@murphai/core/dist/history/api.js +18 -3
  366. package/node_modules/@murphai/core/dist/history/api.js.map +1 -1
  367. package/node_modules/@murphai/core/dist/index.d.ts +4 -2
  368. package/node_modules/@murphai/core/dist/index.d.ts.map +1 -1
  369. package/node_modules/@murphai/core/dist/index.js +3 -1
  370. package/node_modules/@murphai/core/dist/index.js.map +1 -1
  371. package/node_modules/@murphai/core/dist/mutations.d.ts +25 -2
  372. package/node_modules/@murphai/core/dist/mutations.d.ts.map +1 -1
  373. package/node_modules/@murphai/core/dist/mutations.js +1588 -78
  374. package/node_modules/@murphai/core/dist/mutations.js.map +1 -1
  375. package/node_modules/@murphai/core/dist/operations/index.d.ts +1 -1
  376. package/node_modules/@murphai/core/dist/operations/index.d.ts.map +1 -1
  377. package/node_modules/@murphai/core/dist/operations/index.js +1 -1
  378. package/node_modules/@murphai/core/dist/operations/index.js.map +1 -1
  379. package/node_modules/@murphai/core/dist/operations/write-batch.d.ts +2 -0
  380. package/node_modules/@murphai/core/dist/operations/write-batch.d.ts.map +1 -1
  381. package/node_modules/@murphai/core/dist/operations/write-batch.js +7 -0
  382. package/node_modules/@murphai/core/dist/operations/write-batch.js.map +1 -1
  383. package/node_modules/@murphai/core/dist/protocols.d.ts.map +1 -1
  384. package/node_modules/@murphai/core/dist/protocols.js +52 -9
  385. package/node_modules/@murphai/core/dist/protocols.js.map +1 -1
  386. package/node_modules/@murphai/core/dist/public-mutations.d.ts +1 -1
  387. package/node_modules/@murphai/core/dist/public-mutations.d.ts.map +1 -1
  388. package/node_modules/@murphai/core/dist/public-mutations.js +2 -2
  389. package/node_modules/@murphai/core/dist/public-mutations.js.map +1 -1
  390. package/node_modules/@murphai/core/dist/scheduled-logs.d.ts +3 -0
  391. package/node_modules/@murphai/core/dist/scheduled-logs.d.ts.map +1 -1
  392. package/node_modules/@murphai/core/dist/scheduled-logs.js +65 -29
  393. package/node_modules/@murphai/core/dist/scheduled-logs.js.map +1 -1
  394. package/node_modules/@murphai/device-syncd/README.md +140 -46
  395. package/node_modules/@murphai/device-syncd/dist/client.d.ts +14 -0
  396. package/node_modules/@murphai/device-syncd/dist/client.d.ts.map +1 -1
  397. package/node_modules/@murphai/device-syncd/dist/client.js.map +1 -1
  398. package/node_modules/@murphai/device-syncd/dist/config/connect-routes.d.ts +4 -1
  399. package/node_modules/@murphai/device-syncd/dist/config/connect-routes.d.ts.map +1 -1
  400. package/node_modules/@murphai/device-syncd/dist/config/connect-routes.js +24 -2
  401. package/node_modules/@murphai/device-syncd/dist/config/connect-routes.js.map +1 -1
  402. package/node_modules/@murphai/device-syncd/dist/config/junction-connect-sources.js +2 -2
  403. package/node_modules/@murphai/device-syncd/dist/config/junction-connect-sources.js.map +1 -1
  404. package/node_modules/@murphai/device-syncd/dist/config/provider-manifests.d.ts +53 -0
  405. package/node_modules/@murphai/device-syncd/dist/config/provider-manifests.d.ts.map +1 -1
  406. package/node_modules/@murphai/device-syncd/dist/config/provider-manifests.js +13 -0
  407. package/node_modules/@murphai/device-syncd/dist/config/provider-manifests.js.map +1 -1
  408. package/node_modules/@murphai/device-syncd/dist/connect-config.d.ts +1 -1
  409. package/node_modules/@murphai/device-syncd/dist/connect-config.d.ts.map +1 -1
  410. package/node_modules/@murphai/device-syncd/dist/connect-config.js +1 -1
  411. package/node_modules/@murphai/device-syncd/dist/connect-config.js.map +1 -1
  412. package/node_modules/@murphai/device-syncd/dist/fitbit-migration.d.ts +57 -0
  413. package/node_modules/@murphai/device-syncd/dist/fitbit-migration.d.ts.map +1 -0
  414. package/node_modules/@murphai/device-syncd/dist/fitbit-migration.js +166 -0
  415. package/node_modules/@murphai/device-syncd/dist/fitbit-migration.js.map +1 -0
  416. package/node_modules/@murphai/device-syncd/dist/hosted-runtime.d.ts +43 -0
  417. package/node_modules/@murphai/device-syncd/dist/hosted-runtime.d.ts.map +1 -1
  418. package/node_modules/@murphai/device-syncd/dist/hosted-runtime.js +129 -5
  419. package/node_modules/@murphai/device-syncd/dist/hosted-runtime.js.map +1 -1
  420. package/node_modules/@murphai/device-syncd/dist/junction-historical-backfill-progress.d.ts +3 -0
  421. package/node_modules/@murphai/device-syncd/dist/junction-historical-backfill-progress.d.ts.map +1 -1
  422. package/node_modules/@murphai/device-syncd/dist/junction-historical-backfill-progress.js +36 -16
  423. package/node_modules/@murphai/device-syncd/dist/junction-historical-backfill-progress.js.map +1 -1
  424. package/node_modules/@murphai/device-syncd/dist/junction-inline-authority.d.ts +17 -2
  425. package/node_modules/@murphai/device-syncd/dist/junction-inline-authority.d.ts.map +1 -1
  426. package/node_modules/@murphai/device-syncd/dist/junction-inline-authority.js +99 -40
  427. package/node_modules/@murphai/device-syncd/dist/junction-inline-authority.js.map +1 -1
  428. package/node_modules/@murphai/device-syncd/dist/junction-source-reconnect.d.ts +17 -0
  429. package/node_modules/@murphai/device-syncd/dist/junction-source-reconnect.d.ts.map +1 -0
  430. package/node_modules/@murphai/device-syncd/dist/junction-source-reconnect.js +45 -0
  431. package/node_modules/@murphai/device-syncd/dist/junction-source-reconnect.js.map +1 -0
  432. package/node_modules/@murphai/device-syncd/dist/junction-timeseries-progress.d.ts +8 -1
  433. package/node_modules/@murphai/device-syncd/dist/junction-timeseries-progress.d.ts.map +1 -1
  434. package/node_modules/@murphai/device-syncd/dist/junction-timeseries-progress.js +12 -1
  435. package/node_modules/@murphai/device-syncd/dist/junction-timeseries-progress.js.map +1 -1
  436. package/node_modules/@murphai/device-syncd/dist/providers/junction-client.d.ts +7 -0
  437. package/node_modules/@murphai/device-syncd/dist/providers/junction-client.d.ts.map +1 -1
  438. package/node_modules/@murphai/device-syncd/dist/providers/junction-client.js +285 -46
  439. package/node_modules/@murphai/device-syncd/dist/providers/junction-client.js.map +1 -1
  440. package/node_modules/@murphai/device-syncd/dist/providers/junction.d.ts +9 -0
  441. package/node_modules/@murphai/device-syncd/dist/providers/junction.d.ts.map +1 -1
  442. package/node_modules/@murphai/device-syncd/dist/providers/junction.js +2045 -367
  443. package/node_modules/@murphai/device-syncd/dist/providers/junction.js.map +1 -1
  444. package/node_modules/@murphai/device-syncd/dist/public-account.d.ts +2 -0
  445. package/node_modules/@murphai/device-syncd/dist/public-account.d.ts.map +1 -1
  446. package/node_modules/@murphai/device-syncd/dist/public-account.js +96 -0
  447. package/node_modules/@murphai/device-syncd/dist/public-account.js.map +1 -1
  448. package/node_modules/@murphai/device-syncd/dist/public-ingress.d.ts.map +1 -1
  449. package/node_modules/@murphai/device-syncd/dist/public-ingress.js +6 -4
  450. package/node_modules/@murphai/device-syncd/dist/public-ingress.js.map +1 -1
  451. package/node_modules/@murphai/device-syncd/dist/service.d.ts +6 -2
  452. package/node_modules/@murphai/device-syncd/dist/service.d.ts.map +1 -1
  453. package/node_modules/@murphai/device-syncd/dist/service.js +385 -44
  454. package/node_modules/@murphai/device-syncd/dist/service.js.map +1 -1
  455. package/node_modules/@murphai/device-syncd/dist/source-staleness.d.ts +15 -0
  456. package/node_modules/@murphai/device-syncd/dist/source-staleness.d.ts.map +1 -1
  457. package/node_modules/@murphai/device-syncd/dist/source-staleness.js +25 -1
  458. package/node_modules/@murphai/device-syncd/dist/source-staleness.js.map +1 -1
  459. package/node_modules/@murphai/device-syncd/dist/store/jobs.d.ts +10 -1
  460. package/node_modules/@murphai/device-syncd/dist/store/jobs.d.ts.map +1 -1
  461. package/node_modules/@murphai/device-syncd/dist/store/jobs.js +95 -1
  462. package/node_modules/@murphai/device-syncd/dist/store/jobs.js.map +1 -1
  463. package/node_modules/@murphai/device-syncd/dist/store/schema.d.ts +1 -1
  464. package/node_modules/@murphai/device-syncd/dist/store/schema.d.ts.map +1 -1
  465. package/node_modules/@murphai/device-syncd/dist/store/schema.js +25 -3
  466. package/node_modules/@murphai/device-syncd/dist/store/schema.js.map +1 -1
  467. package/node_modules/@murphai/device-syncd/dist/store/sources.d.ts +24 -2
  468. package/node_modules/@murphai/device-syncd/dist/store/sources.d.ts.map +1 -1
  469. package/node_modules/@murphai/device-syncd/dist/store/sources.js +223 -14
  470. package/node_modules/@murphai/device-syncd/dist/store/sources.js.map +1 -1
  471. package/node_modules/@murphai/device-syncd/dist/store.d.ts +13 -3
  472. package/node_modules/@murphai/device-syncd/dist/store.d.ts.map +1 -1
  473. package/node_modules/@murphai/device-syncd/dist/store.js +104 -22
  474. package/node_modules/@murphai/device-syncd/dist/store.js.map +1 -1
  475. package/node_modules/@murphai/device-syncd/dist/types.d.ts +96 -9
  476. package/node_modules/@murphai/device-syncd/dist/types.d.ts.map +1 -1
  477. package/node_modules/@murphai/device-syncd/dist/types.js +4 -0
  478. package/node_modules/@murphai/device-syncd/dist/types.js.map +1 -1
  479. package/node_modules/@murphai/device-syncd/package.json +8 -0
  480. package/node_modules/@murphai/health-metrics/dist/definitions/activity.d.ts.map +1 -1
  481. package/node_modules/@murphai/health-metrics/dist/definitions/activity.js +66 -0
  482. package/node_modules/@murphai/health-metrics/dist/definitions/activity.js.map +1 -1
  483. package/node_modules/@murphai/health-metrics/dist/definitions/body.js +2 -2
  484. package/node_modules/@murphai/health-metrics/dist/definitions/body.js.map +1 -1
  485. package/node_modules/@murphai/health-metrics/dist/normalize.d.ts.map +1 -1
  486. package/node_modules/@murphai/health-metrics/dist/normalize.js +3 -0
  487. package/node_modules/@murphai/health-metrics/dist/normalize.js.map +1 -1
  488. package/node_modules/@murphai/health-metrics/dist/wearable-metric-catalog.d.ts +3 -1
  489. package/node_modules/@murphai/health-metrics/dist/wearable-metric-catalog.d.ts.map +1 -1
  490. package/node_modules/@murphai/health-metrics/dist/wearable-metric-catalog.js +5 -1
  491. package/node_modules/@murphai/health-metrics/dist/wearable-metric-catalog.js.map +1 -1
  492. package/node_modules/@murphai/health-metrics/dist/wearable-provider-catalog.d.ts +79 -0
  493. package/node_modules/@murphai/health-metrics/dist/wearable-provider-catalog.d.ts.map +1 -1
  494. package/node_modules/@murphai/health-metrics/dist/wearable-provider-catalog.js +19 -0
  495. package/node_modules/@murphai/health-metrics/dist/wearable-provider-catalog.js.map +1 -1
  496. package/node_modules/@murphai/importers/dist/core-port.d.ts +15 -1
  497. package/node_modules/@murphai/importers/dist/core-port.d.ts.map +1 -1
  498. package/node_modules/@murphai/importers/dist/core-port.js.map +1 -1
  499. package/node_modules/@murphai/importers/dist/create-importers.d.ts +9 -1
  500. package/node_modules/@murphai/importers/dist/create-importers.d.ts.map +1 -1
  501. package/node_modules/@murphai/importers/dist/create-importers.js +12 -2
  502. package/node_modules/@murphai/importers/dist/create-importers.js.map +1 -1
  503. package/node_modules/@murphai/importers/dist/device-providers/import-device-provider-snapshot.d.ts +17 -3
  504. package/node_modules/@murphai/importers/dist/device-providers/import-device-provider-snapshot.d.ts.map +1 -1
  505. package/node_modules/@murphai/importers/dist/device-providers/import-device-provider-snapshot.js +102 -8
  506. package/node_modules/@murphai/importers/dist/device-providers/import-device-provider-snapshot.js.map +1 -1
  507. package/node_modules/@murphai/importers/dist/device-providers/index.d.ts +5 -4
  508. package/node_modules/@murphai/importers/dist/device-providers/index.d.ts.map +1 -1
  509. package/node_modules/@murphai/importers/dist/device-providers/index.js +4 -3
  510. package/node_modules/@murphai/importers/dist/device-providers/index.js.map +1 -1
  511. package/node_modules/@murphai/importers/dist/device-providers/junction-bounded-features.d.ts +11 -1
  512. package/node_modules/@murphai/importers/dist/device-providers/junction-bounded-features.d.ts.map +1 -1
  513. package/node_modules/@murphai/importers/dist/device-providers/junction-bounded-features.js +420 -27
  514. package/node_modules/@murphai/importers/dist/device-providers/junction-bounded-features.js.map +1 -1
  515. package/node_modules/@murphai/importers/dist/device-providers/junction-origin.d.ts +1 -0
  516. package/node_modules/@murphai/importers/dist/device-providers/junction-origin.d.ts.map +1 -1
  517. package/node_modules/@murphai/importers/dist/device-providers/junction-origin.js +7 -2
  518. package/node_modules/@murphai/importers/dist/device-providers/junction-origin.js.map +1 -1
  519. package/node_modules/@murphai/importers/dist/device-providers/junction-resources.d.ts +4 -1
  520. package/node_modules/@murphai/importers/dist/device-providers/junction-resources.d.ts.map +1 -1
  521. package/node_modules/@murphai/importers/dist/device-providers/junction-resources.js +42 -1
  522. package/node_modules/@murphai/importers/dist/device-providers/junction-resources.js.map +1 -1
  523. package/node_modules/@murphai/importers/dist/device-providers/junction-timeseries-features.d.ts +79 -0
  524. package/node_modules/@murphai/importers/dist/device-providers/junction-timeseries-features.d.ts.map +1 -0
  525. package/node_modules/@murphai/importers/dist/device-providers/junction-timeseries-features.js +356 -0
  526. package/node_modules/@murphai/importers/dist/device-providers/junction-timeseries-features.js.map +1 -0
  527. package/node_modules/@murphai/importers/dist/device-providers/junction.d.ts +76 -3
  528. package/node_modules/@murphai/importers/dist/device-providers/junction.d.ts.map +1 -1
  529. package/node_modules/@murphai/importers/dist/device-providers/junction.js +1676 -347
  530. package/node_modules/@murphai/importers/dist/device-providers/junction.js.map +1 -1
  531. package/node_modules/@murphai/importers/dist/device-providers/shared-normalization.d.ts.map +1 -1
  532. package/node_modules/@murphai/importers/dist/device-providers/shared-normalization.js +1 -0
  533. package/node_modules/@murphai/importers/dist/device-providers/shared-normalization.js.map +1 -1
  534. package/node_modules/@murphai/importers/dist/device-providers/types.d.ts +9 -1
  535. package/node_modules/@murphai/importers/dist/device-providers/types.d.ts.map +1 -1
  536. package/node_modules/@murphai/importers/dist/index.d.ts +2 -2
  537. package/node_modules/@murphai/importers/dist/index.d.ts.map +1 -1
  538. package/node_modules/@murphai/importers/dist/index.js +1 -1
  539. package/node_modules/@murphai/importers/dist/index.js.map +1 -1
  540. package/node_modules/@murphai/inboxd/README.md +1 -1
  541. package/node_modules/@murphai/inboxd/dist/indexing/retention.d.ts +6 -0
  542. package/node_modules/@murphai/inboxd/dist/indexing/retention.d.ts.map +1 -1
  543. package/node_modules/@murphai/inboxd/dist/indexing/retention.js +64 -18
  544. package/node_modules/@murphai/inboxd/dist/indexing/retention.js.map +1 -1
  545. package/node_modules/@murphai/inboxd/dist/retention.d.ts +1 -1
  546. package/node_modules/@murphai/inboxd/dist/retention.d.ts.map +1 -1
  547. package/node_modules/@murphai/inboxd/dist/retention.js +1 -1
  548. package/node_modules/@murphai/inboxd/dist/retention.js.map +1 -1
  549. package/node_modules/@murphai/operator-config/dist/assistant-cli-contracts.d.ts +1 -0
  550. package/node_modules/@murphai/operator-config/dist/assistant-cli-contracts.d.ts.map +1 -1
  551. package/node_modules/@murphai/operator-config/dist/assistant-cli-contracts.js +5 -0
  552. package/node_modules/@murphai/operator-config/dist/assistant-cli-contracts.js.map +1 -1
  553. package/node_modules/@murphai/operator-config/dist/assistant-response-cards.d.ts +175 -152
  554. package/node_modules/@murphai/operator-config/dist/assistant-response-cards.d.ts.map +1 -1
  555. package/node_modules/@murphai/operator-config/dist/assistant-response-cards.js +62 -41
  556. package/node_modules/@murphai/operator-config/dist/assistant-response-cards.js.map +1 -1
  557. package/node_modules/@murphai/operator-config/dist/device-sync-client.d.ts.map +1 -1
  558. package/node_modules/@murphai/operator-config/dist/device-sync-client.js +1 -5
  559. package/node_modules/@murphai/operator-config/dist/device-sync-client.js.map +1 -1
  560. package/node_modules/@murphai/operator-config/dist/vault-cli-contracts.d.ts +66 -0
  561. package/node_modules/@murphai/operator-config/dist/vault-cli-contracts.d.ts.map +1 -1
  562. package/node_modules/@murphai/operator-config/dist/vault-cli-contracts.js +58 -0
  563. package/node_modules/@murphai/operator-config/dist/vault-cli-contracts.js.map +1 -1
  564. package/node_modules/@murphai/operator-config/dist/vault-cli-error-projection.d.ts +24 -0
  565. package/node_modules/@murphai/operator-config/dist/vault-cli-error-projection.d.ts.map +1 -0
  566. package/node_modules/@murphai/operator-config/dist/vault-cli-error-projection.js +219 -0
  567. package/node_modules/@murphai/operator-config/dist/vault-cli-error-projection.js.map +1 -0
  568. package/node_modules/@murphai/operator-config/dist/vault-cli-errors.d.ts.map +1 -1
  569. package/node_modules/@murphai/operator-config/dist/vault-cli-errors.js.map +1 -1
  570. package/node_modules/@murphai/operator-config/dist/workout-action-binding.d.ts +9 -0
  571. package/node_modules/@murphai/operator-config/dist/workout-action-binding.d.ts.map +1 -1
  572. package/node_modules/@murphai/operator-config/dist/workout-action-binding.js +48 -5
  573. package/node_modules/@murphai/operator-config/dist/workout-action-binding.js.map +1 -1
  574. package/node_modules/@murphai/operator-config/package.json +5 -0
  575. package/node_modules/@murphai/query/README.md +20 -5
  576. package/node_modules/@murphai/query/dist/browser-replica/build.d.ts +1 -1
  577. package/node_modules/@murphai/query/dist/browser-replica/build.d.ts.map +1 -1
  578. package/node_modules/@murphai/query/dist/browser-replica/build.js +25 -16
  579. package/node_modules/@murphai/query/dist/browser-replica/build.js.map +1 -1
  580. package/node_modules/@murphai/query/dist/browser-replica/json.d.ts +6 -0
  581. package/node_modules/@murphai/query/dist/browser-replica/json.d.ts.map +1 -0
  582. package/node_modules/@murphai/query/dist/browser-replica/json.js +103 -0
  583. package/node_modules/@murphai/query/dist/browser-replica/json.js.map +1 -0
  584. package/node_modules/@murphai/query/dist/browser-replica/shared.d.ts +1 -0
  585. package/node_modules/@murphai/query/dist/browser-replica/shared.d.ts.map +1 -1
  586. package/node_modules/@murphai/query/dist/browser-replica/source.d.ts +13 -0
  587. package/node_modules/@murphai/query/dist/browser-replica/source.d.ts.map +1 -0
  588. package/node_modules/@murphai/query/dist/browser-replica/source.js +45 -0
  589. package/node_modules/@murphai/query/dist/browser-replica/source.js.map +1 -0
  590. package/node_modules/@murphai/query/dist/browser-replica-server.d.ts +2 -0
  591. package/node_modules/@murphai/query/dist/browser-replica-server.d.ts.map +1 -1
  592. package/node_modules/@murphai/query/dist/browser-replica-server.js +2 -0
  593. package/node_modules/@murphai/query/dist/browser-replica-server.js.map +1 -1
  594. package/node_modules/@murphai/query/dist/health/blood-tests.d.ts.map +1 -1
  595. package/node_modules/@murphai/query/dist/health/blood-tests.js +3 -2
  596. package/node_modules/@murphai/query/dist/health/blood-tests.js.map +1 -1
  597. package/node_modules/@murphai/query/dist/health/canonical-collector.d.ts +11 -1
  598. package/node_modules/@murphai/query/dist/health/canonical-collector.d.ts.map +1 -1
  599. package/node_modules/@murphai/query/dist/health/canonical-collector.js +39 -16
  600. package/node_modules/@murphai/query/dist/health/canonical-collector.js.map +1 -1
  601. package/node_modules/@murphai/query/dist/health/entity-slices.d.ts +9 -3
  602. package/node_modules/@murphai/query/dist/health/entity-slices.d.ts.map +1 -1
  603. package/node_modules/@murphai/query/dist/health/entity-slices.js +6 -6
  604. package/node_modules/@murphai/query/dist/health/entity-slices.js.map +1 -1
  605. package/node_modules/@murphai/query/dist/health/immunizations.d.ts.map +1 -1
  606. package/node_modules/@murphai/query/dist/health/immunizations.js +14 -11
  607. package/node_modules/@murphai/query/dist/health/immunizations.js.map +1 -1
  608. package/node_modules/@murphai/query/dist/health/loaders.d.ts +21 -7
  609. package/node_modules/@murphai/query/dist/health/loaders.d.ts.map +1 -1
  610. package/node_modules/@murphai/query/dist/health/loaders.js +53 -15
  611. package/node_modules/@murphai/query/dist/health/loaders.js.map +1 -1
  612. package/node_modules/@murphai/query/dist/health/registries.d.ts +1 -0
  613. package/node_modules/@murphai/query/dist/health/registries.d.ts.map +1 -1
  614. package/node_modules/@murphai/query/dist/health/registries.js.map +1 -1
  615. package/node_modules/@murphai/query/dist/index.d.ts +4 -3
  616. package/node_modules/@murphai/query/dist/index.d.ts.map +1 -1
  617. package/node_modules/@murphai/query/dist/index.js +4 -3
  618. package/node_modules/@murphai/query/dist/index.js.map +1 -1
  619. package/node_modules/@murphai/query/dist/meal-nutrition.d.ts +1 -1
  620. package/node_modules/@murphai/query/dist/meal-nutrition.d.ts.map +1 -1
  621. package/node_modules/@murphai/query/dist/meal-nutrition.js +10 -3
  622. package/node_modules/@murphai/query/dist/meal-nutrition.js.map +1 -1
  623. package/node_modules/@murphai/query/dist/metrics/projection.d.ts.map +1 -1
  624. package/node_modules/@murphai/query/dist/metrics/projection.js +19 -6
  625. package/node_modules/@murphai/query/dist/metrics/projection.js.map +1 -1
  626. package/node_modules/@murphai/query/dist/murph-age.d.ts +5 -1
  627. package/node_modules/@murphai/query/dist/murph-age.d.ts.map +1 -1
  628. package/node_modules/@murphai/query/dist/murph-age.js +22 -8
  629. package/node_modules/@murphai/query/dist/murph-age.js.map +1 -1
  630. package/node_modules/@murphai/query/dist/projection/entity-store.js +4 -4
  631. package/node_modules/@murphai/query/dist/projection/entity-store.js.map +1 -1
  632. package/node_modules/@murphai/query/dist/projection/schema.d.ts +1 -1
  633. package/node_modules/@murphai/query/dist/projection/schema.d.ts.map +1 -1
  634. package/node_modules/@murphai/query/dist/projection/schema.js +3 -1
  635. package/node_modules/@murphai/query/dist/projection/schema.js.map +1 -1
  636. package/node_modules/@murphai/query/dist/projection/wearable-summary-compose.js +4 -0
  637. package/node_modules/@murphai/query/dist/projection/wearable-summary-compose.js.map +1 -1
  638. package/node_modules/@murphai/query/dist/projection/wearable-summary-projector.js +4 -0
  639. package/node_modules/@murphai/query/dist/projection/wearable-summary-projector.js.map +1 -1
  640. package/node_modules/@murphai/query/dist/query-visibility.d.ts +5 -0
  641. package/node_modules/@murphai/query/dist/query-visibility.d.ts.map +1 -1
  642. package/node_modules/@murphai/query/dist/query-visibility.js +11 -5
  643. package/node_modules/@murphai/query/dist/query-visibility.js.map +1 -1
  644. package/node_modules/@murphai/query/dist/read-model.d.ts +8 -0
  645. package/node_modules/@murphai/query/dist/read-model.d.ts.map +1 -1
  646. package/node_modules/@murphai/query/dist/read-model.js +11 -1
  647. package/node_modules/@murphai/query/dist/read-model.js.map +1 -1
  648. package/node_modules/@murphai/query/dist/scheduled-logs.d.ts.map +1 -1
  649. package/node_modules/@murphai/query/dist/scheduled-logs.js +55 -79
  650. package/node_modules/@murphai/query/dist/scheduled-logs.js.map +1 -1
  651. package/node_modules/@murphai/query/dist/vault-source.d.ts +22 -4
  652. package/node_modules/@murphai/query/dist/vault-source.d.ts.map +1 -1
  653. package/node_modules/@murphai/query/dist/vault-source.js +188 -59
  654. package/node_modules/@murphai/query/dist/vault-source.js.map +1 -1
  655. package/node_modules/@murphai/query/dist/wearables/candidates.d.ts.map +1 -1
  656. package/node_modules/@murphai/query/dist/wearables/candidates.js +26 -15
  657. package/node_modules/@murphai/query/dist/wearables/candidates.js.map +1 -1
  658. package/node_modules/@murphai/query/dist/wearables/public-output.d.ts.map +1 -1
  659. package/node_modules/@murphai/query/dist/wearables/public-output.js +6 -0
  660. package/node_modules/@murphai/query/dist/wearables/public-output.js.map +1 -1
  661. package/node_modules/@murphai/query/dist/wearables/source-health.d.ts.map +1 -1
  662. package/node_modules/@murphai/query/dist/wearables/source-health.js +4 -0
  663. package/node_modules/@murphai/query/dist/wearables/source-health.js.map +1 -1
  664. package/node_modules/@murphai/query/dist/wearables/types.d.ts +41 -4
  665. package/node_modules/@murphai/query/dist/wearables/types.d.ts.map +1 -1
  666. package/node_modules/@murphai/query/dist/wearables/types.js +2 -0
  667. package/node_modules/@murphai/query/dist/wearables/types.js.map +1 -1
  668. package/node_modules/@murphai/query/dist/wearables/workout-features.d.ts +5 -0
  669. package/node_modules/@murphai/query/dist/wearables/workout-features.d.ts.map +1 -0
  670. package/node_modules/@murphai/query/dist/wearables/workout-features.js +179 -0
  671. package/node_modules/@murphai/query/dist/wearables/workout-features.js.map +1 -0
  672. package/node_modules/@murphai/query/dist/wearables.d.ts.map +1 -1
  673. package/node_modules/@murphai/query/dist/wearables.js +23 -6
  674. package/node_modules/@murphai/query/dist/wearables.js.map +1 -1
  675. package/node_modules/@murphai/query/package.json +5 -0
  676. package/node_modules/@murphai/runtime-state/README.md +1 -1
  677. package/node_modules/@murphai/runtime-state/dist/assistant-runtime-issues.d.ts +4 -0
  678. package/node_modules/@murphai/runtime-state/dist/assistant-runtime-issues.d.ts.map +1 -1
  679. package/node_modules/@murphai/runtime-state/dist/assistant-runtime-issues.js +16 -3
  680. package/node_modules/@murphai/runtime-state/dist/assistant-runtime-issues.js.map +1 -1
  681. package/node_modules/@murphai/runtime-state/dist/hosted-bundles.d.ts +1 -0
  682. package/node_modules/@murphai/runtime-state/dist/hosted-bundles.d.ts.map +1 -1
  683. package/node_modules/@murphai/runtime-state/dist/hosted-bundles.js +31 -0
  684. package/node_modules/@murphai/runtime-state/dist/hosted-bundles.js.map +1 -1
  685. package/node_modules/@murphai/runtime-state/dist/hosted-email.d.ts +5 -0
  686. package/node_modules/@murphai/runtime-state/dist/hosted-email.d.ts.map +1 -1
  687. package/node_modules/@murphai/runtime-state/dist/hosted-email.js +10 -0
  688. package/node_modules/@murphai/runtime-state/dist/hosted-email.js.map +1 -1
  689. package/node_modules/@murphai/setup-cli/dist/incur-error-bridge.d.ts.map +1 -1
  690. package/node_modules/@murphai/setup-cli/dist/incur-error-bridge.js +6 -15
  691. package/node_modules/@murphai/setup-cli/dist/incur-error-bridge.js.map +1 -1
  692. package/node_modules/@murphai/vault-usecases/README.md +9 -0
  693. package/node_modules/@murphai/vault-usecases/dist/commands/query-record-command-helpers.d.ts +6 -0
  694. package/node_modules/@murphai/vault-usecases/dist/commands/query-record-command-helpers.d.ts.map +1 -1
  695. package/node_modules/@murphai/vault-usecases/dist/commands/query-record-command-helpers.js +49 -1
  696. package/node_modules/@murphai/vault-usecases/dist/commands/query-record-command-helpers.js.map +1 -1
  697. package/node_modules/@murphai/vault-usecases/dist/usecases/capture.d.ts.map +1 -1
  698. package/node_modules/@murphai/vault-usecases/dist/usecases/capture.js +22 -12
  699. package/node_modules/@murphai/vault-usecases/dist/usecases/capture.js.map +1 -1
  700. package/node_modules/@murphai/vault-usecases/dist/usecases/clinical-imports.d.ts.map +1 -1
  701. package/node_modules/@murphai/vault-usecases/dist/usecases/clinical-imports.js +1 -3
  702. package/node_modules/@murphai/vault-usecases/dist/usecases/clinical-imports.js.map +1 -1
  703. package/node_modules/@murphai/vault-usecases/dist/usecases/document-meal-read.d.ts.map +1 -1
  704. package/node_modules/@murphai/vault-usecases/dist/usecases/document-meal-read.js +24 -18
  705. package/node_modules/@murphai/vault-usecases/dist/usecases/document-meal-read.js.map +1 -1
  706. package/node_modules/@murphai/vault-usecases/dist/usecases/encounter.d.ts.map +1 -1
  707. package/node_modules/@murphai/vault-usecases/dist/usecases/encounter.js +1 -3
  708. package/node_modules/@murphai/vault-usecases/dist/usecases/encounter.js.map +1 -1
  709. package/node_modules/@murphai/vault-usecases/dist/usecases/event-record-mutations.d.ts +8 -0
  710. package/node_modules/@murphai/vault-usecases/dist/usecases/event-record-mutations.d.ts.map +1 -1
  711. package/node_modules/@murphai/vault-usecases/dist/usecases/event-record-mutations.js +56 -5
  712. package/node_modules/@murphai/vault-usecases/dist/usecases/event-record-mutations.js.map +1 -1
  713. package/node_modules/@murphai/vault-usecases/dist/usecases/exact-event-record.d.ts +20 -0
  714. package/node_modules/@murphai/vault-usecases/dist/usecases/exact-event-record.d.ts.map +1 -0
  715. package/node_modules/@murphai/vault-usecases/dist/usecases/exact-event-record.js +72 -0
  716. package/node_modules/@murphai/vault-usecases/dist/usecases/exact-event-record.js.map +1 -0
  717. package/node_modules/@murphai/vault-usecases/dist/usecases/experiment-journal-vault.d.ts +1 -1
  718. package/node_modules/@murphai/vault-usecases/dist/usecases/experiment-journal-vault.d.ts.map +1 -1
  719. package/node_modules/@murphai/vault-usecases/dist/usecases/experiment-journal-vault.js +42 -9
  720. package/node_modules/@murphai/vault-usecases/dist/usecases/experiment-journal-vault.js.map +1 -1
  721. package/node_modules/@murphai/vault-usecases/dist/usecases/explicit-health-family-services.d.ts.map +1 -1
  722. package/node_modules/@murphai/vault-usecases/dist/usecases/explicit-health-family-services.js +158 -24
  723. package/node_modules/@murphai/vault-usecases/dist/usecases/explicit-health-family-services.js.map +1 -1
  724. package/node_modules/@murphai/vault-usecases/dist/usecases/food.d.ts.map +1 -1
  725. package/node_modules/@murphai/vault-usecases/dist/usecases/food.js +98 -5
  726. package/node_modules/@murphai/vault-usecases/dist/usecases/food.js.map +1 -1
  727. package/node_modules/@murphai/vault-usecases/dist/usecases/integrated-services.d.ts.map +1 -1
  728. package/node_modules/@murphai/vault-usecases/dist/usecases/integrated-services.js +20 -4
  729. package/node_modules/@murphai/vault-usecases/dist/usecases/integrated-services.js.map +1 -1
  730. package/node_modules/@murphai/vault-usecases/dist/usecases/intervention-experiment-link.d.ts.map +1 -1
  731. package/node_modules/@murphai/vault-usecases/dist/usecases/intervention-experiment-link.js +49 -24
  732. package/node_modules/@murphai/vault-usecases/dist/usecases/intervention-experiment-link.js.map +1 -1
  733. package/node_modules/@murphai/vault-usecases/dist/usecases/measurement-read.d.ts.map +1 -1
  734. package/node_modules/@murphai/vault-usecases/dist/usecases/measurement-read.js +13 -11
  735. package/node_modules/@murphai/vault-usecases/dist/usecases/measurement-read.js.map +1 -1
  736. package/node_modules/@murphai/vault-usecases/dist/usecases/provider-event.d.ts.map +1 -1
  737. package/node_modules/@murphai/vault-usecases/dist/usecases/provider-event.js +18 -11
  738. package/node_modules/@murphai/vault-usecases/dist/usecases/provider-event.js.map +1 -1
  739. package/node_modules/@murphai/vault-usecases/dist/usecases/recipe.d.ts.map +1 -1
  740. package/node_modules/@murphai/vault-usecases/dist/usecases/recipe.js +43 -1
  741. package/node_modules/@murphai/vault-usecases/dist/usecases/recipe.js.map +1 -1
  742. package/node_modules/@murphai/vault-usecases/dist/usecases/shared.d.ts.map +1 -1
  743. package/node_modules/@murphai/vault-usecases/dist/usecases/shared.js +1 -3
  744. package/node_modules/@murphai/vault-usecases/dist/usecases/shared.js.map +1 -1
  745. package/node_modules/@murphai/vault-usecases/dist/usecases/vault-usecase-helpers.d.ts +2 -0
  746. package/node_modules/@murphai/vault-usecases/dist/usecases/vault-usecase-helpers.d.ts.map +1 -1
  747. package/node_modules/@murphai/vault-usecases/dist/usecases/vault-usecase-helpers.js +24 -1
  748. package/node_modules/@murphai/vault-usecases/dist/usecases/vault-usecase-helpers.js.map +1 -1
  749. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-format.d.ts +2 -0
  750. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-format.d.ts.map +1 -1
  751. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-model.d.ts +36 -4
  752. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-model.d.ts.map +1 -1
  753. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-model.js +48 -12
  754. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-model.js.map +1 -1
  755. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-state.d.ts +14 -14
  756. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-state.d.ts.map +1 -1
  757. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-state.js +84 -82
  758. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live-state.js.map +1 -1
  759. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live.d.ts +16 -4
  760. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live.d.ts.map +1 -1
  761. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live.js +316 -79
  762. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-live.js.map +1 -1
  763. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-read.d.ts.map +1 -1
  764. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-read.js +19 -17
  765. package/node_modules/@murphai/vault-usecases/dist/usecases/workout-read.js.map +1 -1
  766. package/node_modules/@murphai/vault-usecases/dist/usecases/workout.d.ts +20 -7
  767. package/node_modules/@murphai/vault-usecases/dist/usecases/workout.d.ts.map +1 -1
  768. package/node_modules/@murphai/vault-usecases/dist/usecases/workout.js +56 -31
  769. package/node_modules/@murphai/vault-usecases/dist/usecases/workout.js.map +1 -1
  770. package/node_modules/incur/dist/Cli.js +7 -32
  771. package/node_modules/incur/dist/Errors.d.ts +17 -0
  772. package/node_modules/incur/dist/Errors.js +23 -0
  773. package/node_modules/incur/dist/internal/command.d.ts +2 -0
  774. package/node_modules/incur/dist/internal/command.js +2 -6
  775. package/node_modules/incur/src/Cli.ts +11 -32
  776. package/node_modules/incur/src/Errors.ts +36 -0
  777. package/node_modules/incur/src/internal/command.ts +4 -6
  778. package/package.json +4 -4
  779. package/node_modules/@murphai/assistant-engine/skills/nutrition-strategy/references/daily-nutrition-card-safety.md +0 -211
@@ -1,6 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { createReadStream } from "node:fs";
3
- import { auditRecordSchema, assertContractId, collectEventRawReferencePaths, compareIsoTimestampsAscending, deviceDataOriginSchema, experimentFrontmatterSchema, externalRefSchema, journalDayFrontmatterSchema, eventRecordSchema, eventImportDecisionSchema, isWritableIsoDateTime, safeParseContract, sampleRecordSchema, } from "@murphai/contracts";
3
+ import { stat } from "node:fs/promises";
4
+ import { performance } from "node:perf_hooks";
5
+ import { auditRecordSchema, assertContractId, collectEventRawReferencePaths, compareIsoTimestampsAscending, deviceDataOriginSchema, experimentFrontmatterSchema, externalRefSchema, journalDayFrontmatterSchema, eventRecordSchema, eventImportDecisionSchema, isWritableIsoDateTime, resolveFloatingIsoTimestampInTimeZone, safeParseContract, sampleRecordSchema, } from "@murphai/contracts";
4
6
  import { BASELINE_EVENT_KINDS, BASELINE_SAMPLE_STREAMS, EVENT_SCHEMA_VERSION, EVENT_SOURCES, FRONTMATTER_SCHEMA_VERSIONS, ID_PREFIXES, SAMPLE_QUALITIES, SAMPLE_SCHEMA_VERSION, SAMPLE_SOURCES, VAULT_LAYOUT, } from "./constants.js";
5
7
  import { emitAuditRecord } from "./audit.js";
6
8
  import { prepareEventAttachments, stagePreparedEventAttachmentsInBatch, } from "./event-attachments.js";
@@ -26,6 +28,7 @@ import { loadVault } from "./vault.js";
26
28
  import { statAndHashVaultFile } from "./raw-artifact-integrity.js";
27
29
  const DENSE_DEVICE_TELEMETRY_NOT_ALLOWED_CODE = "VAULT_DENSE_DEVICE_TELEMETRY_NOT_ALLOWED";
28
30
  const DENSE_DEVICE_SAMPLES_NOT_ALLOWED_LEGACY_CODE = "VAULT_DENSE_DEVICE_SAMPLES_NOT_ALLOWED";
31
+ const MAX_JUNCTION_DAILY_ALIAS_REPAIR_REVISIONS = 64;
29
32
  const RESERVED_DEVICE_EVENT_FIELD_NAMES = new Set([
30
33
  "schemaVersion",
31
34
  "id",
@@ -49,6 +52,9 @@ const RESERVED_DEVICE_EVENT_FIELD_NAMES = new Set([
49
52
  "visibility",
50
53
  ]);
51
54
  const MAX_DEVICE_PROVIDER_SAMPLE_ROWS_DEFAULT = 1_000;
55
+ // Junction menstrual resources admit 512 dated non-BBT facts plus the
56
+ // period-length and cycle-length scalar facets for the same resource version.
57
+ const MAX_DEVICE_AUTHORITATIVE_CURRENT_FACETS = 514;
52
58
  const EVENT_KIND_SET = new Set(BASELINE_EVENT_KINDS);
53
59
  const EVENT_SOURCE_SET = new Set(EVENT_SOURCES);
54
60
  const SAMPLE_STREAM_SET = new Set(BASELINE_SAMPLE_STREAMS);
@@ -543,7 +549,8 @@ function normalizeDeviceEventInputs(eventInputs, context) {
543
549
  const inputTimeZone = typeof eventInput.timeZone === "string" ? eventInput.timeZone : undefined;
544
550
  const preservesProviderDayWithoutTimeZone = Boolean(inputDayKey &&
545
551
  !inputTimeZone &&
546
- (isJunctionSleepStageExternalRefInput(eventInput.externalRef)
552
+ (context.provider === "junction"
553
+ || isJunctionSleepStageExternalRefInput(eventInput.externalRef)
547
554
  || isDateOnlyFloatingProviderDayInput(inputDayKey, eventInput.dataOrigin)));
548
555
  const seed = buildNormalizedEventSeed({
549
556
  kind,
@@ -658,7 +665,65 @@ function normalizeDeviceBatchObjectArray(input) {
658
665
  }
659
666
  return input.value.map((entry, index) => assertPlainObject(entry, input.itemCode, `${input.itemLabel} ${index + 1} must be a plain object.`));
660
667
  }
661
- function normalizeDeviceBatchInputs({ provider, accountId, importedAt = new Date(), defaultTimeZone: fallbackTimeZone, source = "device", events = [], samples = [], evidenceParts = [], ingestReceipt, provenance, }) {
668
+ function normalizeDeviceAuthoritativeEventSets(inputs, events) {
669
+ if (inputs.length > 128) {
670
+ throw new VaultError("VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SETS", "Device batch authoritativeEventSets exceeds 128 resources.");
671
+ }
672
+ const sets = inputs.map((input, index) => {
673
+ const parsedRef = safeParseContract(externalRefSchema, {
674
+ system: input.system,
675
+ resourceType: input.resourceType,
676
+ resourceId: input.resourceId,
677
+ version: input.version,
678
+ });
679
+ if (!parsedRef.success || !parsedRef.data.version || !isWritableIsoDateTime(parsedRef.data.version)) {
680
+ throw new VaultError("VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SET", `Device authoritative event set ${index + 1} requires a valid versioned externalRef identity.`);
681
+ }
682
+ const facetPrefixes = normalizeUniqueTextList(input.facetPrefixes, `authoritativeEventSets[${index}].facetPrefixes`, 32, 200);
683
+ const currentFacets = normalizeUniqueTextList(input.currentFacets, `authoritativeEventSets[${index}].currentFacets`, MAX_DEVICE_AUTHORITATIVE_CURRENT_FACETS, 200) ?? [];
684
+ if (!facetPrefixes || facetPrefixes.length === 0) {
685
+ throw new VaultError("VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SET", `Device authoritative event set ${index + 1} requires at least one facet prefix.`);
686
+ }
687
+ const ownsFacet = (facet) => facetPrefixes.some((prefix) => facet === prefix || facet.startsWith(`${prefix}-`));
688
+ if (currentFacets.some((facet) => !ownsFacet(facet))) {
689
+ throw new VaultError("VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SET", `Device authoritative event set ${index + 1} contains a current facet outside its owned prefixes.`);
690
+ }
691
+ return {
692
+ system: parsedRef.data.system,
693
+ resourceType: parsedRef.data.resourceType,
694
+ resourceId: parsedRef.data.resourceId,
695
+ version: parsedRef.data.version,
696
+ facetPrefixes: [...facetPrefixes].sort(),
697
+ currentFacets: new Set(currentFacets),
698
+ };
699
+ });
700
+ const identityKeys = sets.map((set) => stableStringify({
701
+ system: set.system,
702
+ resourceType: set.resourceType,
703
+ resourceId: set.resourceId,
704
+ }));
705
+ if (new Set(identityKeys).size !== identityKeys.length) {
706
+ throw new VaultError("VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SETS", "Device batch authoritativeEventSets contains duplicate resource identities.");
707
+ }
708
+ for (const set of sets) {
709
+ for (const facet of set.currentFacets) {
710
+ // Unversioned members follow complete-set reconciliation: the set's
711
+ // version orders only retraction tombstones, while member events collapse
712
+ // by semantic content and reassert through the serialized set seam.
713
+ const hasCurrentEvent = events.some(({ seed }) => seed.externalRef?.system === set.system
714
+ && seed.externalRef.resourceType === set.resourceType
715
+ && seed.externalRef.resourceId === set.resourceId
716
+ && (seed.externalRef.version === set.version
717
+ || seed.externalRef.version === undefined)
718
+ && seed.externalRef.facet === facet);
719
+ if (!hasCurrentEvent) {
720
+ throw new VaultError("VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SET", `Device authoritative event set current facet "${facet}" has no matching current event.`);
721
+ }
722
+ }
723
+ }
724
+ return sets;
725
+ }
726
+ function normalizeDeviceBatchInputs({ provider, accountId, importedAt = new Date(), defaultTimeZone: fallbackTimeZone, source = "device", events = [], samples = [], evidenceParts = [], authoritativeEventSets = [], ingestReceipt, provenance, }) {
662
727
  const normalizedProvider = sanitizePathSegment(provider, "provider");
663
728
  const normalizedAccountId = typeof accountId === "string" && accountId.trim() ? accountId.trim() : undefined;
664
729
  const normalizedImportedAt = toIsoTimestamp(importedAt, "importedAt");
@@ -688,12 +753,26 @@ function normalizeDeviceBatchInputs({ provider, accountId, importedAt = new Date
688
753
  itemCode: "VAULT_INVALID_RAW_ARTIFACT",
689
754
  itemLabel: "Device raw artifact",
690
755
  });
756
+ const authoritativeEventSetInputs = normalizeDeviceBatchObjectArray({
757
+ value: authoritativeEventSets,
758
+ code: "VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SETS",
759
+ message: "Device batch authoritativeEventSets must be an array when provided.",
760
+ itemCode: "VAULT_INVALID_DEVICE_AUTHORITATIVE_EVENT_SET",
761
+ itemLabel: "Device authoritative event set",
762
+ });
691
763
  if (eventInputs.length === 0
692
764
  && sampleInputs.length === 0
693
765
  && evidencePartInputs.length === 0
694
766
  && !normalizedIngestReceipt) {
695
767
  throw new VaultError("VAULT_INVALID_DEVICE_BATCH", "importDeviceBatch requires at least one event, sample, evidence part, or ingest receipt.");
696
768
  }
769
+ const normalizedEvents = normalizeDeviceEventInputs(eventInputs, {
770
+ provider: normalizedProvider,
771
+ accountId: normalizedAccountId,
772
+ importedAt: normalizedImportedAt,
773
+ source: normalizedSource,
774
+ defaultTimeZone,
775
+ });
697
776
  return {
698
777
  provider: normalizedProvider,
699
778
  accountId: normalizedAccountId,
@@ -703,13 +782,7 @@ function normalizeDeviceBatchInputs({ provider, accountId, importedAt = new Date
703
782
  provenance: normalizedProvenance,
704
783
  ingestReceipt: normalizedIngestReceipt,
705
784
  legacyIngestReceipt,
706
- events: normalizeDeviceEventInputs(eventInputs, {
707
- provider: normalizedProvider,
708
- accountId: normalizedAccountId,
709
- importedAt: normalizedImportedAt,
710
- source: normalizedSource,
711
- defaultTimeZone,
712
- }),
785
+ events: normalizedEvents,
713
786
  samples: normalizeDeviceSampleInputs(sampleInputs, {
714
787
  provider: normalizedProvider,
715
788
  accountId: normalizedAccountId,
@@ -717,6 +790,7 @@ function normalizeDeviceBatchInputs({ provider, accountId, importedAt = new Date
717
790
  defaultTimeZone,
718
791
  }),
719
792
  evidenceParts: normalizeDeviceEvidencePartInputs(evidencePartInputs, normalizedProvider),
793
+ authoritativeEventSets: normalizeDeviceAuthoritativeEventSets(authoritativeEventSetInputs, normalizedEvents),
720
794
  };
721
795
  }
722
796
  function prepareDeviceEvidenceParts(evidenceParts) {
@@ -863,10 +937,43 @@ function junctionSparseAffectedDayKeys(targets) {
863
937
  }
864
938
  // Device-sync content equality ignores per-import identity (id, lifecycle,
865
939
  // recordedAt) AND rawRefs, because device imports mint fresh raw-artifact
866
- // paths on every sync run.
867
- function deviceEventContentKey(record) {
940
+ // paths on every sync run. Member-edit comparison additionally excludes the
941
+ // immutable provider attribution fields while retaining every public edit.
942
+ function eventContentKey(record, options) {
868
943
  const { id: _id, rawRefs: _rawRefs, lifecycle: _lifecycle, recordedAt: _recordedAt, ...content } = record;
869
- return stableStringify(content);
944
+ if (options.includeProviderAttribution) {
945
+ return stableStringify(content);
946
+ }
947
+ const { dataOrigin: _dataOrigin, externalRef: _externalRef, ...memberMutableContent } = content;
948
+ return stableStringify(memberMutableContent);
949
+ }
950
+ function deviceEventContentKey(record) {
951
+ const { id: _id, rawRefs: _rawRefs, lifecycle: _lifecycle, recordedAt: _recordedAt, externalRef, dataOrigin, ...semanticContent } = record;
952
+ return stableStringify({
953
+ ...semanticContent,
954
+ // Identity and source ordering are enforced independently. Keeping them
955
+ // out of semantic equality prevents a newer provider capture/version from
956
+ // masquerading as a health-data change over a member-authored revision.
957
+ externalRef: externalRef
958
+ ? {
959
+ system: externalRef.system,
960
+ resourceType: externalRef.resourceType,
961
+ resourceId: externalRef.resourceId,
962
+ facet: externalRef.facet ?? null,
963
+ }
964
+ : null,
965
+ dataOrigin: dataOrigin
966
+ ? {
967
+ // This is the fixed data-origin schema discriminator, not the
968
+ // provider resource version used for ordering.
969
+ version: dataOrigin.version,
970
+ aggregatorProvider: dataOrigin.aggregatorProvider ?? null,
971
+ sourceProviderSlug: dataOrigin.sourceProviderSlug ?? null,
972
+ sourceType: dataOrigin.sourceType ?? null,
973
+ sourceInstanceId: dataOrigin.sourceInstanceId ?? null,
974
+ }
975
+ : null,
976
+ });
870
977
  }
871
978
  function deviceEventContentFingerprint(record) {
872
979
  return createHash("sha256").update(deviceEventContentKey(record)).digest("hex");
@@ -948,8 +1055,11 @@ function orderEventImportDecisionsBySourceVersion(decisions) {
948
1055
  return ordered;
949
1056
  }
950
1057
  async function indexLatestEventsByExternalRef(vaultRoot, relativePaths, signal) {
1058
+ const aliasRepairContaminatedEventIds = new Set();
1059
+ const aliasRepairContaminatedRefKeys = new Set();
951
1060
  const deviceOwnerRevisionsByRefKeyAndFingerprint = new Map();
952
1061
  const junctionSparseDayHistoryById = new Map();
1062
+ const liveOwnerIdsByRefKey = new Map();
953
1063
  const latestByRefKey = new Map();
954
1064
  const latestById = new Map();
955
1065
  const maxRevisionById = new Map();
@@ -969,6 +1079,15 @@ async function indexLatestEventsByExternalRef(vaultRoot, relativePaths, signal)
969
1079
  signal?.throwIfAborted();
970
1080
  const parsed = safeParseContract(eventRecordSchema, raw);
971
1081
  if (!parsed.success) {
1082
+ if (isLooseRecordValue(raw)) {
1083
+ if (typeof raw.id === "string" && raw.id.length > 0) {
1084
+ aliasRepairContaminatedEventIds.add(raw.id);
1085
+ }
1086
+ const rejectedRefKey = readRejectedEventExternalRefKey(raw.externalRef);
1087
+ if (rejectedRefKey) {
1088
+ aliasRepairContaminatedRefKeys.add(rejectedRefKey);
1089
+ }
1090
+ }
972
1091
  return;
973
1092
  }
974
1093
  const entry = { relativePath, record: parsed.data };
@@ -1045,13 +1164,19 @@ async function indexLatestEventsByExternalRef(vaultRoot, relativePaths, signal)
1045
1164
  const refKey = eventExternalRefKey(externalRefEntry.record.externalRef);
1046
1165
  const indexedExternalRefEntry = state.latestDeviceExternalRefEntryByRefKey.get(refKey) ?? externalRefEntry;
1047
1166
  const refGroup = groupedByRefKey.get(refKey) ?? [];
1048
- refGroup.push({
1167
+ const indexedMatch = {
1049
1168
  indexedExternalRef: indexedExternalRefEntry.record.externalRef ?? externalRefEntry.record.externalRef,
1050
1169
  indexedRecord: indexedExternalRefEntry.record,
1051
1170
  relativePath: latestForId.relativePath,
1052
1171
  record: latestForId.record,
1053
- });
1172
+ };
1173
+ refGroup.push(indexedMatch);
1054
1174
  groupedByRefKey.set(refKey, refGroup);
1175
+ if (!isDeletedEventSpineRecord(latestForId.record)) {
1176
+ const liveOwnerIds = liveOwnerIdsByRefKey.get(refKey) ?? new Set();
1177
+ liveOwnerIds.add(latestForId.record.id);
1178
+ liveOwnerIdsByRefKey.set(refKey, liveOwnerIds);
1179
+ }
1055
1180
  }
1056
1181
  for (const [refKey, group] of groupedByRefKey) {
1057
1182
  signal?.throwIfAborted();
@@ -1062,15 +1187,45 @@ async function indexLatestEventsByExternalRef(vaultRoot, relativePaths, signal)
1062
1187
  latestByRefKey.set(refKey, latest);
1063
1188
  }
1064
1189
  }
1190
+ const junctionNoIdProfilePredecessorsByScope = indexJunctionNoIdProfilePredecessors(latestByRefKey.values());
1065
1191
  return {
1192
+ aliasRepairContaminatedEventIds,
1193
+ aliasRepairContaminatedRefKeys,
1194
+ aliasRepairHistoryById: new Map(),
1066
1195
  deviceOwnerRevisionsByRefKeyAndFingerprint,
1196
+ junctionNoIdProfilePredecessorsByScope,
1067
1197
  junctionSparseDayHistoryById,
1068
1198
  latestByRefKey,
1069
1199
  latestById,
1200
+ liveOwnerIdsByRefKey,
1070
1201
  maxRevisionById,
1071
1202
  revisionsById,
1072
1203
  };
1073
1204
  }
1205
+ async function loadBoundedAliasRepairHistories(vaultRoot, relativePaths, eventIds) {
1206
+ const histories = new Map();
1207
+ for (const relativePath of relativePaths) {
1208
+ await visitJsonlRecordsInterruptible({
1209
+ vaultRoot,
1210
+ relativePath,
1211
+ visit(raw) {
1212
+ const parsed = safeParseContract(eventRecordSchema, raw);
1213
+ if (!parsed.success || !eventIds.has(parsed.data.id)) {
1214
+ return;
1215
+ }
1216
+ const history = histories.get(parsed.data.id) ?? [];
1217
+ if (history.length <= MAX_JUNCTION_DAILY_ALIAS_REPAIR_REVISIONS) {
1218
+ history.push({ relativePath, record: parsed.data });
1219
+ histories.set(parsed.data.id, history);
1220
+ }
1221
+ },
1222
+ });
1223
+ }
1224
+ for (const history of histories.values()) {
1225
+ history.sort((left, right) => eventSpineRevision(left.record) - eventSpineRevision(right.record));
1226
+ }
1227
+ return histories;
1228
+ }
1074
1229
  function isSameObservationFacet(existing, incoming) {
1075
1230
  return existing.kind === "observation" &&
1076
1231
  incoming.kind === "observation" &&
@@ -1092,6 +1247,8 @@ const JUNCTION_SLEEP_STAGE_METRIC_FACETS = new Set([
1092
1247
  ]);
1093
1248
  const JUNCTION_SLEEP_STAGE_SUMMARY_NORMALIZER_VERSION = "junction-sleep-stage-summary.v1";
1094
1249
  const JUNCTION_SLEEP_STAGE_CYCLE_FALLBACK_NORMALIZER_VERSION = "junction-sleep-stage-cycle-fallback.v1";
1250
+ const JUNCTION_NO_ID_PROFILE_NORMALIZER_VERSION = "junction-no-id-profile.v1";
1251
+ const JUNCTION_STABLE_PROFILE_CREATED_AT_NORMALIZER_VERSION = "junction-stable-profile-created-at.v1";
1095
1252
  function deviceDataOriginSourceMatches(existing, incoming) {
1096
1253
  if (!existing || !incoming) {
1097
1254
  return false;
@@ -1110,6 +1267,193 @@ function deviceDataOriginSourceMatches(existing, incoming) {
1110
1267
  }
1111
1268
  return compared;
1112
1269
  }
1270
+ function junctionNoIdProfileScopeKey(externalRef, origin) {
1271
+ if (externalRef?.system !== "junction"
1272
+ || !externalRef.resourceType.endsWith("-profile")
1273
+ || typeof externalRef.facet !== "string"
1274
+ || !/^profile-[a-f0-9]{16}$/u.test(externalRef.resourceId)
1275
+ || origin?.sourceProviderSlug === undefined) {
1276
+ return null;
1277
+ }
1278
+ return stableStringify({
1279
+ resourceType: externalRef.resourceType,
1280
+ facet: externalRef.facet,
1281
+ aggregatorProvider: origin.aggregatorProvider,
1282
+ sourceProviderSlug: origin.sourceProviderSlug,
1283
+ sourceType: origin.sourceType,
1284
+ sourceInstanceId: origin.sourceInstanceId,
1285
+ });
1286
+ }
1287
+ function junctionNoIdProfileResourceId(origin, occurredAt) {
1288
+ return `profile-${createHash("sha256")
1289
+ .update(JSON.stringify([
1290
+ "profile",
1291
+ origin.sourceProviderSlug,
1292
+ origin.sourceType,
1293
+ origin.sourceInstanceId,
1294
+ occurredAt,
1295
+ ]))
1296
+ .digest("hex")
1297
+ .slice(0, 16)}`;
1298
+ }
1299
+ function junctionNoIdProfileProviderBaselineRevision(match) {
1300
+ const externalRef = match.indexedExternalRef;
1301
+ const providerRecord = match.indexedRecord;
1302
+ const origin = providerRecord.dataOrigin;
1303
+ const isCurrentNoIdProfile = origin?.normalizerVersion
1304
+ === JUNCTION_NO_ID_PROFILE_NORMALIZER_VERSION;
1305
+ const isLegacyNoIdProfile = origin?.normalizerVersion === "junction-normalizer.v1";
1306
+ const persistedRevision = isCurrentNoIdProfile
1307
+ ? externalRef.version
1308
+ : externalRef.version ?? providerRecord.occurredAt;
1309
+ if ((!isCurrentNoIdProfile && !isLegacyNoIdProfile)
1310
+ || !junctionNoIdProfileScopeKey(externalRef, origin)
1311
+ || persistedRevision === undefined
1312
+ || !isWritableIsoDateTime(persistedRevision)
1313
+ || !isWritableIsoDateTime(providerRecord.occurredAt)
1314
+ || origin?.observedAtRaw !== providerRecord.occurredAt
1315
+ || (isLegacyNoIdProfile && providerRecord.occurredAt !== persistedRevision)) {
1316
+ return null;
1317
+ }
1318
+ const expectedResourceId = junctionNoIdProfileResourceId(origin, providerRecord.occurredAt);
1319
+ return externalRef.resourceId === expectedResourceId ? persistedRevision : null;
1320
+ }
1321
+ function junctionNoIdProfilePredecessorRevision(match) {
1322
+ return isDeletedEventSpineRecord(match.record)
1323
+ ? null
1324
+ : junctionNoIdProfileProviderBaselineRevision(match);
1325
+ }
1326
+ function isJunctionNoIdProfilePredecessor(match) {
1327
+ return junctionNoIdProfileProviderBaselineRevision(match) !== null;
1328
+ }
1329
+ function indexJunctionNoIdProfilePredecessors(matches) {
1330
+ const byScope = new Map();
1331
+ for (const match of matches) {
1332
+ if (!isJunctionNoIdProfilePredecessor(match)) {
1333
+ continue;
1334
+ }
1335
+ const scopeKey = junctionNoIdProfileScopeKey(match.indexedExternalRef, match.indexedRecord.dataOrigin);
1336
+ if (!scopeKey) {
1337
+ continue;
1338
+ }
1339
+ const scopedMatches = byScope.get(scopeKey) ?? [];
1340
+ scopedMatches.push(match);
1341
+ byScope.set(scopeKey, scopedMatches);
1342
+ }
1343
+ return byScope;
1344
+ }
1345
+ function isIncomingJunctionNoIdProfile(record) {
1346
+ return record.dataOrigin?.normalizerVersion === JUNCTION_NO_ID_PROFILE_NORMALIZER_VERSION
1347
+ && junctionNoIdProfileScopeKey(record.externalRef, record.dataOrigin) !== null;
1348
+ }
1349
+ function isIncomingJunctionStableProfileCreatedAt(record) {
1350
+ return record.dataOrigin?.normalizerVersion === JUNCTION_STABLE_PROFILE_CREATED_AT_NORMALIZER_VERSION
1351
+ && junctionNoIdProfileScopeKey(record.externalRef, record.dataOrigin) !== null;
1352
+ }
1353
+ function isExactJunctionProfileCreatedAtTimestampReplay(existing, incoming) {
1354
+ const existingRef = existing.externalRef;
1355
+ const incomingRef = incoming.externalRef;
1356
+ const existingOrigin = existing.dataOrigin;
1357
+ const incomingOrigin = incoming.dataOrigin;
1358
+ const existingRevision = existingRef?.version ?? existing.occurredAt;
1359
+ const existingScopeKey = junctionNoIdProfileScopeKey(existingRef, existingOrigin);
1360
+ const incomingScopeKey = junctionNoIdProfileScopeKey(incomingRef, incomingOrigin);
1361
+ if (!existingOrigin
1362
+ || !incomingOrigin
1363
+ || existingScopeKey === null
1364
+ || existingScopeKey !== incomingScopeKey
1365
+ || !existingRef
1366
+ || !incomingRef?.version
1367
+ || !isWritableIsoDateTime(existingRevision)
1368
+ || !isWritableIsoDateTime(incomingRef.version)
1369
+ || !isWritableIsoDateTime(existing.occurredAt)
1370
+ || !isWritableIsoDateTime(existing.recordedAt)
1371
+ || !isWritableIsoDateTime(incoming.occurredAt)
1372
+ || !isWritableIsoDateTime(incoming.recordedAt)
1373
+ || compareIsoTimestampsAscending(incomingRef.version, existingRevision) !== 0
1374
+ || (!isDeletedEventSpineRecord(existing)
1375
+ && compareIsoTimestampsAscending(existing.recordedAt, existing.occurredAt) !== 0)
1376
+ || (isDeletedEventSpineRecord(existing)
1377
+ && compareIsoTimestampsAscending(existing.recordedAt, existing.occurredAt) < 0)
1378
+ || existing.dayKey !== toIsoTimestamp(existing.occurredAt, "existing.occurredAt").slice(0, 10)
1379
+ || compareIsoTimestampsAscending(incoming.occurredAt, existing.occurredAt) >= 0
1380
+ || compareIsoTimestampsAscending(incoming.recordedAt, incoming.occurredAt) !== 0
1381
+ || incoming.dayKey !== toIsoTimestamp(incoming.occurredAt, "incoming.occurredAt").slice(0, 10)
1382
+ || existingOrigin.observedAtRaw !== existing.occurredAt
1383
+ || incomingOrigin.observedAtRaw !== incoming.occurredAt
1384
+ || !deviceDataOriginSourceMatches(existingOrigin, incomingOrigin)
1385
+ || existingOrigin.timeZoneOffsetMinutes !== incomingOrigin.timeZoneOffsetMinutes
1386
+ || existingOrigin.timestampSemantics !== incomingOrigin.timestampSemantics
1387
+ || existingOrigin.originConfidence !== incomingOrigin.originConfidence) {
1388
+ return false;
1389
+ }
1390
+ return deviceEventContentKey({
1391
+ ...existing,
1392
+ occurredAt: incoming.occurredAt,
1393
+ recordedAt: incoming.recordedAt,
1394
+ dayKey: incoming.dayKey,
1395
+ externalRef: incomingRef,
1396
+ dataOrigin: incomingOrigin,
1397
+ }) === deviceEventContentKey(incoming);
1398
+ }
1399
+ // One released Junction normalizer pinned stable-profile occurrence to the
1400
+ // mutable updated-at revision. The successor pins occurrence to created-at but
1401
+ // deliberately keeps updated-at as source ordering. Admit only that exact,
1402
+ // one-way timestamp-only replay so the generic equal-revision guard remains
1403
+ // fail-closed for health-data changes.
1404
+ function isJunctionStableProfileCreatedAtTimestampMigration(existing, incoming) {
1405
+ const existingRef = existing.externalRef;
1406
+ const incomingRef = incoming.externalRef;
1407
+ return isIncomingJunctionStableProfileCreatedAt(incoming)
1408
+ && existing.dataOrigin?.normalizerVersion === "junction-normalizer.v1"
1409
+ && existingRef?.version !== undefined
1410
+ && incomingRef !== undefined
1411
+ && eventExternalRefKey(existingRef) === eventExternalRefKey(incomingRef)
1412
+ && isExactJunctionProfileCreatedAtTimestampReplay(existing, incoming);
1413
+ }
1414
+ // Revision-2 admission is account-wide, so a released no-ID profile can be
1415
+ // replayed after created_at becomes available while updated_at (the provider
1416
+ // revision) is unchanged. Its timestamp-derived resource identity changes,
1417
+ // but the replay must not split a member edit or bypass a deletion. Retain the
1418
+ // old identity only for this exact timestamp-only transition; changed health
1419
+ // data at the same source revision remains a conflict.
1420
+ function isJunctionNoIdProfileEqualRevisionTimestampReplay(existing, incoming) {
1421
+ const existingRef = existing.externalRef;
1422
+ const incomingRef = incoming.externalRef;
1423
+ const existingOrigin = existing.dataOrigin;
1424
+ const incomingOrigin = incoming.dataOrigin;
1425
+ if (!isIncomingJunctionNoIdProfile(incoming)
1426
+ || !existingRef
1427
+ || !incomingRef
1428
+ || !existingOrigin
1429
+ || !incomingOrigin
1430
+ || eventExternalRefKey(existingRef) === eventExternalRefKey(incomingRef)
1431
+ || junctionNoIdProfileResourceId(existingOrigin, existing.occurredAt)
1432
+ !== existingRef.resourceId
1433
+ || junctionNoIdProfileResourceId(incomingOrigin, incoming.occurredAt)
1434
+ !== incomingRef.resourceId
1435
+ || !(existingOrigin.normalizerVersion === "junction-normalizer.v1"
1436
+ || existingOrigin.normalizerVersion === JUNCTION_NO_ID_PROFILE_NORMALIZER_VERSION)) {
1437
+ return false;
1438
+ }
1439
+ return isExactJunctionProfileCreatedAtTimestampReplay(existing, incoming);
1440
+ }
1441
+ function isMemberDeletedJunctionStableProfile(existing, incoming) {
1442
+ const existingRef = existing.externalRef;
1443
+ const incomingRef = incoming.externalRef;
1444
+ if (!existingRef || !incomingRef) {
1445
+ return false;
1446
+ }
1447
+ return isDeletedEventSpineRecord(existing)
1448
+ && isIncomingJunctionStableProfileCreatedAt(incoming)
1449
+ && junctionNoIdProfileScopeKey(existingRef, existing.dataOrigin) !== null
1450
+ && eventExternalRefKey(existingRef) === eventExternalRefKey(incomingRef)
1451
+ && deviceDataOriginSourceMatches(existing.dataOrigin, incoming.dataOrigin)
1452
+ && existingRef?.version !== undefined
1453
+ && isWritableIsoDateTime(existingRef.version)
1454
+ && isWritableIsoDateTime(existing.recordedAt)
1455
+ && compareIsoTimestampsAscending(existing.recordedAt, existingRef.version) > 0;
1456
+ }
1113
1457
  function isDateLikeWhoopBodyMeasurementResourceId(resourceId) {
1114
1458
  return /^\d{4}-\d{2}-\d{2}$/u.test(resourceId) ||
1115
1459
  /^date:\d{4}-\d{2}-\d{2}$/u.test(resourceId);
@@ -1174,6 +1518,95 @@ function shouldKeepExistingJunctionSleepStageSummaryObservation(existing, incomi
1174
1518
  isJunctionSleepStageCycleFallbackObservation(incoming);
1175
1519
  }
1176
1520
  const JUNCTION_COMPANION_HEALTH_METADATA_SOURCE_TYPE = "companion-whoop-metadata-unverified";
1521
+ const JUNCTION_SPARSE_FLOATING_FALLBACK_NORMALIZER_VERSION = "junction-sparse-timeseries.floating-fallback.v2";
1522
+ function resolveJunctionFloatingFallbackTime(existing, incoming) {
1523
+ if (incoming.externalRef?.system !== "junction"
1524
+ || incoming.dataOrigin?.normalizerVersion
1525
+ !== JUNCTION_SPARSE_FLOATING_FALLBACK_NORMALIZER_VERSION
1526
+ || incoming.dataOrigin.timestampSemantics !== "floating") {
1527
+ return incoming;
1528
+ }
1529
+ if (!incoming.timeZone) {
1530
+ return incoming;
1531
+ }
1532
+ // Junction documents Libre's +00:00 values as wall clocks, not instants.
1533
+ // The importer carries those raw wall values through a schema-valid transient
1534
+ // event. Resolve them only after stable external identity lookup, so a matched
1535
+ // row uses its event spine's accepted zone while a new row uses the incoming
1536
+ // fallback zone. Each endpoint resolves independently across DST transitions.
1537
+ const acceptedExisting = existing?.externalRef?.system === "junction"
1538
+ && existing.dataOrigin?.timestampSemantics === "floating"
1539
+ && deviceDataOriginSourceMatches(existing.dataOrigin, incoming.dataOrigin)
1540
+ ? existing
1541
+ : undefined;
1542
+ const targetTimeZone = acceptedExisting?.timeZone ?? incoming.timeZone;
1543
+ const startRaw = incoming.dataOrigin.observedAtRaw;
1544
+ if (typeof startRaw !== "string") {
1545
+ return incoming;
1546
+ }
1547
+ const resolvedOccurrence = resolveFloatingIsoTimestampInTimeZone(startRaw, targetTimeZone);
1548
+ const resolvedStart = incoming.kind === "intervention_session"
1549
+ && typeof incoming.fields?.["start-at"] === "string"
1550
+ ? resolveFloatingIsoTimestampInTimeZone(incoming.fields["start-at"], targetTimeZone)
1551
+ : undefined;
1552
+ const resolvedStartAt = resolvedStart?.timestamp;
1553
+ const resolvedEndAt = incoming.kind === "intervention_session"
1554
+ && typeof incoming.fields?.["end-at"] === "string"
1555
+ ? resolveFloatingIsoTimestampInTimeZone(incoming.fields["end-at"], targetTimeZone)?.timestamp
1556
+ : undefined;
1557
+ if (!resolvedOccurrence
1558
+ || (incoming.kind === "intervention_session"
1559
+ && (!resolvedStartAt
1560
+ || !resolvedEndAt
1561
+ || Date.parse(resolvedEndAt) < Date.parse(resolvedStartAt)))) {
1562
+ return null;
1563
+ }
1564
+ const resolvedInterventionFields = incoming.kind === "intervention_session"
1565
+ && incoming.fields
1566
+ ? {
1567
+ ...incoming.fields,
1568
+ ...(resolvedStartAt ? { "start-at": resolvedStartAt } : {}),
1569
+ ...(resolvedEndAt ? { "end-at": resolvedEndAt } : {}),
1570
+ }
1571
+ : undefined;
1572
+ const canonicalOccurrence = incoming.kind === "intervention_session"
1573
+ && resolvedStartAt
1574
+ ? resolvedStartAt
1575
+ : resolvedOccurrence.timestamp;
1576
+ const canonicalDayKey = incoming.kind === "intervention_session"
1577
+ && resolvedStart
1578
+ ? resolvedStart.dayKey
1579
+ : resolvedOccurrence.dayKey;
1580
+ return {
1581
+ ...incoming,
1582
+ occurredAt: canonicalOccurrence,
1583
+ recordedAt: acceptedExisting?.recordedAt ?? incoming.recordedAt,
1584
+ dayKey: canonicalDayKey,
1585
+ timeZone: targetTimeZone,
1586
+ ...(incoming.kind === "intervention_session" && resolvedInterventionFields
1587
+ ? { fields: resolvedInterventionFields }
1588
+ : {}),
1589
+ };
1590
+ }
1591
+ function resolveJunctionFloatingFallbackEntries(entries, context) {
1592
+ return entries.flatMap((entry) => {
1593
+ if (entry.record.externalRef?.system !== "junction"
1594
+ || entry.record.dataOrigin?.normalizerVersion
1595
+ !== JUNCTION_SPARSE_FLOATING_FALLBACK_NORMALIZER_VERSION) {
1596
+ return [entry];
1597
+ }
1598
+ const resolved = resolveDeviceEventIdentity(entry, context, { strict: true });
1599
+ const indexedProviderMatch = resolved?.matchedEntries.find((match) => match.indexedMatch.record.id === resolved.latest?.id)?.indexedMatch ?? resolved?.matchedEntries[0]?.indexedMatch;
1600
+ const canonicalRecord = resolveJunctionFloatingFallbackTime(indexedProviderMatch?.indexedRecord, entry.record);
1601
+ return canonicalRecord
1602
+ ? [{
1603
+ ...entry,
1604
+ relativePath: toEventLedgerFile(canonicalRecord.occurredAt),
1605
+ record: canonicalRecord,
1606
+ }]
1607
+ : [];
1608
+ });
1609
+ }
1177
1610
  function parseJunctionCompanionSyncVersion(version) {
1178
1611
  if (!version || !/^(?:0|[1-9]\d*)$/u.test(version)) {
1179
1612
  return undefined;
@@ -1275,12 +1708,126 @@ function toIndexedExternalRefMatch(record, externalRef, relativePath = "") {
1275
1708
  record,
1276
1709
  };
1277
1710
  }
1278
- function userAuthoredEventStateKey(record) {
1279
- return stableStringify({
1280
- links: record.links ?? [],
1281
- note: record.note ?? null,
1282
- tags: record.tags ?? [],
1711
+ const deviceBatchImportSessionState = new WeakMap();
1712
+ export function createDeviceBatchImportSession() {
1713
+ const session = Object.freeze({
1714
+ kind: "device_batch_import_session",
1715
+ });
1716
+ deviceBatchImportSessionState.set(session, new Map());
1717
+ return session;
1718
+ }
1719
+ function buildDeviceEventIdentityDependency(plan, additionalEventIds = []) {
1720
+ const externalRefs = plan.preparedEvents.flatMap((entry) => [
1721
+ ...(entry.record.externalRef ? [entry.record.externalRef] : []),
1722
+ ...entry.legacyExternalRefs,
1723
+ ]);
1724
+ return {
1725
+ authoritativeEventSets: plan.authoritativeEventSets,
1726
+ eventIds: new Set([
1727
+ ...plan.preparedEvents.map((entry) => entry.record.id),
1728
+ ...additionalEventIds,
1729
+ ]),
1730
+ externalRefs,
1731
+ junctionNoIdProfileScopes: new Set(plan.preparedEvents.flatMap((entry) => {
1732
+ const externalRef = entry.record.externalRef;
1733
+ const scope = externalRef
1734
+ ? junctionNoIdProfileScopeKey(externalRef, entry.record.dataOrigin)
1735
+ : null;
1736
+ return scope ? [scope] : [];
1737
+ })),
1738
+ };
1739
+ }
1740
+ function eventRefBelongsToAuthoritativeSet(externalRef, set) {
1741
+ if (externalRef.system !== set.system
1742
+ || externalRef.resourceType !== set.resourceType
1743
+ || externalRef.resourceId !== set.resourceId
1744
+ || externalRef.facet === undefined) {
1745
+ return false;
1746
+ }
1747
+ return set.facetPrefixes.some((prefix) => externalRef.facet === prefix || externalRef.facet?.startsWith(`${prefix}-`));
1748
+ }
1749
+ function authoritativeEventSetsOverlap(left, right) {
1750
+ if (left.system !== right.system
1751
+ || left.resourceType !== right.resourceType
1752
+ || left.resourceId !== right.resourceId) {
1753
+ return false;
1754
+ }
1755
+ return left.facetPrefixes.some((leftPrefix) => right.facetPrefixes.some((rightPrefix) => leftPrefix === rightPrefix
1756
+ || leftPrefix.startsWith(`${rightPrefix}-`)
1757
+ || rightPrefix.startsWith(`${leftPrefix}-`)));
1758
+ }
1759
+ function deviceEventIdentityDependenciesOverlap(left, right) {
1760
+ if ([...left.eventIds].some((eventId) => right.eventIds.has(eventId))) {
1761
+ return true;
1762
+ }
1763
+ const rightRefKeys = new Set(right.externalRefs.map(eventExternalRefKey));
1764
+ if (left.externalRefs.some((externalRef) => rightRefKeys.has(eventExternalRefKey(externalRef)))) {
1765
+ return true;
1766
+ }
1767
+ if ([...left.junctionNoIdProfileScopes].some((scope) => right.junctionNoIdProfileScopes.has(scope))) {
1768
+ return true;
1769
+ }
1770
+ if (left.externalRefs.some((externalRef) => right.authoritativeEventSets.some((set) => eventRefBelongsToAuthoritativeSet(externalRef, set)))) {
1771
+ return true;
1772
+ }
1773
+ if (right.externalRefs.some((externalRef) => left.authoritativeEventSets.some((set) => eventRefBelongsToAuthoritativeSet(externalRef, set)))) {
1774
+ return true;
1775
+ }
1776
+ return left.authoritativeEventSets.some((leftSet) => right.authoritativeEventSets.some((rightSet) => authoritativeEventSetsOverlap(leftSet, rightSet)));
1777
+ }
1778
+ function readDeviceBatchImportSessionVaultState(session, vaultRoot) {
1779
+ return session ? deviceBatchImportSessionState.get(session)?.get(vaultRoot) : undefined;
1780
+ }
1781
+ function replaceDeviceBatchImportSessionVaultState(session, vaultRoot, state) {
1782
+ if (!session) {
1783
+ return;
1784
+ }
1785
+ deviceBatchImportSessionState.get(session)?.set(vaultRoot, state);
1786
+ }
1787
+ function clearDeviceBatchImportSessionVaultState(session, vaultRoot) {
1788
+ if (!session) {
1789
+ return;
1790
+ }
1791
+ deviceBatchImportSessionState.get(session)?.delete(vaultRoot);
1792
+ }
1793
+ function emitDeviceBatchImportTiming(observer, timing) {
1794
+ try {
1795
+ observer?.({ ...timing });
1796
+ }
1797
+ catch {
1798
+ // Diagnostics must not turn an already-committed canonical write into an
1799
+ // apparent import failure.
1800
+ }
1801
+ }
1802
+ async function buildDeviceEventLedgerFingerprint(vaultRoot) {
1803
+ const shardPaths = await walkVaultFiles(vaultRoot, VAULT_LAYOUT.eventLedgerDirectory, {
1804
+ extension: ".jsonl",
1283
1805
  });
1806
+ const metadata = await Promise.all(shardPaths.map(async (relativePath) => {
1807
+ const shard = await stat(resolveVaultPath(vaultRoot, relativePath).absolutePath);
1808
+ return [
1809
+ relativePath,
1810
+ shard.dev,
1811
+ shard.ino,
1812
+ shard.size,
1813
+ shard.mtimeMs,
1814
+ shard.ctimeMs,
1815
+ ].join("\0");
1816
+ }));
1817
+ return createHash("sha256").update(metadata.join("\n")).digest("hex");
1818
+ }
1819
+ async function tryBuildDeviceEventLedgerFingerprint(vaultRoot) {
1820
+ try {
1821
+ return await buildDeviceEventLedgerFingerprint(vaultRoot);
1822
+ }
1823
+ catch {
1824
+ // A failed cache fence disables reuse. The canonical import path remains
1825
+ // authoritative and will surface any real ledger read failure itself.
1826
+ return undefined;
1827
+ }
1828
+ }
1829
+ function userAuthoredEventStateKey(record) {
1830
+ return eventContentKey(record, { includeProviderAttribution: false });
1284
1831
  }
1285
1832
  function hasHistoricalExternalRefUserAuthoredChanges(match) {
1286
1833
  return eventSpineRevision(match.record) > eventSpineRevision(match.indexedRecord) &&
@@ -1288,40 +1835,99 @@ function hasHistoricalExternalRefUserAuthoredChanges(match) {
1288
1835
  }
1289
1836
  function buildLegacyExternalRefReservations(entries, index) {
1290
1837
  const reservations = new Map();
1838
+ const reserve = (entry, refKey, indexedMatch) => {
1839
+ const existing = reservations.get(refKey);
1840
+ if (existing && existing.entry !== entry) {
1841
+ const externalRef = indexedMatch.indexedExternalRef;
1842
+ throw new VaultError("EVENT_EXTERNAL_REF_ALIAS_CONFLICT", `Event legacy externalRef "${externalRef.system}/${externalRef.resourceType}/` +
1843
+ `${externalRef.resourceId}${externalRef.facet ? `#${externalRef.facet}` : ""}" ` +
1844
+ "matched multiple incoming device events; ambiguous legacy cleanup must be repaired explicitly.");
1845
+ }
1846
+ reservations.set(refKey, { entry, indexedMatch });
1847
+ };
1291
1848
  for (const entry of entries) {
1292
1849
  const externalRef = entry.record.externalRef;
1293
1850
  if (!externalRef) {
1294
1851
  continue;
1295
1852
  }
1296
1853
  const primaryRefKey = eventExternalRefKey(externalRef);
1854
+ const dailyAliasOwnerSplit = resolveJunctionDailyAggregateAliasOwnerSplit(entry, index);
1297
1855
  for (const legacyExternalRef of entry.legacyExternalRefs) {
1298
1856
  const legacyRefKey = eventExternalRefKey(legacyExternalRef);
1299
1857
  if (legacyRefKey === primaryRefKey) {
1300
1858
  continue;
1301
1859
  }
1302
- const indexedMatch = index.latestByRefKey.get(legacyRefKey);
1303
- if (!indexedMatch || !isCompatibleLegacyExternalRefMatch(indexedMatch, entry.record, legacyExternalRef)) {
1860
+ const persistedAliasMatch = dailyAliasOwnerSplit?.legacyRefKey === legacyRefKey
1861
+ ? dailyAliasOwnerSplit.legacyOwner
1862
+ : undefined;
1863
+ const indexedMatch = persistedAliasMatch ?? index.latestByRefKey.get(legacyRefKey);
1864
+ if (!indexedMatch
1865
+ || (!persistedAliasMatch
1866
+ && !isCompatibleLegacyExternalRefMatch(indexedMatch, entry.record, legacyExternalRef))) {
1304
1867
  continue;
1305
1868
  }
1306
- const existing = reservations.get(legacyRefKey);
1307
- if (existing && existing.entry !== entry) {
1308
- throw new VaultError("EVENT_EXTERNAL_REF_ALIAS_CONFLICT", `Event legacy externalRef "${legacyExternalRef.system}/${legacyExternalRef.resourceType}/` +
1309
- `${legacyExternalRef.resourceId}${legacyExternalRef.facet ? `#${legacyExternalRef.facet}` : ""}" ` +
1310
- "matched multiple incoming device events; ambiguous legacy cleanup must be repaired explicitly.");
1869
+ reserve(entry, legacyRefKey, indexedMatch);
1870
+ }
1871
+ if (!isIncomingJunctionNoIdProfile(entry.record)) {
1872
+ continue;
1873
+ }
1874
+ const scopeKey = junctionNoIdProfileScopeKey(externalRef, entry.record.dataOrigin);
1875
+ const incomingRevision = externalRef.version;
1876
+ if (!scopeKey || !incomingRevision || !isWritableIsoDateTime(incomingRevision)) {
1877
+ continue;
1878
+ }
1879
+ const scopedMatches = (index.junctionNoIdProfilePredecessorsByScope.get(scopeKey) ?? [])
1880
+ .filter((match) => eventExternalRefKey(match.indexedExternalRef) !== primaryRefKey);
1881
+ const equalRevisionMatches = scopedMatches.filter((match) => {
1882
+ const predecessorRevision = junctionNoIdProfileProviderBaselineRevision(match);
1883
+ return predecessorRevision !== null
1884
+ && compareIsoTimestampsAscending(incomingRevision, predecessorRevision) === 0;
1885
+ });
1886
+ if (equalRevisionMatches.length > 1) {
1887
+ throw new VaultError("EVENT_EXTERNAL_REF_ALIAS_CONFLICT", `Junction profile externalRef "${externalRef.system}/${externalRef.resourceType}/` +
1888
+ `${externalRef.resourceId}${externalRef.facet ? `#${externalRef.facet}` : ""}" ` +
1889
+ "matched multiple persisted identities at the same provider revision; nothing was imported.");
1890
+ }
1891
+ const equalRevisionMatch = equalRevisionMatches[0];
1892
+ if (equalRevisionMatch) {
1893
+ if (!isJunctionNoIdProfileEqualRevisionTimestampReplay(equalRevisionMatch.indexedRecord, entry.record)) {
1894
+ throw new VaultError("EVENT_SOURCE_REVISION_CONFLICT", `Junction profile externalRef "${externalRef.system}/${externalRef.resourceType}/` +
1895
+ `${externalRef.resourceId}${externalRef.facet ? `#${externalRef.facet}` : ""}" ` +
1896
+ `has conflicting content for source revision "${incomingRevision}"; nothing was imported.`);
1311
1897
  }
1312
- reservations.set(legacyRefKey, { entry, indexedMatch });
1898
+ reserve(entry, eventExternalRefKey(equalRevisionMatch.indexedExternalRef), equalRevisionMatch);
1899
+ continue;
1900
+ }
1901
+ const predecessorMatches = scopedMatches.filter((match) => {
1902
+ const predecessorRevision = junctionNoIdProfilePredecessorRevision(match);
1903
+ return predecessorRevision !== null
1904
+ && compareIsoTimestampsAscending(incomingRevision, predecessorRevision) > 0;
1905
+ });
1906
+ if (predecessorMatches.length > 1) {
1907
+ throw new VaultError("EVENT_EXTERNAL_REF_ALIAS_CONFLICT", `Junction profile externalRef "${externalRef.system}/${externalRef.resourceType}/` +
1908
+ `${externalRef.resourceId}${externalRef.facet ? `#${externalRef.facet}` : ""}" ` +
1909
+ "matched multiple persisted predecessor identities; nothing was imported.");
1910
+ }
1911
+ const predecessor = predecessorMatches[0];
1912
+ if (predecessor) {
1913
+ reserve(entry, eventExternalRefKey(predecessor.indexedExternalRef), predecessor);
1313
1914
  }
1314
1915
  }
1315
1916
  return reservations;
1316
1917
  }
1317
- async function buildDeviceEventIdentityContext(vaultRoot, entries) {
1318
- if (entries.length === 0) {
1918
+ async function buildDeviceEventIdentityContext(vaultRoot, entries, authoritativeEventSets = []) {
1919
+ if (entries.length === 0 && authoritativeEventSets.length === 0) {
1319
1920
  return {
1320
1921
  index: {
1922
+ aliasRepairContaminatedEventIds: new Set(),
1923
+ aliasRepairContaminatedRefKeys: new Set(),
1924
+ aliasRepairHistoryById: new Map(),
1321
1925
  deviceOwnerRevisionsByRefKeyAndFingerprint: new Map(),
1926
+ junctionNoIdProfilePredecessorsByScope: new Map(),
1322
1927
  junctionSparseDayHistoryById: new Map(),
1323
1928
  latestByRefKey: new Map(),
1324
1929
  latestById: new Map(),
1930
+ liveOwnerIdsByRefKey: new Map(),
1325
1931
  maxRevisionById: new Map(),
1326
1932
  revisionsById: new Map(),
1327
1933
  },
@@ -1332,30 +1938,55 @@ async function buildDeviceEventIdentityContext(vaultRoot, entries) {
1332
1938
  extension: ".jsonl",
1333
1939
  });
1334
1940
  const index = await indexLatestEventsByExternalRef(vaultRoot, shardPaths);
1335
- return {
1336
- index,
1337
- legacyReservations: buildLegacyExternalRefReservations(entries, index),
1338
- };
1941
+ const context = { index, legacyReservations: new Map() };
1942
+ // A legitimate primary spine may advance before the duplicate is repaired.
1943
+ // Load only structural candidates first so reservations can use the bounded
1944
+ // common-initial-history proof instead of latest-record equality.
1945
+ const aliasRepairOwnerIds = new Set();
1946
+ for (const entry of entries) {
1947
+ const ownerSplit = resolveStructuralJunctionDailyAggregateAliasOwnerSplit(entry, index);
1948
+ if (!ownerSplit) {
1949
+ continue;
1950
+ }
1951
+ assertJunctionDailyAggregateAliasHistoryIsUncontaminated(entry, context);
1952
+ aliasRepairOwnerIds.add(ownerSplit.primaryOwner.record.id);
1953
+ aliasRepairOwnerIds.add(ownerSplit.legacyOwner.record.id);
1954
+ }
1955
+ if (aliasRepairOwnerIds.size > 0) {
1956
+ index.aliasRepairHistoryById = await loadBoundedAliasRepairHistories(vaultRoot, shardPaths, aliasRepairOwnerIds);
1957
+ }
1958
+ context.legacyReservations = buildLegacyExternalRefReservations(entries, index);
1959
+ return context;
1339
1960
  }
1340
1961
  function cloneDeviceEventIdentityContext(context) {
1341
1962
  return {
1342
1963
  index: {
1964
+ aliasRepairContaminatedEventIds: new Set(context.index.aliasRepairContaminatedEventIds),
1965
+ aliasRepairContaminatedRefKeys: new Set(context.index.aliasRepairContaminatedRefKeys),
1966
+ aliasRepairHistoryById: new Map([...context.index.aliasRepairHistoryById].map(([id, history]) => [id, [...history]])),
1343
1967
  deviceOwnerRevisionsByRefKeyAndFingerprint: context.index.deviceOwnerRevisionsByRefKeyAndFingerprint,
1968
+ junctionNoIdProfilePredecessorsByScope: context.index.junctionNoIdProfilePredecessorsByScope,
1344
1969
  junctionSparseDayHistoryById: context.index.junctionSparseDayHistoryById,
1345
1970
  latestByRefKey: new Map(context.index.latestByRefKey),
1346
1971
  latestById: new Map(context.index.latestById),
1972
+ liveOwnerIdsByRefKey: new Map([...context.index.liveOwnerIdsByRefKey].map(([key, ids]) => [key, new Set(ids)])),
1347
1973
  maxRevisionById: new Map(context.index.maxRevisionById),
1348
- revisionsById: new Map(context.index.revisionsById),
1974
+ revisionsById: new Map([...context.index.revisionsById].map(([id, revisions]) => [id, new Set(revisions)])),
1349
1975
  },
1350
1976
  legacyReservations: new Map(context.legacyReservations),
1351
1977
  };
1352
1978
  }
1353
1979
  function buildEmptyDeviceEventIdentityContext(entries) {
1354
1980
  const index = {
1981
+ aliasRepairContaminatedEventIds: new Set(),
1982
+ aliasRepairContaminatedRefKeys: new Set(),
1983
+ aliasRepairHistoryById: new Map(),
1355
1984
  deviceOwnerRevisionsByRefKeyAndFingerprint: new Map(),
1985
+ junctionNoIdProfilePredecessorsByScope: new Map(),
1356
1986
  junctionSparseDayHistoryById: new Map(),
1357
1987
  latestByRefKey: new Map(),
1358
1988
  latestById: new Map(),
1989
+ liveOwnerIdsByRefKey: new Map(),
1359
1990
  maxRevisionById: new Map(),
1360
1991
  revisionsById: new Map(),
1361
1992
  };
@@ -1372,6 +2003,470 @@ function eventSpineRevisionsAreComplete(index, eventId) {
1372
2003
  && revisions !== undefined
1373
2004
  && revisions.size === maxRevision;
1374
2005
  }
2006
+ function buildDeviceEventAliasRepairContext(plan) {
2007
+ return {
2008
+ evidenceByRole: new Map(plan.preparedEvidenceParts.map((part) => [part.role, part])),
2009
+ evidenceRolesByPreparedRecordId: plan.evidenceRolesByPreparedRecordId,
2010
+ };
2011
+ }
2012
+ function isLooseRecordValue(value) {
2013
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2014
+ }
2015
+ function readRejectedEventExternalRefKey(value) {
2016
+ if (!isLooseRecordValue(value)) {
2017
+ return undefined;
2018
+ }
2019
+ const parsed = safeParseContract(externalRefSchema, {
2020
+ system: value.system,
2021
+ resourceType: value.resourceType,
2022
+ resourceId: value.resourceId,
2023
+ ...(value.facet === undefined ? {} : { facet: value.facet }),
2024
+ ...(value.version === undefined ? {} : { version: value.version }),
2025
+ });
2026
+ return parsed.success ? eventExternalRefKey(parsed.data) : undefined;
2027
+ }
2028
+ function junctionDailyAggregateAliasRefShape(externalRef) {
2029
+ return stableStringify({
2030
+ facet: externalRef.facet ?? null,
2031
+ resourceType: externalRef.resourceType,
2032
+ system: externalRef.system,
2033
+ version: externalRef.version ?? null,
2034
+ });
2035
+ }
2036
+ function junctionDailyAggregateAliasOriginKey(origin) {
2037
+ if (!origin) {
2038
+ return null;
2039
+ }
2040
+ const { observedAtRaw: _observedAtRaw, ...stableOrigin } = origin;
2041
+ return stableStringify(stableOrigin);
2042
+ }
2043
+ function junctionDailyAggregateAliasHistoricalOriginMatches(historical, current) {
2044
+ if (!historical) {
2045
+ return false;
2046
+ }
2047
+ if (junctionDailyAggregateAliasOriginKey(historical)
2048
+ === junctionDailyAggregateAliasOriginKey(current)) {
2049
+ return true;
2050
+ }
2051
+ if (historical.timeZoneOffsetMinutes !== undefined) {
2052
+ return false;
2053
+ }
2054
+ const { observedAtRaw: _historicalObservedAtRaw, timeZoneOffsetMinutes: _historicalOffset, ...stableHistorical } = historical;
2055
+ const { observedAtRaw: _currentObservedAtRaw, timeZoneOffsetMinutes: _currentOffset, ...stableCurrent } = current;
2056
+ return stableStringify(stableHistorical) === stableStringify(stableCurrent);
2057
+ }
2058
+ function junctionDailyAggregateProviderStateKey(record) {
2059
+ const { dataOrigin: _dataOrigin, dayKey: _dayKey, externalRef: _externalRef, id: _id, lifecycle: _lifecycle, links: _links, note: _note, recordedAt: _recordedAt, source: _source, tags: _tags, ...providerState } = record;
2060
+ return stableStringify(providerState);
2061
+ }
2062
+ function isJunctionDailyAggregateAliasCandidate(entry) {
2063
+ const externalRef = entry.record.externalRef;
2064
+ const legacyExternalRef = entry.legacyExternalRefs[0];
2065
+ return externalRef !== undefined
2066
+ && legacyExternalRef !== undefined
2067
+ && entry.legacyExternalRefs.length === 1
2068
+ && entry.record.kind === "observation"
2069
+ && entry.record.source === "device"
2070
+ && entry.record.dataOrigin?.aggregatorProvider === "junction"
2071
+ && entry.record.dataOrigin.normalizerVersion === "junction-normalizer.v1"
2072
+ && externalRef.system === "junction"
2073
+ && externalRef.version === undefined
2074
+ && legacyExternalRef.version === undefined
2075
+ && eventExternalRefKey(externalRef) !== eventExternalRefKey(legacyExternalRef)
2076
+ && junctionDailyAggregateAliasRefShape(externalRef)
2077
+ === junctionDailyAggregateAliasRefShape(legacyExternalRef)
2078
+ && entry.record.note === undefined
2079
+ && entry.record.tags === undefined
2080
+ && entry.record.links === undefined
2081
+ && entry.record.rawRefs === undefined
2082
+ && entry.record.evidence === undefined
2083
+ && entry.record.attachments === undefined;
2084
+ }
2085
+ function resolveStructuralJunctionDailyAggregateAliasOwnerSplit(entry, index) {
2086
+ const externalRef = entry.record.externalRef;
2087
+ const legacyExternalRef = entry.legacyExternalRefs[0];
2088
+ if (!externalRef
2089
+ || !legacyExternalRef
2090
+ || !isJunctionDailyAggregateAliasCandidate(entry)) {
2091
+ return null;
2092
+ }
2093
+ const primaryRefKey = eventExternalRefKey(externalRef);
2094
+ const legacyRefKey = eventExternalRefKey(legacyExternalRef);
2095
+ const primaryOwnerIds = index.liveOwnerIdsByRefKey.get(primaryRefKey)
2096
+ ?? new Set();
2097
+ const legacyOwnerIds = index.liveOwnerIdsByRefKey.get(legacyRefKey)
2098
+ ?? new Set();
2099
+ if (primaryOwnerIds.size === 0 || legacyOwnerIds.size === 0) {
2100
+ return null;
2101
+ }
2102
+ const primaryOwner = index.latestByRefKey.get(primaryRefKey);
2103
+ const legacyOwner = index.latestByRefKey.get(legacyRefKey);
2104
+ if (!primaryOwner || !legacyOwner) {
2105
+ return null;
2106
+ }
2107
+ const primaryProviderRecord = primaryOwner.indexedRecord;
2108
+ const legacyProviderRecord = legacyOwner.indexedRecord;
2109
+ const primaryOrigin = primaryProviderRecord.dataOrigin;
2110
+ const legacyOrigin = legacyProviderRecord.dataOrigin;
2111
+ const incomingOrigin = entry.record.dataOrigin;
2112
+ const observedAtRaw = incomingOrigin?.observedAtRaw;
2113
+ const observedAtRawPrefix = `${entry.record.dayKey}:`;
2114
+ const resource = observedAtRaw?.startsWith(observedAtRawPrefix)
2115
+ && observedAtRaw.endsWith(":daily")
2116
+ ? observedAtRaw.slice(observedAtRawPrefix.length, -":daily".length)
2117
+ : "";
2118
+ if (primaryProviderRecord.dayKey !== entry.record.dayKey
2119
+ || !isSameObservationFacet(primaryProviderRecord, entry.record)
2120
+ || !isSameObservationFacet(primaryProviderRecord, legacyProviderRecord)
2121
+ || junctionDailyAggregateAliasRefShape(primaryOwner.indexedExternalRef)
2122
+ !== junctionDailyAggregateAliasRefShape(externalRef)
2123
+ || junctionDailyAggregateAliasRefShape(legacyOwner.indexedExternalRef)
2124
+ !== junctionDailyAggregateAliasRefShape(legacyExternalRef)
2125
+ || !primaryOrigin
2126
+ || !legacyOrigin
2127
+ || !incomingOrigin
2128
+ || resource.length === 0
2129
+ || !deviceDataOriginSourceMatches(primaryOrigin, incomingOrigin)
2130
+ || !deviceDataOriginSourceMatches(legacyOrigin, incomingOrigin)) {
2131
+ return null;
2132
+ }
2133
+ const distinctOwnerIds = new Set([...primaryOwnerIds, ...legacyOwnerIds]);
2134
+ if (distinctOwnerIds.size < 2) {
2135
+ return null;
2136
+ }
2137
+ if (primaryOwnerIds.size !== 1
2138
+ || legacyOwnerIds.size !== 1
2139
+ || distinctOwnerIds.size !== 2) {
2140
+ throw new VaultError("EVENT_ALIAS_REPAIR_OWNER_REFUSED", "Junction daily aggregate alias repair requires exactly one live owner per claimed reference.");
2141
+ }
2142
+ if (!primaryOwnerIds.has(primaryOwner.record.id)
2143
+ || !legacyOwnerIds.has(legacyOwner.record.id)
2144
+ || primaryOwner.record.id === legacyOwner.record.id) {
2145
+ throw new VaultError("EVENT_ALIAS_REPAIR_OWNER_REFUSED", "Junction daily aggregate alias repair could not prove the two live reference owners.");
2146
+ }
2147
+ return { legacyOwner, legacyRefKey, primaryOwner, primaryRefKey, resource };
2148
+ }
2149
+ function assertJunctionDailyAggregateAliasHistoryIsUncontaminated(entry, context) {
2150
+ const externalRef = entry.record.externalRef;
2151
+ const legacyExternalRef = entry.legacyExternalRefs[0];
2152
+ if (!externalRef || !legacyExternalRef) {
2153
+ return;
2154
+ }
2155
+ const primaryRefKey = eventExternalRefKey(externalRef);
2156
+ const legacyRefKey = eventExternalRefKey(legacyExternalRef);
2157
+ const ownerIds = new Set([
2158
+ ...(context.index.liveOwnerIdsByRefKey.get(primaryRefKey) ?? []),
2159
+ ...(context.index.liveOwnerIdsByRefKey.get(legacyRefKey) ?? []),
2160
+ ]);
2161
+ if ([...ownerIds].some((id) => context.index.aliasRepairContaminatedEventIds.has(id))
2162
+ || context.index.aliasRepairContaminatedRefKeys.has(primaryRefKey)
2163
+ || context.index.aliasRepairContaminatedRefKeys.has(legacyRefKey)) {
2164
+ throw new VaultError("EVENT_ALIAS_REPAIR_HISTORY_REFUSED", "Junction daily aggregate alias repair found a schema-rejected candidate revision.");
2165
+ }
2166
+ }
2167
+ function parseJunctionDailyAggregateAliasEvidence(entry, context) {
2168
+ const roles = context.evidenceRolesByPreparedRecordId.get(entry.record.id) ?? [];
2169
+ const part = roles.length === 1 ? context.evidenceByRole.get(roles[0]) : undefined;
2170
+ const metadata = part?.metadata;
2171
+ if (!part
2172
+ || !isLooseRecordValue(metadata)
2173
+ || metadata.artifactClass !== "compact_provider_timeseries_aggregate"
2174
+ || metadata.provider !== "junction"
2175
+ || metadata.resourceCategory !== "timeseries_daily_aggregate"
2176
+ || metadata.retentionClass !== "provider_evidence") {
2177
+ throw new VaultError("EVENT_ALIAS_REPAIR_EVIDENCE_REFUSED", "Junction daily aggregate alias repair requires one exact compact provider evidence role.");
2178
+ }
2179
+ let parsed;
2180
+ try {
2181
+ parsed = JSON.parse(part.content);
2182
+ }
2183
+ catch {
2184
+ throw new VaultError("EVENT_ALIAS_REPAIR_EVIDENCE_REFUSED", "Junction daily aggregate alias repair evidence was not valid JSON.");
2185
+ }
2186
+ if (!isLooseRecordValue(parsed)) {
2187
+ throw new VaultError("EVENT_ALIAS_REPAIR_EVIDENCE_REFUSED", "Junction daily aggregate alias repair evidence did not contain an object.");
2188
+ }
2189
+ const legacyDayKeys = parsed.legacyDayKeys;
2190
+ const origin = entry.record.dataOrigin;
2191
+ if (parsed.schema !== "junction.timeseries_daily_aggregate.v1"
2192
+ || parsed.provider !== "junction"
2193
+ || typeof parsed.resource !== "string"
2194
+ || parsed.resource.length === 0
2195
+ || metadata.resource !== parsed.resource
2196
+ || typeof parsed.dayKey !== "string"
2197
+ || !/^\d{4}-\d{2}-\d{2}$/u.test(parsed.dayKey)
2198
+ || parsed.dayKey !== entry.record.dayKey
2199
+ || typeof parsed.sourceProviderSlug !== "string"
2200
+ || parsed.sourceProviderSlug.length === 0
2201
+ || typeof parsed.sampleCount !== "number"
2202
+ || !Number.isSafeInteger(parsed.sampleCount)
2203
+ || parsed.sampleCount <= 0
2204
+ || !Array.isArray(legacyDayKeys)
2205
+ || legacyDayKeys.length !== 1
2206
+ || typeof legacyDayKeys[0] !== "string"
2207
+ || !/^\d{4}-\d{2}-\d{2}$/u.test(legacyDayKeys[0])
2208
+ || legacyDayKeys[0] === parsed.dayKey
2209
+ || (parsed.sourceType !== undefined && typeof parsed.sourceType !== "string")
2210
+ || (parsed.sourceInstanceId !== undefined
2211
+ && parsed.sourceInstanceId !== null
2212
+ && typeof parsed.sourceInstanceId !== "string")
2213
+ || origin?.sourceProviderSlug !== parsed.sourceProviderSlug
2214
+ || (origin.sourceType ?? null) !== (parsed.sourceType ?? null)
2215
+ || (origin.sourceInstanceId ?? null) !== (parsed.sourceInstanceId ?? null)
2216
+ || origin.observedAtRaw !== `${parsed.dayKey}:${parsed.resource}:daily`) {
2217
+ throw new VaultError("EVENT_ALIAS_REPAIR_EVIDENCE_REFUSED", "Junction daily aggregate alias repair evidence did not prove one normalized primary and legacy day split.");
2218
+ }
2219
+ return {
2220
+ dayKey: parsed.dayKey,
2221
+ legacyDayKey: legacyDayKeys[0],
2222
+ resource: parsed.resource,
2223
+ };
2224
+ }
2225
+ function readCompleteJunctionDailyAggregateAliasHistory(index, eventId) {
2226
+ const history = index.aliasRepairHistoryById.get(eventId) ?? [];
2227
+ const maxRevision = index.maxRevisionById.get(eventId) ?? 0;
2228
+ const revisions = index.revisionsById.get(eventId);
2229
+ if (maxRevision < 1
2230
+ || maxRevision > MAX_JUNCTION_DAILY_ALIAS_REPAIR_REVISIONS
2231
+ || !revisions
2232
+ || revisions.size !== maxRevision
2233
+ || history.length !== maxRevision
2234
+ || history.some((entry, historyIndex) => eventSpineRevision(entry.record) !== historyIndex + 1)) {
2235
+ throw new VaultError("EVENT_ALIAS_REPAIR_HISTORY_REFUSED", "Junction daily aggregate alias repair requires a complete bounded event history.");
2236
+ }
2237
+ return history;
2238
+ }
2239
+ function junctionDailyAggregateAliasPersistedOriginsMatch(left, right) {
2240
+ if (!left || !right) {
2241
+ return false;
2242
+ }
2243
+ return junctionDailyAggregateAliasHistoricalOriginMatches(left, right)
2244
+ || junctionDailyAggregateAliasHistoricalOriginMatches(right, left);
2245
+ }
2246
+ function junctionDailyAggregateAliasInitialRecordMatches(record, input) {
2247
+ return record !== undefined
2248
+ && record.source === "device"
2249
+ && !isDeletedEventSpineRecord(record)
2250
+ && record.dayKey === input.expectedDayKey
2251
+ && record.dataOrigin?.observedAtRaw === input.expectedObservedAtRaw
2252
+ && record.externalRef !== undefined
2253
+ && eventExternalRefKey(record.externalRef)
2254
+ === eventExternalRefKey(input.expectedExternalRef)
2255
+ && junctionDailyAggregateAliasRefShape(record.externalRef)
2256
+ === junctionDailyAggregateAliasRefShape(input.expectedExternalRef);
2257
+ }
2258
+ function analyzeJunctionDailyAggregateAliasHistoryEvolution(input) {
2259
+ const expectedRefKey = eventExternalRefKey(input.expectedExternalRef);
2260
+ let providerStateKey = input.initialProviderStateKey;
2261
+ let memberCarrierStateKey;
2262
+ for (const { record } of input.history) {
2263
+ const recordProviderStateKey = junctionDailyAggregateProviderStateKey(record);
2264
+ const providerRecord = record.source === "device";
2265
+ const manualMatchesProvider = recordProviderStateKey === providerStateKey;
2266
+ const manualMatchesCarrier = memberCarrierStateKey !== undefined
2267
+ && recordProviderStateKey === memberCarrierStateKey;
2268
+ if (isDeletedEventSpineRecord(record)
2269
+ || (record.source !== "device" && record.source !== "manual")
2270
+ || record.dayKey !== input.expectedDayKey
2271
+ || !isSameObservationFacet(input.initialRecord, record)
2272
+ || (providerRecord
2273
+ && (!record.externalRef
2274
+ || !record.dataOrigin))
2275
+ || (record.externalRef !== undefined
2276
+ && (eventExternalRefKey(record.externalRef) !== expectedRefKey
2277
+ || junctionDailyAggregateAliasRefShape(record.externalRef)
2278
+ !== junctionDailyAggregateAliasRefShape(input.expectedExternalRef)))
2279
+ || (record.dataOrigin !== undefined
2280
+ && (record.dataOrigin.observedAtRaw !== input.expectedObservedAtRaw
2281
+ || !junctionDailyAggregateAliasHistoricalOriginMatches(record.dataOrigin, input.expectedOrigin)))
2282
+ || (providerRecord
2283
+ ? !input.allowProviderEvolution
2284
+ && recordProviderStateKey !== input.initialProviderStateKey
2285
+ : !manualMatchesProvider && !manualMatchesCarrier)) {
2286
+ throw new VaultError("EVENT_ALIAS_REPAIR_HISTORY_REFUSED", "Junction daily aggregate alias repair found unsupported or divergent historical state.");
2287
+ }
2288
+ if (providerRecord) {
2289
+ providerStateKey = recordProviderStateKey;
2290
+ }
2291
+ else if (manualMatchesProvider) {
2292
+ memberCarrierStateKey = recordProviderStateKey;
2293
+ }
2294
+ }
2295
+ const latest = input.history.at(-1)?.record;
2296
+ if (!latest) {
2297
+ throw new VaultError("EVENT_ALIAS_REPAIR_HISTORY_REFUSED", "Junction daily aggregate alias repair requires a complete bounded event history.");
2298
+ }
2299
+ const links = [...(latest.links ?? [])];
2300
+ const tags = [...(latest.tags ?? [])];
2301
+ return {
2302
+ links,
2303
+ memberAuthored: latest.source === "manual"
2304
+ || latest.note !== undefined
2305
+ || tags.length > 0
2306
+ || links.length > 0,
2307
+ ...(latest.note ? { note: latest.note } : {}),
2308
+ tags,
2309
+ };
2310
+ }
2311
+ function resolveJunctionDailyAggregateAliasOwnerSplit(entry, index) {
2312
+ const ownerSplit = resolveStructuralJunctionDailyAggregateAliasOwnerSplit(entry, index);
2313
+ const externalRef = entry.record.externalRef;
2314
+ const legacyExternalRef = entry.legacyExternalRefs[0];
2315
+ if (!ownerSplit || !externalRef || !legacyExternalRef || !entry.record.dataOrigin) {
2316
+ return null;
2317
+ }
2318
+ const primaryInitial = index.aliasRepairHistoryById.get(ownerSplit.primaryOwner.record.id)?.[0]?.record;
2319
+ const legacyInitial = index.aliasRepairHistoryById.get(ownerSplit.legacyOwner.record.id)?.[0]?.record;
2320
+ const legacyDayKey = ownerSplit.legacyOwner.indexedRecord.dayKey;
2321
+ // Divergent initial baselines are ordinary adjacent-day owners. Once the
2322
+ // baselines match, every later revision must satisfy the bounded evolution
2323
+ // rules before this pair can reserve or mutate either identity.
2324
+ if (!junctionDailyAggregateAliasInitialRecordMatches(primaryInitial, {
2325
+ expectedDayKey: entry.record.dayKey,
2326
+ expectedExternalRef: externalRef,
2327
+ expectedObservedAtRaw: `${entry.record.dayKey}:${ownerSplit.resource}:daily`,
2328
+ })
2329
+ || !junctionDailyAggregateAliasInitialRecordMatches(legacyInitial, {
2330
+ expectedDayKey: legacyDayKey,
2331
+ expectedExternalRef: legacyExternalRef,
2332
+ expectedObservedAtRaw: `${legacyDayKey}:${ownerSplit.resource}:daily`,
2333
+ })
2334
+ || !isSameObservationFacet(primaryInitial, legacyInitial)
2335
+ || junctionDailyAggregateProviderStateKey(primaryInitial)
2336
+ !== junctionDailyAggregateProviderStateKey(legacyInitial)
2337
+ || !junctionDailyAggregateAliasPersistedOriginsMatch(primaryInitial.dataOrigin, legacyInitial.dataOrigin)) {
2338
+ return null;
2339
+ }
2340
+ const primaryHistory = readCompleteJunctionDailyAggregateAliasHistory(index, ownerSplit.primaryOwner.record.id);
2341
+ const legacyHistory = readCompleteJunctionDailyAggregateAliasHistory(index, ownerSplit.legacyOwner.record.id);
2342
+ const primaryOverlay = analyzeJunctionDailyAggregateAliasHistoryEvolution({
2343
+ allowProviderEvolution: true,
2344
+ expectedDayKey: entry.record.dayKey,
2345
+ expectedExternalRef: externalRef,
2346
+ expectedOrigin: entry.record.dataOrigin,
2347
+ expectedObservedAtRaw: `${entry.record.dayKey}:${ownerSplit.resource}:daily`,
2348
+ history: primaryHistory,
2349
+ initialProviderStateKey: junctionDailyAggregateProviderStateKey(primaryInitial),
2350
+ initialRecord: primaryInitial,
2351
+ });
2352
+ const legacyOverlay = analyzeJunctionDailyAggregateAliasHistoryEvolution({
2353
+ allowProviderEvolution: false,
2354
+ expectedDayKey: legacyDayKey,
2355
+ expectedExternalRef: legacyExternalRef,
2356
+ expectedOrigin: entry.record.dataOrigin,
2357
+ expectedObservedAtRaw: `${legacyDayKey}:${ownerSplit.resource}:daily`,
2358
+ history: legacyHistory,
2359
+ initialProviderStateKey: junctionDailyAggregateProviderStateKey(legacyInitial),
2360
+ initialRecord: legacyInitial,
2361
+ });
2362
+ return { ...ownerSplit, legacyOverlay, primaryOverlay };
2363
+ }
2364
+ function hasJunctionDailyAggregateAliasSplit(entry, context) {
2365
+ return resolveJunctionDailyAggregateAliasOwnerSplit(entry, context.index) !== null;
2366
+ }
2367
+ function mergeJunctionDailyAggregateAliasOverlays(primary, legacy) {
2368
+ if (primary.note && legacy.note && primary.note !== legacy.note) {
2369
+ throw new VaultError("EVENT_ALIAS_REPAIR_OVERLAY_REFUSED", "Junction daily aggregate alias repair found conflicting member notes.");
2370
+ }
2371
+ const linksByKey = new Map();
2372
+ for (const link of [...primary.links, ...legacy.links]) {
2373
+ linksByKey.set(stableStringify(link), link);
2374
+ }
2375
+ return {
2376
+ links: [...linksByKey.entries()]
2377
+ .sort(([left], [right]) => left.localeCompare(right))
2378
+ .map(([, link]) => link),
2379
+ memberAuthored: primary.memberAuthored || legacy.memberAuthored,
2380
+ ...(primary.note || legacy.note ? { note: primary.note ?? legacy.note } : {}),
2381
+ tags: [...new Set([...primary.tags, ...legacy.tags])].sort(),
2382
+ };
2383
+ }
2384
+ function buildJunctionDailyAggregateAliasRepairPlan(input) {
2385
+ const { aliasRepairContext, context, entry } = input;
2386
+ const externalRef = entry.record.externalRef;
2387
+ const legacyExternalRef = entry.legacyExternalRefs[0];
2388
+ const ownerSplit = resolveJunctionDailyAggregateAliasOwnerSplit(entry, context.index);
2389
+ if (!aliasRepairContext
2390
+ || !externalRef
2391
+ || !legacyExternalRef
2392
+ || !ownerSplit) {
2393
+ return null;
2394
+ }
2395
+ assertJunctionDailyAggregateAliasHistoryIsUncontaminated(entry, context);
2396
+ const { legacyOverlay, legacyOwner, legacyRefKey, primaryOverlay, primaryOwner, primaryRefKey, } = ownerSplit;
2397
+ const evidence = parseJunctionDailyAggregateAliasEvidence(entry, aliasRepairContext);
2398
+ if (primaryOwner.record.dayKey !== evidence.dayKey
2399
+ || legacyOwner.record.dayKey !== evidence.legacyDayKey
2400
+ || ownerSplit.resource !== evidence.resource) {
2401
+ throw new VaultError("EVENT_ALIAS_REPAIR_HISTORY_REFUSED", "Junction daily aggregate alias repair owner days did not match the normalized evidence.");
2402
+ }
2403
+ const overlay = mergeJunctionDailyAggregateAliasOverlays(primaryOverlay, legacyOverlay);
2404
+ const primaryRevision = (context.index.maxRevisionById.get(primaryOwner.record.id) ?? 0) + 1;
2405
+ const legacyRevision = (context.index.maxRevisionById.get(legacyOwner.record.id) ?? 0) + 1;
2406
+ const providerSurvivor = {
2407
+ ...entry.record,
2408
+ id: primaryOwner.record.id,
2409
+ lifecycle: buildEventSpineLifecycle(primaryRevision),
2410
+ };
2411
+ const loserTombstone = {
2412
+ ...entry.record,
2413
+ id: legacyOwner.record.id,
2414
+ lifecycle: buildEventSpineLifecycle(legacyRevision, "deleted"),
2415
+ };
2416
+ const overlaySurvivor = overlay.memberAuthored
2417
+ ? {
2418
+ ...providerSurvivor,
2419
+ source: "manual",
2420
+ ...(overlay.note ? { note: overlay.note } : {}),
2421
+ ...(overlay.tags.length > 0 ? { tags: overlay.tags } : {}),
2422
+ ...(overlay.links.length > 0 ? { links: overlay.links } : {}),
2423
+ lifecycle: buildEventSpineLifecycle(primaryRevision + 1),
2424
+ }
2425
+ : undefined;
2426
+ return {
2427
+ legacyRefKey,
2428
+ loserPath: legacyOwner.relativePath || toEventLedgerFile(legacyOwner.record.occurredAt),
2429
+ loserTombstone,
2430
+ ...(overlaySurvivor ? { overlaySurvivor } : {}),
2431
+ primaryExternalRef: externalRef,
2432
+ primaryRefKey,
2433
+ providerSurvivor,
2434
+ survivorPath: entry.relativePath,
2435
+ };
2436
+ }
2437
+ function applyJunctionDailyAggregateAliasRepairToIndex(plan, index) {
2438
+ const survivor = plan.overlaySurvivor ?? plan.providerSurvivor;
2439
+ index.latestByRefKey.set(plan.primaryRefKey, {
2440
+ indexedExternalRef: plan.primaryExternalRef,
2441
+ indexedRecord: plan.providerSurvivor,
2442
+ relativePath: plan.survivorPath,
2443
+ record: survivor,
2444
+ });
2445
+ index.latestByRefKey.delete(plan.legacyRefKey);
2446
+ index.latestById.set(survivor.id, survivor);
2447
+ index.latestById.set(plan.loserTombstone.id, plan.loserTombstone);
2448
+ index.liveOwnerIdsByRefKey.set(plan.primaryRefKey, new Set([survivor.id]));
2449
+ index.liveOwnerIdsByRefKey.delete(plan.legacyRefKey);
2450
+ for (const record of [
2451
+ plan.providerSurvivor,
2452
+ ...(plan.overlaySurvivor ? [plan.overlaySurvivor] : []),
2453
+ plan.loserTombstone,
2454
+ ]) {
2455
+ const revision = eventSpineRevision(record);
2456
+ index.maxRevisionById.set(record.id, revision);
2457
+ const revisions = index.revisionsById.get(record.id) ?? new Set();
2458
+ revisions.add(revision);
2459
+ index.revisionsById.set(record.id, revisions);
2460
+ const history = index.aliasRepairHistoryById.get(record.id) ?? [];
2461
+ history.push({
2462
+ relativePath: record.id === plan.loserTombstone.id
2463
+ ? plan.loserPath
2464
+ : plan.survivorPath,
2465
+ record,
2466
+ });
2467
+ index.aliasRepairHistoryById.set(record.id, history);
2468
+ }
2469
+ }
1375
2470
  function whoopSleepTypeProviderBaselineRevision(index, refKey, eventId, incoming) {
1376
2471
  if (incoming.kind !== "sleep_session"
1377
2472
  || incoming.externalRef?.system !== "whoop"
@@ -1414,6 +2509,7 @@ function resolveDeviceEventIdentity(entry, context, options = {}) {
1414
2509
  const primaryMatch = reservedForLegacyClaim && reservedForLegacyClaim.entry !== entry
1415
2510
  ? undefined
1416
2511
  : index.latestByRefKey.get(refKey);
2512
+ const explicitLegacyRefKeys = new Set(entry.legacyExternalRefs.map((legacyExternalRef) => eventExternalRefKey(legacyExternalRef)));
1417
2513
  const legacyMatchedEntries = entry.legacyExternalRefs
1418
2514
  .map((legacyExternalRef) => ({
1419
2515
  legacyExternalRef,
@@ -1438,9 +2534,18 @@ function resolveDeviceEventIdentity(entry, context, options = {}) {
1438
2534
  return (!reservation || reservation.entry === entry)
1439
2535
  && isCompatibleLegacyExternalRefMatch(match.indexedMatch, entry.record, match.legacyExternalRef);
1440
2536
  });
2537
+ const implicitReservedEntries = [...legacyReservations.entries()]
2538
+ .filter(([reservedRefKey, reservation]) => reservation.entry === entry
2539
+ && reservedRefKey !== refKey
2540
+ && !explicitLegacyRefKeys.has(reservedRefKey))
2541
+ .map(([reservedRefKey, reservation]) => ({
2542
+ refKey: reservedRefKey,
2543
+ indexedMatch: reservation.indexedMatch,
2544
+ }));
1441
2545
  const matchedEntries = [
1442
2546
  ...(primaryMatch ? [{ refKey, indexedMatch: primaryMatch }] : []),
1443
2547
  ...legacyMatchedEntries,
2548
+ ...implicitReservedEntries,
1444
2549
  ];
1445
2550
  const selectedPrimaryMatch = matchedEntries.find((match) => match.refKey === refKey);
1446
2551
  const latest = selectedPrimaryMatch?.indexedMatch.record ?? matchedEntries[0]?.indexedMatch.record;
@@ -1547,6 +2652,8 @@ function mapCurrentDeviceEventOwners(entries, context) {
1547
2652
  ? []
1548
2653
  : resolved.matchedEntries.map(({ indexedMatch }) => compareIncomingExternalRefVersion(indexedMatch.indexedExternalRef, incomingExternalRef)).filter((comparison) => comparison !== null);
1549
2654
  const incomingHasEqualOrNewerComparableVersion = sourceVersionComparisons.some((comparison) => comparison >= 0);
2655
+ const incomingHasOnlyOlderComparableVersions = sourceVersionComparisons.length > 0
2656
+ && !incomingHasEqualOrNewerComparableVersion;
1550
2657
  if (sourceVersionComparisons.some((comparison) => comparison > 0)) {
1551
2658
  incomingNewerPreparedIds.add(entry.record.id);
1552
2659
  }
@@ -1556,6 +2663,7 @@ function mapCurrentDeviceEventOwners(entries, context) {
1556
2663
  historicallyDeliveredPreparedIds.add(entry.record.id);
1557
2664
  }
1558
2665
  if (resolved.associationSafe
2666
+ && !incomingHasOnlyOlderComparableVersions
1559
2667
  && !isDeletedEventSpineRecord(current)
1560
2668
  && deviceEventContentKey(current) === deviceEventContentKey(entry.record)) {
1561
2669
  associationSafePreparedIds.add(entry.record.id);
@@ -1579,18 +2687,63 @@ function mapCurrentDeviceEventOwners(entries, context) {
1579
2687
  // skipped; changed content normally appends an event-spine revision onto the
1580
2688
  // existing event id instead of a new event. Callers may mark a capture's
1581
2689
  // externalRef immutable when changed content must be rejected instead.
1582
- async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, existingContext, preferredCanonicalIdByPreparedId = new Map()) {
2690
+ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, existingContext, preferredCanonicalIdByPreparedId = new Map(), authoritativeEventSets = [], aliasRepairContext) {
1583
2691
  assertCanonicalWriteLockScope(vaultRoot);
1584
2692
  const context = existingContext ?? await buildDeviceEventIdentityContext(vaultRoot, entries);
1585
2693
  const { index } = context;
1586
2694
  const appendEntries = [];
1587
2695
  const appendRecordIdByPreparedRecordId = new Map();
1588
- const records = [];
2696
+ const recordsByEntryIndex = new Map();
1589
2697
  const forceAppendIds = new Set();
1590
2698
  const retainedPreparedIds = new Set();
1591
2699
  let skippedDuplicateCount = 0;
1592
2700
  let supersededCount = 0;
1593
- for (const entry of entries) {
2701
+ let retractedCount = 0;
2702
+ const aliasRepairByEntryIndex = new Map();
2703
+ for (const [entryIndex, entry] of entries.entries()) {
2704
+ const aliasRepair = buildJunctionDailyAggregateAliasRepairPlan({
2705
+ aliasRepairContext,
2706
+ context,
2707
+ entry,
2708
+ });
2709
+ if (aliasRepair) {
2710
+ aliasRepairByEntryIndex.set(entryIndex, aliasRepair);
2711
+ }
2712
+ }
2713
+ const aliasRepairOwnerIds = new Set();
2714
+ for (const aliasRepair of aliasRepairByEntryIndex.values()) {
2715
+ for (const ownerId of [
2716
+ aliasRepair.providerSurvivor.id,
2717
+ aliasRepair.loserTombstone.id,
2718
+ ]) {
2719
+ if (aliasRepairOwnerIds.has(ownerId)) {
2720
+ throw new VaultError("EVENT_ALIAS_REPAIR_OWNER_REFUSED", "Junction daily aggregate alias repair operations cannot share persisted owners.");
2721
+ }
2722
+ aliasRepairOwnerIds.add(ownerId);
2723
+ }
2724
+ }
2725
+ for (const [entryIndex, aliasRepair] of aliasRepairByEntryIndex) {
2726
+ const entry = entries[entryIndex];
2727
+ appendEntries.push({ relativePath: aliasRepair.survivorPath, record: aliasRepair.providerSurvivor }, { relativePath: aliasRepair.loserPath, record: aliasRepair.loserTombstone });
2728
+ if (aliasRepair.overlaySurvivor) {
2729
+ appendEntries.push({
2730
+ relativePath: aliasRepair.survivorPath,
2731
+ record: aliasRepair.overlaySurvivor,
2732
+ });
2733
+ }
2734
+ appendRecordIdByPreparedRecordId.set(entry.record.id, aliasRepair.providerSurvivor.id);
2735
+ forceAppendIds.add(aliasRepair.providerSurvivor.id);
2736
+ forceAppendIds.add(aliasRepair.loserTombstone.id);
2737
+ recordsByEntryIndex.set(entryIndex, aliasRepair.overlaySurvivor ?? aliasRepair.providerSurvivor);
2738
+ applyJunctionDailyAggregateAliasRepairToIndex(aliasRepair, index);
2739
+ supersededCount += 1;
2740
+ retractedCount += 1;
2741
+ }
2742
+ for (const [entryIndex, originalEntry] of entries.entries()) {
2743
+ if (aliasRepairByEntryIndex.has(entryIndex)) {
2744
+ continue;
2745
+ }
2746
+ let entry = originalEntry;
1594
2747
  const externalRef = entry.record.externalRef;
1595
2748
  if (!externalRef) {
1596
2749
  const current = index.latestById.get(entry.record.id);
@@ -1599,20 +2752,25 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1599
2752
  && deviceEventContentKey(current) === deviceEventContentKey(entry.record)) {
1600
2753
  skippedDuplicateCount += 1;
1601
2754
  retainedPreparedIds.add(entry.record.id);
1602
- records.push(current);
2755
+ recordsByEntryIndex.set(entryIndex, current);
1603
2756
  continue;
1604
2757
  }
1605
2758
  appendEntries.push(entry);
1606
2759
  appendRecordIdByPreparedRecordId.set(entry.record.id, entry.record.id);
1607
- records.push(entry.record);
2760
+ recordsByEntryIndex.set(entryIndex, entry.record);
1608
2761
  continue;
1609
2762
  }
1610
2763
  const resolved = resolveDeviceEventIdentity(entry, context, { strict: true });
1611
2764
  if (!resolved) {
1612
2765
  throw new VaultError("EVENT_EXTERNAL_REF_ALIAS_CONFLICT", "Device event identity unexpectedly lost its externalRef during reconciliation.");
1613
2766
  }
2767
+ if (resolved.latest && aliasRepairOwnerIds.has(resolved.latest.id)) {
2768
+ throw new VaultError("EVENT_ALIAS_REPAIR_OWNER_REFUSED", "Junction daily aggregate alias repair operations cannot share persisted owners.");
2769
+ }
1614
2770
  const { matchedEntries, refKey } = resolved;
1615
2771
  let latest = resolved.latest;
2772
+ const migratesJunctionNoIdProfileIdentity = isIncomingJunctionNoIdProfile(entry.record)
2773
+ && matchedEntries.some((match) => match.refKey !== refKey);
1616
2774
  if (!latest) {
1617
2775
  const canonicalRecordId = preferredCanonicalIdByPreparedId.get(entry.record.id)
1618
2776
  ?? entry.record.id;
@@ -1622,9 +2780,59 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1622
2780
  index.latestByRefKey.set(refKey, toIndexedExternalRefMatch(canonicalRecord, externalRef));
1623
2781
  appendEntries.push({ relativePath: entry.relativePath, record: canonicalRecord });
1624
2782
  appendRecordIdByPreparedRecordId.set(entry.record.id, canonicalRecord.id);
1625
- records.push(canonicalRecord);
2783
+ recordsByEntryIndex.set(entryIndex, canonicalRecord);
2784
+ continue;
2785
+ }
2786
+ const indexedProviderMatch = matchedEntries.find((match) => match.indexedMatch.record.id === latest.id)?.indexedMatch ?? matchedEntries[0]?.indexedMatch;
2787
+ const replaysJunctionNoIdProfileTimestamp = indexedProviderMatch !== undefined
2788
+ && isJunctionNoIdProfileEqualRevisionTimestampReplay(indexedProviderMatch.indexedRecord, entry.record);
2789
+ if (replaysJunctionNoIdProfileTimestamp) {
2790
+ skippedDuplicateCount += 1;
2791
+ retainedPreparedIds.add(entry.record.id);
2792
+ recordsByEntryIndex.set(entryIndex, latest);
1626
2793
  continue;
1627
2794
  }
2795
+ const matchesIndexedProviderContent = indexedProviderMatch !== undefined
2796
+ && deviceEventContentKey(indexedProviderMatch.indexedRecord)
2797
+ === deviceEventContentKey(entry.record);
2798
+ const indexedSourceVersionComparison = indexedProviderMatch
2799
+ ? compareIncomingExternalRefVersion(indexedProviderMatch.indexedExternalRef, externalRef)
2800
+ : null;
2801
+ const migratesJunctionStableProfileTimestamp = indexedProviderMatch !== undefined
2802
+ && isJunctionStableProfileCreatedAtTimestampMigration(indexedProviderMatch.indexedRecord, entry.record);
2803
+ const retainsDeletedJunctionStableProfile = isMemberDeletedJunctionStableProfile(latest, entry.record);
2804
+ if (isDeletedEventSpineRecord(latest)
2805
+ && (migratesJunctionStableProfileTimestamp || retainsDeletedJunctionStableProfile)) {
2806
+ skippedDuplicateCount += 1;
2807
+ retainedPreparedIds.add(entry.record.id);
2808
+ recordsByEntryIndex.set(entryIndex, latest);
2809
+ continue;
2810
+ }
2811
+ const authoritativeSet = authoritativeEventSets.find((set) => externalRef.system === set.system
2812
+ && externalRef.resourceType === set.resourceType
2813
+ && externalRef.resourceId === set.resourceId
2814
+ && externalRef.facet !== undefined
2815
+ && set.currentFacets.has(externalRef.facet));
2816
+ if (authoritativeSet) {
2817
+ const sourceVersionComparison = compareIncomingExternalRefVersion(matchedEntries.find((match) => match.refKey === refKey)?.indexedMatch.indexedExternalRef
2818
+ ?? matchedEntries[0]?.indexedMatch.indexedExternalRef
2819
+ ?? latest.externalRef
2820
+ ?? externalRef, externalRef);
2821
+ if (sourceVersionComparison !== null && sourceVersionComparison < 0) {
2822
+ skippedDuplicateCount += 1;
2823
+ recordsByEntryIndex.set(entryIndex, latest);
2824
+ continue;
2825
+ }
2826
+ if (sourceVersionComparison === 0
2827
+ && (isDeletedEventSpineRecord(latest)
2828
+ || deviceEventContentKey(latest) !== deviceEventContentKey(entry.record))
2829
+ && !matchesIndexedProviderContent
2830
+ && !migratesJunctionStableProfileTimestamp) {
2831
+ throw new VaultError("EVENT_SOURCE_REVISION_CONFLICT", `Authoritative device event externalRef "${externalRef.system}/${externalRef.resourceType}/` +
2832
+ `${externalRef.resourceId}#${externalRef.facet}" has conflicting content for source revision ` +
2833
+ `"${authoritativeSet.version}"; nothing was imported.`);
2834
+ }
2835
+ }
1628
2836
  // externalRef identity does not include kind. Event spines are kind-stable,
1629
2837
  // so device reconciliation must reject under-faceted provider refs instead
1630
2838
  // of rewriting an existing event id as a different event kind.
@@ -1633,20 +2841,79 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1633
2841
  `${externalRef.facet ? `#${externalRef.facet}` : ""}" already belongs to kind ` +
1634
2842
  `"${latest.kind}" and cannot be rewritten as "${entry.record.kind}"; nothing was imported.`);
1635
2843
  }
1636
- if (deviceEventContentKey(latest) === deviceEventContentKey(entry.record)) {
2844
+ // An unversioned member declared current by this batch's authoritative set
2845
+ // reasserts a retracted facet in serialized arrival order rather than
2846
+ // treating the tombstone's identical content as an exact replay. Ordinary
2847
+ // versionless deliveries without complete-set authority never resurrect.
2848
+ // Only provider-owned authoritative-set retractions may be reasserted.
2849
+ // Their tombstones carry the set's explicit version marker on the external
2850
+ // reference, while a member deletion preserves the member's unversioned
2851
+ // reference and must stay deleted under authoritative replay.
2852
+ const reassertsUnversionedSetMember = Boolean(authoritativeSet
2853
+ && indexedSourceVersionComparison === null
2854
+ && isDeletedEventSpineRecord(latest)
2855
+ && latest.source === "device"
2856
+ && latest.externalRef?.version !== undefined);
2857
+ if (deviceEventContentKey(latest) === deviceEventContentKey(entry.record)
2858
+ && (indexedSourceVersionComparison === null || indexedSourceVersionComparison === 0)
2859
+ && !reassertsUnversionedSetMember) {
1637
2860
  skippedDuplicateCount += 1;
1638
2861
  retainedPreparedIds.add(entry.record.id);
1639
- records.push(latest);
2862
+ recordsByEntryIndex.set(entryIndex, latest);
1640
2863
  continue;
1641
2864
  }
1642
- const indexedProviderMatch = matchedEntries.find((match) => match.indexedMatch.record.id === latest.id)?.indexedMatch ?? matchedEntries[0]?.indexedMatch;
1643
- if (indexedProviderMatch
1644
- && deviceEventContentKey(indexedProviderMatch.indexedRecord)
1645
- === deviceEventContentKey(entry.record)) {
1646
- skippedDuplicateCount += 1;
1647
- retainedPreparedIds.add(entry.record.id);
1648
- records.push(latest);
1649
- continue;
2865
+ if (matchesIndexedProviderContent && !reassertsUnversionedSetMember) {
2866
+ const historicalUserEditMatch = matchedEntries.find((match) => hasHistoricalExternalRefUserAuthoredChanges(match.indexedMatch));
2867
+ if (historicalUserEditMatch
2868
+ && indexedSourceVersionComparison !== null
2869
+ && indexedSourceVersionComparison > 0) {
2870
+ const providerRevision = Math.max(eventSpineRevision(latest), index.maxRevisionById.get(latest.id) ?? 0) + 1;
2871
+ const providerBaseline = {
2872
+ ...entry.record,
2873
+ id: latest.id,
2874
+ lifecycle: buildEventSpineLifecycle(providerRevision),
2875
+ };
2876
+ const retainedMemberRevision = {
2877
+ ...latest,
2878
+ ...(migratesJunctionNoIdProfileIdentity
2879
+ ? { externalRef, dataOrigin: entry.record.dataOrigin }
2880
+ : {}),
2881
+ lifecycle: buildEventSpineLifecycle(providerRevision + 1),
2882
+ };
2883
+ const retainedMemberPath = historicalUserEditMatch.indexedMatch.relativePath
2884
+ || toEventLedgerFile(latest.occurredAt);
2885
+ forceAppendIds.add(latest.id);
2886
+ index.latestByRefKey.set(refKey, {
2887
+ indexedExternalRef: externalRef,
2888
+ indexedRecord: providerBaseline,
2889
+ relativePath: retainedMemberPath,
2890
+ record: retainedMemberRevision,
2891
+ });
2892
+ for (const { refKey: matchedRefKey, indexedMatch } of matchedEntries) {
2893
+ const currentMatch = index.latestByRefKey.get(matchedRefKey);
2894
+ if (matchedRefKey !== refKey
2895
+ && indexedMatch.record.id === latest.id
2896
+ && currentMatch?.record.id === latest.id) {
2897
+ index.latestByRefKey.delete(matchedRefKey);
2898
+ }
2899
+ }
2900
+ index.maxRevisionById.set(latest.id, providerRevision + 1);
2901
+ appendEntries.push({ relativePath: entry.relativePath, record: providerBaseline });
2902
+ appendEntries.push({
2903
+ relativePath: retainedMemberPath,
2904
+ record: retainedMemberRevision,
2905
+ });
2906
+ appendRecordIdByPreparedRecordId.set(entry.record.id, providerBaseline.id);
2907
+ recordsByEntryIndex.set(entryIndex, retainedMemberRevision);
2908
+ supersededCount += 1;
2909
+ continue;
2910
+ }
2911
+ if (indexedSourceVersionComparison === null || indexedSourceVersionComparison === 0) {
2912
+ skippedDuplicateCount += 1;
2913
+ retainedPreparedIds.add(entry.record.id);
2914
+ recordsByEntryIndex.set(entryIndex, latest);
2915
+ continue;
2916
+ }
1650
2917
  }
1651
2918
  if (entry.externalRefUpdatePolicy === "immutable") {
1652
2919
  throw new VaultError("EVENT_IMMUTABLE_EXTERNAL_REF_CONFLICT", "Immutable device event externalRef already exists with different content; nothing was imported.");
@@ -1672,7 +2939,7 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1672
2939
  if (eventSpineRevisionsAreComplete(index, latest.id)) {
1673
2940
  retainedPreparedIds.add(entry.record.id);
1674
2941
  }
1675
- records.push(latest);
2942
+ recordsByEntryIndex.set(entryIndex, latest);
1676
2943
  continue;
1677
2944
  }
1678
2945
  if (sourceVersionComparison === 0) {
@@ -1688,7 +2955,7 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1688
2955
  if (eventSpineRevisionsAreComplete(index, latest.id)) {
1689
2956
  retainedPreparedIds.add(entry.record.id);
1690
2957
  }
1691
- records.push(latest);
2958
+ recordsByEntryIndex.set(entryIndex, latest);
1692
2959
  continue;
1693
2960
  }
1694
2961
  const sleepTypeBaselineRevision = sourceVersionComparison === 0
@@ -1714,7 +2981,7 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1714
2981
  if (eventSpineRevisionsAreComplete(index, latest.id)) {
1715
2982
  retainedPreparedIds.add(entry.record.id);
1716
2983
  }
1717
- records.push(latest);
2984
+ recordsByEntryIndex.set(entryIndex, latest);
1718
2985
  continue;
1719
2986
  }
1720
2987
  }
@@ -1725,37 +2992,87 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1725
2992
  if (eventSpineRevisionsAreComplete(index, latest.id)) {
1726
2993
  retainedPreparedIds.add(entry.record.id);
1727
2994
  }
1728
- records.push(latest);
2995
+ recordsByEntryIndex.set(entryIndex, latest);
1729
2996
  continue;
1730
2997
  }
1731
- if (isDeletedEventSpineRecord(latest)) {
2998
+ const replaysProviderOwnedRetractionWithoutSetAuthority = Boolean(isDeletedEventSpineRecord(latest)
2999
+ && latest.source === "device"
3000
+ && latest.externalRef?.version !== undefined
3001
+ && externalRef.version === undefined
3002
+ && !authoritativeSet);
3003
+ if (replaysProviderOwnedRetractionWithoutSetAuthority) {
3004
+ skippedDuplicateCount += 1;
3005
+ retainedPreparedIds.add(entry.record.id);
3006
+ recordsByEntryIndex.set(entryIndex, latest);
3007
+ continue;
3008
+ }
3009
+ if (isDeletedEventSpineRecord(latest) && !reassertsUnversionedSetMember) {
3010
+ // A member-authored deletion tombstone (unversioned reference) stays
3011
+ // dead under authoritative replay: no append, no index change, so a
3012
+ // later empty-then-populated cadence cannot launder the deletion away.
3013
+ if (authoritativeSet && indexedSourceVersionComparison === null) {
3014
+ skippedDuplicateCount += 1;
3015
+ retainedPreparedIds.add(entry.record.id);
3016
+ recordsByEntryIndex.set(entryIndex, latest);
3017
+ continue;
3018
+ }
1732
3019
  index.latestByRefKey.set(refKey, toIndexedExternalRefMatch(entry.record, externalRef));
1733
3020
  appendEntries.push(entry);
1734
3021
  appendRecordIdByPreparedRecordId.set(entry.record.id, entry.record.id);
1735
- records.push(entry.record);
3022
+ recordsByEntryIndex.set(entryIndex, entry.record);
1736
3023
  continue;
1737
3024
  }
3025
+ // A member declared current by this batch's authoritative set falls
3026
+ // through to the superseding append below, so the reassertion lands as
3027
+ // the next serialized event-spine revision over the retraction tombstone.
1738
3028
  if (shouldKeepExistingJunctionSleepStageSummaryObservation(latest, entry.record)) {
1739
3029
  if (eventSpineRevisionsAreComplete(index, latest.id)) {
1740
3030
  retainedPreparedIds.add(entry.record.id);
1741
3031
  }
1742
- records.push(latest);
3032
+ recordsByEntryIndex.set(entryIndex, latest);
1743
3033
  continue;
1744
3034
  }
1745
3035
  const historicalUserEditMatch = matchedEntries.find((match) => hasHistoricalExternalRefUserAuthoredChanges(match.indexedMatch));
1746
- if (historicalUserEditMatch) {
1747
- throw new VaultError("EVENT_EXTERNAL_REF_ALIAS_CONFLICT", `Event externalRef "${externalRef.system}/${externalRef.resourceType}/${externalRef.resourceId}` +
1748
- `${externalRef.facet ? `#${externalRef.facet}` : ""}" matched a historical provider identity ` +
1749
- "whose latest event revision contains user-authored changes; merge must be repaired explicitly.");
1750
- }
1751
3036
  const revision = Math.max(eventSpineRevision(latest), index.maxRevisionById.get(latest.id) ?? 0) + 1;
1752
3037
  const superseding = {
1753
3038
  ...entry.record,
1754
3039
  id: latest.id,
1755
3040
  lifecycle: buildEventSpineLifecycle(revision),
1756
3041
  };
3042
+ const migratesRetainedMemberOccurrence = migratesJunctionStableProfileTimestamp
3043
+ && indexedProviderMatch !== undefined
3044
+ && latest.occurredAt === indexedProviderMatch.indexedRecord.occurredAt
3045
+ && latest.dayKey === indexedProviderMatch.indexedRecord.dayKey;
3046
+ const retainedMemberRevision = historicalUserEditMatch
3047
+ ? {
3048
+ ...latest,
3049
+ ...(migratesJunctionStableProfileTimestamp
3050
+ ? {
3051
+ ...(migratesRetainedMemberOccurrence
3052
+ ? { occurredAt: entry.record.occurredAt, dayKey: entry.record.dayKey }
3053
+ : {}),
3054
+ externalRef,
3055
+ dataOrigin: entry.record.dataOrigin,
3056
+ }
3057
+ : migratesJunctionNoIdProfileIdentity
3058
+ ? { externalRef, dataOrigin: entry.record.dataOrigin }
3059
+ : {}),
3060
+ lifecycle: buildEventSpineLifecycle(revision + 1),
3061
+ }
3062
+ : null;
3063
+ const retainedMemberPath = migratesRetainedMemberOccurrence
3064
+ ? entry.relativePath
3065
+ : historicalUserEditMatch?.indexedMatch.relativePath
3066
+ || toEventLedgerFile(latest.occurredAt);
1757
3067
  forceAppendIds.add(latest.id);
1758
- index.latestByRefKey.set(refKey, toIndexedExternalRefMatch(superseding, externalRef));
3068
+ index.latestByRefKey.set(refKey, retainedMemberRevision
3069
+ ? {
3070
+ indexedExternalRef: externalRef,
3071
+ indexedRecord: superseding,
3072
+ relativePath: retainedMemberPath,
3073
+ record: retainedMemberRevision,
3074
+ }
3075
+ : toIndexedExternalRefMatch(superseding, externalRef));
1759
3076
  for (const { refKey: matchedRefKey, indexedMatch } of matchedEntries) {
1760
3077
  const currentMatch = index.latestByRefKey.get(matchedRefKey);
1761
3078
  if (matchedRefKey !== refKey &&
@@ -1764,12 +3081,87 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1764
3081
  index.latestByRefKey.delete(matchedRefKey);
1765
3082
  }
1766
3083
  }
1767
- index.maxRevisionById.set(latest.id, revision);
3084
+ index.maxRevisionById.set(latest.id, retainedMemberRevision ? revision + 1 : revision);
1768
3085
  appendEntries.push({ relativePath: entry.relativePath, record: superseding });
3086
+ if (retainedMemberRevision) {
3087
+ appendEntries.push({ relativePath: retainedMemberPath, record: retainedMemberRevision });
3088
+ }
1769
3089
  appendRecordIdByPreparedRecordId.set(entry.record.id, superseding.id);
1770
- records.push(superseding);
3090
+ recordsByEntryIndex.set(entryIndex, retainedMemberRevision ?? superseding);
1771
3091
  supersededCount += 1;
1772
3092
  }
3093
+ for (const set of authoritativeEventSets) {
3094
+ const ownsFacet = (facet) => set.facetPrefixes.some((prefix) => facet === prefix || facet.startsWith(`${prefix}-`));
3095
+ const candidates = [...index.latestByRefKey.entries()].filter(([, match]) => {
3096
+ const externalRef = match.record.externalRef;
3097
+ return externalRef?.system === set.system
3098
+ && externalRef.resourceType === set.resourceType
3099
+ && externalRef.resourceId === set.resourceId
3100
+ && typeof externalRef.facet === "string"
3101
+ && ownsFacet(externalRef.facet)
3102
+ && !set.currentFacets.has(externalRef.facet)
3103
+ && !isDeletedEventSpineRecord(match.record);
3104
+ });
3105
+ for (const [refKey, latestMatch] of candidates) {
3106
+ const latest = latestMatch.record;
3107
+ const latestRef = latest.externalRef;
3108
+ if (!latestRef?.facet) {
3109
+ continue;
3110
+ }
3111
+ const incomingRef = { ...latestRef, version: set.version };
3112
+ const sourceVersionComparison = compareIncomingExternalRefVersion(latestMatch.indexedExternalRef, incomingRef);
3113
+ if (sourceVersionComparison !== null && sourceVersionComparison < 0) {
3114
+ continue;
3115
+ }
3116
+ if (sourceVersionComparison === 0
3117
+ && isDeletedEventSpineRecord(latestMatch.indexedRecord)) {
3118
+ continue;
3119
+ }
3120
+ if (sourceVersionComparison === 0) {
3121
+ throw new VaultError("EVENT_SOURCE_REVISION_CONFLICT", `Authoritative device event externalRef "${set.system}/${set.resourceType}/` +
3122
+ `${set.resourceId}#${latestRef.facet}" has conflicting membership for source revision ` +
3123
+ `"${set.version}"; nothing was imported.`);
3124
+ }
3125
+ const hasMemberEdit = hasHistoricalExternalRefUserAuthoredChanges(latestMatch);
3126
+ const revision = Math.max(eventSpineRevision(latest), index.maxRevisionById.get(latest.id) ?? 0) + 1;
3127
+ const tombstone = {
3128
+ ...latestMatch.indexedRecord,
3129
+ source: "device",
3130
+ recordedAt: set.version,
3131
+ externalRef: incomingRef,
3132
+ lifecycle: buildEventSpineLifecycle(revision, "deleted"),
3133
+ };
3134
+ const retainedMemberRevision = hasMemberEdit
3135
+ ? {
3136
+ ...latest,
3137
+ lifecycle: buildEventSpineLifecycle(revision + 1),
3138
+ }
3139
+ : null;
3140
+ const latestPath = latestMatch.relativePath || toEventLedgerFile(latest.occurredAt);
3141
+ forceAppendIds.add(latest.id);
3142
+ index.latestByRefKey.set(refKey, retainedMemberRevision
3143
+ ? {
3144
+ indexedExternalRef: incomingRef,
3145
+ indexedRecord: tombstone,
3146
+ relativePath: latestPath,
3147
+ record: retainedMemberRevision,
3148
+ }
3149
+ : toIndexedExternalRefMatch(tombstone, incomingRef, latestPath));
3150
+ index.maxRevisionById.set(latest.id, retainedMemberRevision ? revision + 1 : revision);
3151
+ appendEntries.push({ relativePath: latestPath, record: tombstone });
3152
+ if (retainedMemberRevision) {
3153
+ appendEntries.push({ relativePath: latestPath, record: retainedMemberRevision });
3154
+ }
3155
+ retractedCount += 1;
3156
+ }
3157
+ }
3158
+ const records = entries.map((entry, entryIndex) => {
3159
+ const record = recordsByEntryIndex.get(entryIndex);
3160
+ if (!record) {
3161
+ throw new VaultError("EVENT_EXTERNAL_REF_ALIAS_CONFLICT", `Device event reconciliation did not produce a result for "${entry.record.id}".`);
3162
+ }
3163
+ return record;
3164
+ });
1773
3165
  return {
1774
3166
  appendEntries,
1775
3167
  appendRecordIdByPreparedRecordId,
@@ -1778,6 +3170,7 @@ async function reconcileDeviceEventEntriesByExternalRef(vaultRoot, entries, exis
1778
3170
  retainedPreparedIds,
1779
3171
  skippedDuplicateCount,
1780
3172
  supersededCount,
3173
+ retractedCount,
1781
3174
  };
1782
3175
  }
1783
3176
  // Public bulk import reconciles externalRef identity vault-wide, not per
@@ -2147,6 +3540,9 @@ function buildDeviceBatchAuditSummary(input) {
2147
3540
  if (input.supersededCount > 0) {
2148
3541
  dedupeNotes.push(`${input.supersededCount} event(s) updated in place by externalRef`);
2149
3542
  }
3543
+ if (input.retractedCount > 0) {
3544
+ dedupeNotes.push(`${input.retractedCount} omitted authoritative event(s) retracted`);
3545
+ }
2150
3546
  const dedupeSuffix = dedupeNotes.length > 0 ? ` (${dedupeNotes.join(", ")})` : "";
2151
3547
  return `Imported ${input.provider} device batch with ${input.eventCount} event(s) and ${input.sampleCount} sample(s)${dedupeSuffix}.`;
2152
3548
  }
@@ -2178,7 +3574,7 @@ function buildLegacyReceiptFingerprint(receipt, provider) {
2178
3574
  sha256: createHash("sha256").update(content).digest("hex"),
2179
3575
  }];
2180
3576
  }
2181
- function prepareDeviceBatchPlan({ provider, accountId, importedAt = new Date(), defaultTimeZone: fallbackTimeZone, source = "device", events = [], samples = [], evidenceParts = [], ingestReceipt, provenance, }) {
3577
+ function prepareDeviceBatchPlan({ provider, accountId, importedAt = new Date(), defaultTimeZone: fallbackTimeZone, source = "device", events = [], samples = [], evidenceParts = [], authoritativeEventSets = [], ingestReceipt, provenance, }) {
2182
3578
  const normalizedInputs = normalizeDeviceBatchInputs({
2183
3579
  provider,
2184
3580
  accountId,
@@ -2188,6 +3584,7 @@ function prepareDeviceBatchPlan({ provider, accountId, importedAt = new Date(),
2188
3584
  events,
2189
3585
  samples,
2190
3586
  evidenceParts,
3587
+ authoritativeEventSets,
2191
3588
  ingestReceipt,
2192
3589
  provenance,
2193
3590
  });
@@ -2224,6 +3621,18 @@ function prepareDeviceBatchPlan({ provider, accountId, importedAt = new Date(),
2224
3621
  receipt: compactedIngestReceipt ?? null,
2225
3622
  eventIds: normalizedInputs.events.map(({ recordId }) => recordId),
2226
3623
  sampleIds: normalizedInputs.samples.map(({ recordId }) => recordId),
3624
+ ...(normalizedInputs.authoritativeEventSets.length === 0
3625
+ ? {}
3626
+ : {
3627
+ authoritativeEventSets: normalizedInputs.authoritativeEventSets.map((set) => ({
3628
+ system: set.system,
3629
+ resourceType: set.resourceType,
3630
+ resourceId: set.resourceId,
3631
+ version: set.version,
3632
+ facetPrefixes: set.facetPrefixes,
3633
+ currentFacets: [...set.currentFacets].sort(),
3634
+ })),
3635
+ }),
2227
3636
  evidenceParts: fingerprint,
2228
3637
  }));
2229
3638
  const importId = buildImportId(rawArtifactFingerprint);
@@ -2245,6 +3654,7 @@ function prepareDeviceBatchPlan({ provider, accountId, importedAt = new Date(),
2245
3654
  evidenceRolesByPreparedRecordId: eventPlan.evidenceRolesByPreparedRecordId,
2246
3655
  preparedSamples,
2247
3656
  preparedEvidenceParts,
3657
+ authoritativeEventSets: normalizedInputs.authoritativeEventSets,
2248
3658
  };
2249
3659
  }
2250
3660
  async function hashSourceFile(sourcePath) {
@@ -3212,7 +4622,8 @@ function preparedDeviceEventRetainsCurrentOutput(input) {
3212
4622
  }
3213
4623
  const current = input.currentEventOwners.currentRecordByPreparedId.get(preparedId);
3214
4624
  return current !== undefined && (isDeletedEventSpineRecord(current)
3215
- || (eventSpineRevision(current) > eventSpineRevision(input.entry.record)
4625
+ || (current.source !== "device"
4626
+ && eventSpineRevision(current) > eventSpineRevision(input.entry.record)
3216
4627
  && userAuthoredEventStateKey(current) !== userAuthoredEventStateKey(input.entry.record)));
3217
4628
  }
3218
4629
  function storedIntegrationIngestRetainsCurrentOutputLocators(input) {
@@ -3339,7 +4750,28 @@ function buildStoredDeviceDeliveryNoopResult(input) {
3339
4750
  auditPath: null,
3340
4751
  };
3341
4752
  }
3342
- export async function importDeviceBatch({ vaultRoot, provider, accountId, importedAt = new Date(), source = "device", events = [], samples = [], evidenceParts = [], ingestReceipt, provenance, }) {
4753
+ export async function importDeviceBatch(input, options = {}) {
4754
+ assertCanonicalWriteLockScope(input.vaultRoot);
4755
+ const startedAt = performance.now();
4756
+ const timing = {
4757
+ canonicalWriteElapsedMs: 0,
4758
+ eventIdentityIndexCacheHit: false,
4759
+ eventIdentityIndexElapsedMs: 0,
4760
+ totalElapsedMs: 0,
4761
+ };
4762
+ try {
4763
+ return await importDeviceBatchWithExecutionOptions(input, options, timing);
4764
+ }
4765
+ catch (error) {
4766
+ clearDeviceBatchImportSessionVaultState(options.session, input.vaultRoot);
4767
+ throw error;
4768
+ }
4769
+ finally {
4770
+ timing.totalElapsedMs = Math.max(0, performance.now() - startedAt);
4771
+ emitDeviceBatchImportTiming(options.onTiming, timing);
4772
+ }
4773
+ }
4774
+ async function importDeviceBatchWithExecutionOptions({ vaultRoot, provider, accountId, importedAt = new Date(), source = "device", events = [], samples = [], evidenceParts = [], authoritativeEventSets = [], ingestReceipt, provenance, }, options, timing) {
3343
4775
  assertDeviceSampleRowLimit(Array.isArray(samples) ? samples.length : 0);
3344
4776
  const vault = await loadVault({ vaultRoot });
3345
4777
  const deviceBatchPlan = prepareDeviceBatchPlan({
@@ -3351,17 +4783,66 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3351
4783
  events,
3352
4784
  samples,
3353
4785
  evidenceParts,
4786
+ authoritativeEventSets,
3354
4787
  ingestReceipt,
3355
4788
  provenance,
3356
4789
  });
3357
- const eventTargetShardPaths = [
3358
- ...new Set(deviceBatchPlan.preparedEvents.map((entry) => entry.relativePath)),
3359
- ].sort();
3360
4790
  const sampleRecords = deviceBatchPlan.preparedSamples.map((entry) => entry.record);
3361
4791
  const sampleAppendPlan = await buildJsonlAppendPlan(vaultRoot, deviceBatchPlan.preparedSamples, {
3362
4792
  dedupeWithinPlan: true,
3363
4793
  });
3364
- const eventIdentityContext = await buildDeviceEventIdentityContext(vaultRoot, deviceBatchPlan.preparedEvents);
4794
+ const requiresEventIdentityContext = deviceBatchPlan.preparedEvents.length > 0
4795
+ || deviceBatchPlan.authoritativeEventSets.length > 0;
4796
+ const eventIdentityDependency = buildDeviceEventIdentityDependency(deviceBatchPlan);
4797
+ const indexStartedAt = performance.now();
4798
+ const sessionVaultState = requiresEventIdentityContext
4799
+ ? readDeviceBatchImportSessionVaultState(options.session, vaultRoot)
4800
+ : undefined;
4801
+ const currentEventLedgerFingerprint = sessionVaultState
4802
+ ? await tryBuildDeviceEventLedgerFingerprint(vaultRoot)
4803
+ : undefined;
4804
+ const sessionFingerprintMatches = sessionVaultState
4805
+ && currentEventLedgerFingerprint !== undefined
4806
+ ? sessionVaultState.eventLedgerFingerprint
4807
+ === currentEventLedgerFingerprint
4808
+ : false;
4809
+ const cachedEventIdentityContext = sessionVaultState
4810
+ && sessionFingerprintMatches
4811
+ && sessionVaultState.dependencyChanges.every((dependencyChange) => !deviceEventIdentityDependenciesOverlap(eventIdentityDependency, dependencyChange))
4812
+ ? cloneDeviceEventIdentityContext(sessionVaultState.eventIdentityContext)
4813
+ : undefined;
4814
+ let initialEventIdentityContext = cachedEventIdentityContext
4815
+ ?? await buildDeviceEventIdentityContext(vaultRoot, deviceBatchPlan.preparedEvents, deviceBatchPlan.authoritativeEventSets);
4816
+ if (cachedEventIdentityContext
4817
+ && deviceBatchPlan.preparedEvents.some((entry) => resolveStructuralJunctionDailyAggregateAliasOwnerSplit(entry, cachedEventIdentityContext.index) !== null)) {
4818
+ initialEventIdentityContext = await buildDeviceEventIdentityContext(vaultRoot, deviceBatchPlan.preparedEvents, deviceBatchPlan.authoritativeEventSets);
4819
+ }
4820
+ else if (cachedEventIdentityContext) {
4821
+ timing.eventIdentityIndexCacheHit = true;
4822
+ }
4823
+ timing.eventIdentityIndexElapsedMs = Math.max(0, performance.now() - indexStartedAt);
4824
+ if (requiresEventIdentityContext && !timing.eventIdentityIndexCacheHit) {
4825
+ const eventLedgerFingerprint = await tryBuildDeviceEventLedgerFingerprint(vaultRoot);
4826
+ if (eventLedgerFingerprint) {
4827
+ replaceDeviceBatchImportSessionVaultState(options.session, vaultRoot, {
4828
+ dependencyChanges: [],
4829
+ eventIdentityContext: cloneDeviceEventIdentityContext(initialEventIdentityContext),
4830
+ eventLedgerFingerprint,
4831
+ });
4832
+ }
4833
+ else {
4834
+ clearDeviceBatchImportSessionVaultState(options.session, vaultRoot);
4835
+ }
4836
+ }
4837
+ deviceBatchPlan.preparedEvents = resolveJunctionFloatingFallbackEntries(deviceBatchPlan.preparedEvents, initialEventIdentityContext);
4838
+ const eventIdentityContext = {
4839
+ index: initialEventIdentityContext.index,
4840
+ legacyReservations: buildLegacyExternalRefReservations(deviceBatchPlan.preparedEvents, initialEventIdentityContext.index),
4841
+ };
4842
+ const aliasRepairContext = buildDeviceEventAliasRepairContext(deviceBatchPlan);
4843
+ const eventTargetShardPaths = [
4844
+ ...new Set(deviceBatchPlan.preparedEvents.map((entry) => entry.relativePath)),
4845
+ ].sort();
3365
4846
  const currentEventOwners = mapCurrentDeviceEventOwners(deviceBatchPlan.preparedEvents, eventIdentityContext);
3366
4847
  const baselineEventReconciliation = await reconcileDeviceEventEntriesByExternalRef(vaultRoot, deviceBatchPlan.preparedEvents, buildEmptyDeviceEventIdentityContext(deviceBatchPlan.preparedEvents));
3367
4848
  const baselineRetainedPreparedIds = new Set(baselineEventReconciliation.appendRecordIdByPreparedRecordId.keys());
@@ -3376,6 +4857,7 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3376
4857
  });
3377
4858
  const protectedPreparedEventIds = new Set(deviceBatchPlan.preparedEvents
3378
4859
  .filter((entry) => baselineRetainedPreparedIds.has(entry.record.id)
4860
+ && !hasJunctionDailyAggregateAliasSplit(entry, eventIdentityContext)
3379
4861
  && (currentEventOwners.historicallyDeliveredPreparedIds.has(entry.record.id)
3380
4862
  || (entry.record.externalRef === undefined
3381
4863
  && currentEventOwners.currentRecordByPreparedId.has(entry.record.id)
@@ -3384,7 +4866,7 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3384
4866
  .map((entry) => entry.record.id));
3385
4867
  const ordinaryReconciliationEntries = deviceBatchPlan.preparedEvents.filter((entry) => baselineRetainedPreparedIds.has(entry.record.id)
3386
4868
  && !protectedPreparedEventIds.has(entry.record.id));
3387
- const currentEventReconciliation = await reconcileDeviceEventEntriesByExternalRef(vaultRoot, ordinaryReconciliationEntries, cloneDeviceEventIdentityContext(eventIdentityContext));
4869
+ const currentEventReconciliation = await reconcileDeviceEventEntriesByExternalRef(vaultRoot, ordinaryReconciliationEntries, cloneDeviceEventIdentityContext(eventIdentityContext), new Map(), deviceBatchPlan.authoritativeEventSets, aliasRepairContext);
3388
4870
  const replayRetainedPreparedIds = new Set([
3389
4871
  ...protectedPreparedEventIds,
3390
4872
  ...currentEventReconciliation.retainedPreparedIds,
@@ -3693,7 +5175,7 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3693
5175
  const persistenceReconciliationEntries = reconciliationEntries.filter((entry) => !storedEventRepairIds.has(entry.record.id));
3694
5176
  const repairedProtectedPreparedEventCount = [...historicalRepairEntryByPreparedId.keys()].filter((preparedId) => persistenceProtectedPreparedEventIds.has(preparedId)).length;
3695
5177
  const reconciled = evidenceRepairRequired
3696
- ? await reconcileDeviceEventEntriesByExternalRef(vaultRoot, persistenceReconciliationEntries, cloneDeviceEventIdentityContext(eventIdentityContext))
5178
+ ? await reconcileDeviceEventEntriesByExternalRef(vaultRoot, persistenceReconciliationEntries, cloneDeviceEventIdentityContext(eventIdentityContext), new Map(), deviceBatchPlan.authoritativeEventSets, aliasRepairContext)
3697
5179
  : currentEventReconciliation;
3698
5180
  const reconciledRecordByPreparedId = new Map(persistenceReconciliationEntries.map((entry, index) => [
3699
5181
  entry.record.id,
@@ -3767,7 +5249,7 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3767
5249
  ]));
3768
5250
  const persistencePreparedEvents = deviceBatchPlan.preparedEvents.filter((entry) => associablePreparedEventIds.has(entry.record.id)
3769
5251
  && persistenceEvidenceRolesByPreparedRecordId.has(entry.record.id));
3770
- const hasAppendedOutputs = appendedPreparedEventIds.size > 0
5252
+ const hasAppendedOutputs = eventAppendPlan.appendedRecordIds.length > 0
3771
5253
  || sampleAppendPlan.appendedRecordIds.length > 0;
3772
5254
  const eventIdsByEvidenceRole = new Map();
3773
5255
  for (const entry of deviceBatchPlan.preparedEvents) {
@@ -3936,7 +5418,8 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3936
5418
  assertInspectionCanAuthorizeNoop();
3937
5419
  return buildNoopResult();
3938
5420
  }
3939
- return runCanonicalWrite({
5421
+ const canonicalWriteStartedAt = performance.now();
5422
+ const result = await runCanonicalWrite({
3940
5423
  vaultRoot,
3941
5424
  operationType: "device_batch_import",
3942
5425
  summary: `Import ${deviceBatchPlan.provider} device batch ${persistedImportId}`,
@@ -3961,6 +5444,7 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3961
5444
  sampleCount: sampleRecords.length,
3962
5445
  skippedDuplicateCount: eventReconciliation.skippedDuplicateCount,
3963
5446
  supersededCount: eventReconciliation.supersededCount,
5447
+ retractedCount: eventReconciliation.retractedCount,
3964
5448
  }),
3965
5449
  occurredAt: deviceBatchPlan.importedAt,
3966
5450
  files: touchedPaths,
@@ -3978,7 +5462,12 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3978
5462
  importedAt: deviceBatchPlan.importedAt,
3979
5463
  events: canonicalEventRecords,
3980
5464
  samples: sampleRecords,
3981
- eventShardPaths: eventTargetShardPaths,
5465
+ eventShardPaths: [
5466
+ ...new Set([
5467
+ ...eventTargetShardPaths,
5468
+ ...eventAppendPlan.targetShardPaths,
5469
+ ]),
5470
+ ].sort(),
3982
5471
  sampleShardPaths: sampleAppendPlan.targetShardPaths,
3983
5472
  evidencePartCount: deviceBatchPlan.preparedEvidenceParts.length,
3984
5473
  persistedEvidencePartCount: retainedEvidenceParts.length,
@@ -3986,6 +5475,27 @@ export async function importDeviceBatch({ vaultRoot, provider, accountId, import
3986
5475
  };
3987
5476
  },
3988
5477
  });
5478
+ timing.canonicalWriteElapsedMs = Math.max(0, performance.now() - canonicalWriteStartedAt);
5479
+ if (eventAppendPlan.appendedRecordIds.length > 0) {
5480
+ const currentSessionState = readDeviceBatchImportSessionVaultState(options.session, vaultRoot);
5481
+ if (currentSessionState) {
5482
+ const eventLedgerFingerprint = await tryBuildDeviceEventLedgerFingerprint(vaultRoot);
5483
+ if (eventLedgerFingerprint) {
5484
+ replaceDeviceBatchImportSessionVaultState(options.session, vaultRoot, {
5485
+ ...currentSessionState,
5486
+ dependencyChanges: [
5487
+ ...currentSessionState.dependencyChanges,
5488
+ buildDeviceEventIdentityDependency(deviceBatchPlan, eventReconciliation.records.map((record) => record.id)),
5489
+ ],
5490
+ eventLedgerFingerprint,
5491
+ });
5492
+ }
5493
+ else {
5494
+ clearDeviceBatchImportSessionVaultState(options.session, vaultRoot);
5495
+ }
5496
+ }
5497
+ }
5498
+ return result;
3989
5499
  }
3990
5500
  const EVENT_BATCH_FAILURE_REPORT_LIMIT = 20;
3991
5501
  // importEventBatch is public API surface, so the batch-shape invariants live