open-multi-agent-kit 1.0.0 → 1.2.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 (622) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/README.md +2 -2
  3. package/dist/approval-api.d.ts +5 -0
  4. package/dist/approval-api.d.ts.map +1 -0
  5. package/dist/approval-api.js +5 -0
  6. package/dist/approval-api.js.map +1 -0
  7. package/dist/cli/help.d.ts.map +1 -1
  8. package/dist/cli/help.js +1 -0
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/session-picker.d.ts +2 -2
  11. package/dist/cli/session-picker.d.ts.map +1 -1
  12. package/dist/cli/session-picker.js.map +1 -1
  13. package/dist/commands/verified-run-cli.d.ts.map +1 -1
  14. package/dist/commands/verified-run-cli.js +69 -22
  15. package/dist/commands/verified-run-cli.js.map +1 -1
  16. package/dist/commands/verified-run-signal.d.ts +3 -0
  17. package/dist/commands/verified-run-signal.d.ts.map +1 -0
  18. package/dist/commands/verified-run-signal.js +15 -0
  19. package/dist/commands/verified-run-signal.js.map +1 -0
  20. package/dist/coordination/awareness.d.ts +32 -0
  21. package/dist/coordination/awareness.d.ts.map +1 -0
  22. package/dist/coordination/awareness.js +55 -0
  23. package/dist/coordination/awareness.js.map +1 -0
  24. package/dist/coordination/broker.d.ts +79 -0
  25. package/dist/coordination/broker.d.ts.map +1 -0
  26. package/dist/coordination/broker.js +202 -0
  27. package/dist/coordination/broker.js.map +1 -0
  28. package/dist/coordination/index.d.ts +16 -0
  29. package/dist/coordination/index.d.ts.map +1 -0
  30. package/dist/coordination/index.js +16 -0
  31. package/dist/coordination/index.js.map +1 -0
  32. package/dist/coordination/integration.d.ts +59 -0
  33. package/dist/coordination/integration.d.ts.map +1 -0
  34. package/dist/coordination/integration.js +126 -0
  35. package/dist/coordination/integration.js.map +1 -0
  36. package/dist/coordination/operation.d.ts +107 -0
  37. package/dist/coordination/operation.d.ts.map +1 -0
  38. package/dist/coordination/operation.js +231 -0
  39. package/dist/coordination/operation.js.map +1 -0
  40. package/dist/coordination/resource.d.ts +33 -0
  41. package/dist/coordination/resource.d.ts.map +1 -0
  42. package/dist/coordination/resource.js +95 -0
  43. package/dist/coordination/resource.js.map +1 -0
  44. package/dist/coordination/session.d.ts +74 -0
  45. package/dist/coordination/session.d.ts.map +1 -0
  46. package/dist/coordination/session.js +143 -0
  47. package/dist/coordination/session.js.map +1 -0
  48. package/dist/coordination/types.d.ts +68 -0
  49. package/dist/coordination/types.d.ts.map +1 -0
  50. package/dist/coordination/types.js +26 -0
  51. package/dist/coordination/types.js.map +1 -0
  52. package/dist/core/adaptorch-bridge.d.ts +1 -1
  53. package/dist/core/adaptorch-bridge.d.ts.map +1 -1
  54. package/dist/core/adaptorch-bridge.js +1 -1
  55. package/dist/core/adaptorch-bridge.js.map +1 -1
  56. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  57. package/dist/core/agent-session-runtime.js +16 -16
  58. package/dist/core/agent-session-runtime.js.map +1 -1
  59. package/dist/core/agent-session.d.ts.map +1 -1
  60. package/dist/core/agent-session.js +35 -15
  61. package/dist/core/agent-session.js.map +1 -1
  62. package/dist/core/canonical-json.d.ts +3 -0
  63. package/dist/core/canonical-json.d.ts.map +1 -0
  64. package/dist/core/canonical-json.js +20 -0
  65. package/dist/core/canonical-json.js.map +1 -0
  66. package/dist/core/compaction/compaction.d.ts +7 -3
  67. package/dist/core/compaction/compaction.d.ts.map +1 -1
  68. package/dist/core/compaction/compaction.js +14 -29
  69. package/dist/core/compaction/compaction.js.map +1 -1
  70. package/dist/core/compaction/control-state.d.ts +41 -0
  71. package/dist/core/compaction/control-state.d.ts.map +1 -0
  72. package/dist/core/compaction/control-state.js +85 -0
  73. package/dist/core/compaction/control-state.js.map +1 -0
  74. package/dist/core/compaction/fallback.d.ts +71 -0
  75. package/dist/core/compaction/fallback.d.ts.map +1 -0
  76. package/dist/core/compaction/fallback.js +118 -0
  77. package/dist/core/compaction/fallback.js.map +1 -0
  78. package/dist/core/compaction/index.d.ts +2 -0
  79. package/dist/core/compaction/index.d.ts.map +1 -1
  80. package/dist/core/compaction/index.js +2 -0
  81. package/dist/core/compaction/index.js.map +1 -1
  82. package/dist/core/compaction/model-failover.d.ts +21 -0
  83. package/dist/core/compaction/model-failover.d.ts.map +1 -0
  84. package/dist/core/compaction/model-failover.js +46 -0
  85. package/dist/core/compaction/model-failover.js.map +1 -0
  86. package/dist/core/compaction/session-summary.d.ts +21 -0
  87. package/dist/core/compaction/session-summary.d.ts.map +1 -0
  88. package/dist/core/compaction/session-summary.js +27 -0
  89. package/dist/core/compaction/session-summary.js.map +1 -0
  90. package/dist/core/context-budget-headroom-candidates.d.ts +16 -1
  91. package/dist/core/context-budget-headroom-candidates.d.ts.map +1 -1
  92. package/dist/core/context-budget-headroom-candidates.js +31 -13
  93. package/dist/core/context-budget-headroom-candidates.js.map +1 -1
  94. package/dist/core/context-budget-headroom-types.d.ts +7 -1
  95. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  96. package/dist/core/context-budget-headroom-types.js +0 -1
  97. package/dist/core/context-budget-headroom-types.js.map +1 -1
  98. package/dist/core/context-budget-headroom.d.ts +10 -0
  99. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  100. package/dist/core/context-budget-headroom.js +11 -2
  101. package/dist/core/context-budget-headroom.js.map +1 -1
  102. package/dist/core/context-budget-v2-global-pass.d.ts +21 -0
  103. package/dist/core/context-budget-v2-global-pass.d.ts.map +1 -0
  104. package/dist/core/context-budget-v2-global-pass.js +203 -0
  105. package/dist/core/context-budget-v2-global-pass.js.map +1 -0
  106. package/dist/core/context-budget-v2-planned-items.d.ts +12 -0
  107. package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -1
  108. package/dist/core/context-budget-v2-planned-items.js +21 -2
  109. package/dist/core/context-budget-v2-planned-items.js.map +1 -1
  110. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  111. package/dist/core/context-budget-v2-planner.js +18 -19
  112. package/dist/core/context-budget-v2-planner.js.map +1 -1
  113. package/dist/core/context-budget-v2-scoring.d.ts +7 -1
  114. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  115. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  116. package/dist/core/context-budget-v2-selection.d.ts +3 -0
  117. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  118. package/dist/core/context-budget-v2-selection.js +6 -2
  119. package/dist/core/context-budget-v2-selection.js.map +1 -1
  120. package/dist/core/context-budget-v2-types.d.ts +6 -1
  121. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  122. package/dist/core/context-budget-v2-types.js +6 -1
  123. package/dist/core/context-budget-v2-types.js.map +1 -1
  124. package/dist/core/export-html/template.js +14 -0
  125. package/dist/core/extensions/index.d.ts +1 -1
  126. package/dist/core/extensions/index.d.ts.map +1 -1
  127. package/dist/core/extensions/index.js.map +1 -1
  128. package/dist/core/extensions/session-lifecycle-types.d.ts +14 -0
  129. package/dist/core/extensions/session-lifecycle-types.d.ts.map +1 -0
  130. package/dist/core/extensions/session-lifecycle-types.js +2 -0
  131. package/dist/core/extensions/session-lifecycle-types.js.map +1 -0
  132. package/dist/core/extensions/types.d.ts +4 -9
  133. package/dist/core/extensions/types.d.ts.map +1 -1
  134. package/dist/core/extensions/types.js.map +1 -1
  135. package/dist/core/http-dispatcher.d.ts.map +1 -1
  136. package/dist/core/http-dispatcher.js +10 -1
  137. package/dist/core/http-dispatcher.js.map +1 -1
  138. package/dist/core/keybindings.d.ts.map +1 -1
  139. package/dist/core/keybindings.js +3 -6
  140. package/dist/core/keybindings.js.map +1 -1
  141. package/dist/core/legacy-resource-policy.d.ts +7 -0
  142. package/dist/core/legacy-resource-policy.d.ts.map +1 -0
  143. package/dist/core/legacy-resource-policy.js +9 -0
  144. package/dist/core/legacy-resource-policy.js.map +1 -0
  145. package/dist/core/mcp/config.d.ts.map +1 -1
  146. package/dist/core/mcp/config.js +17 -3
  147. package/dist/core/mcp/config.js.map +1 -1
  148. package/dist/core/mcp/connection-queue.d.ts +12 -0
  149. package/dist/core/mcp/connection-queue.d.ts.map +1 -0
  150. package/dist/core/mcp/connection-queue.js +46 -0
  151. package/dist/core/mcp/connection-queue.js.map +1 -0
  152. package/dist/core/mcp/manager-runtime.d.ts +55 -0
  153. package/dist/core/mcp/manager-runtime.d.ts.map +1 -0
  154. package/dist/core/mcp/manager-runtime.js +66 -0
  155. package/dist/core/mcp/manager-runtime.js.map +1 -0
  156. package/dist/core/mcp/manager.d.ts +12 -68
  157. package/dist/core/mcp/manager.d.ts.map +1 -1
  158. package/dist/core/mcp/manager.js +33 -113
  159. package/dist/core/mcp/manager.js.map +1 -1
  160. package/dist/core/mcp/protocol.d.ts.map +1 -1
  161. package/dist/core/mcp/protocol.js +70 -40
  162. package/dist/core/mcp/protocol.js.map +1 -1
  163. package/dist/core/package-manager.d.ts +4 -0
  164. package/dist/core/package-manager.d.ts.map +1 -1
  165. package/dist/core/package-manager.js +59 -129
  166. package/dist/core/package-manager.js.map +1 -1
  167. package/dist/core/package-resource-patterns.d.ts +7 -0
  168. package/dist/core/package-resource-patterns.d.ts.map +1 -0
  169. package/dist/core/package-resource-patterns.js +99 -0
  170. package/dist/core/package-resource-patterns.js.map +1 -0
  171. package/dist/core/provider-default-models.d.ts +1 -0
  172. package/dist/core/provider-default-models.d.ts.map +1 -1
  173. package/dist/core/provider-default-models.js +1 -0
  174. package/dist/core/provider-default-models.js.map +1 -1
  175. package/dist/core/provider-display-names.d.ts.map +1 -1
  176. package/dist/core/provider-display-names.js +1 -0
  177. package/dist/core/provider-display-names.js.map +1 -1
  178. package/dist/core/provider-resilience.d.ts +9 -0
  179. package/dist/core/provider-resilience.d.ts.map +1 -1
  180. package/dist/core/provider-resilience.js +15 -0
  181. package/dist/core/provider-resilience.js.map +1 -1
  182. package/dist/core/reasoning-router-resolver.d.ts +7 -0
  183. package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
  184. package/dist/core/reasoning-router-resolver.js +21 -3
  185. package/dist/core/reasoning-router-resolver.js.map +1 -1
  186. package/dist/core/reasoning-router-v4.d.ts +13 -7
  187. package/dist/core/reasoning-router-v4.d.ts.map +1 -1
  188. package/dist/core/reasoning-router-v4.js +8 -5
  189. package/dist/core/reasoning-router-v4.js.map +1 -1
  190. package/dist/core/resource-loader.d.ts.map +1 -1
  191. package/dist/core/resource-loader.js +4 -8
  192. package/dist/core/resource-loader.js.map +1 -1
  193. package/dist/core/run-execution-api.d.ts +8 -1
  194. package/dist/core/run-execution-api.d.ts.map +1 -1
  195. package/dist/core/run-execution-api.js +4 -0
  196. package/dist/core/run-execution-api.js.map +1 -1
  197. package/dist/core/run-journal.d.ts +1 -2
  198. package/dist/core/run-journal.d.ts.map +1 -1
  199. package/dist/core/run-journal.js +2 -19
  200. package/dist/core/run-journal.js.map +1 -1
  201. package/dist/core/session-compaction-service.d.ts +15 -0
  202. package/dist/core/session-compaction-service.d.ts.map +1 -1
  203. package/dist/core/session-compaction-service.js +17 -4
  204. package/dist/core/session-compaction-service.js.map +1 -1
  205. package/dist/core/session-listing.d.ts +51 -0
  206. package/dist/core/session-listing.d.ts.map +1 -0
  207. package/dist/core/session-listing.js +111 -0
  208. package/dist/core/session-listing.js.map +1 -0
  209. package/dist/core/session-manager.d.ts +12 -37
  210. package/dist/core/session-manager.d.ts.map +1 -1
  211. package/dist/core/session-manager.js +12 -118
  212. package/dist/core/session-manager.js.map +1 -1
  213. package/dist/core/session-message-text.d.ts +5 -0
  214. package/dist/core/session-message-text.d.ts.map +1 -0
  215. package/dist/core/session-message-text.js +9 -0
  216. package/dist/core/session-message-text.js.map +1 -0
  217. package/dist/core/skills-catalog-cache.d.ts +2 -0
  218. package/dist/core/skills-catalog-cache.d.ts.map +1 -1
  219. package/dist/core/skills-catalog-cache.js +71 -17
  220. package/dist/core/skills-catalog-cache.js.map +1 -1
  221. package/dist/core/todo-runtime-state.d.ts +12 -0
  222. package/dist/core/todo-runtime-state.d.ts.map +1 -1
  223. package/dist/core/todo-runtime-state.js +23 -0
  224. package/dist/core/todo-runtime-state.js.map +1 -1
  225. package/dist/core/tools/read.d.ts.map +1 -1
  226. package/dist/core/tools/read.js +1 -1
  227. package/dist/core/tools/read.js.map +1 -1
  228. package/dist/core/verified-run/authority-clock.d.ts +3 -0
  229. package/dist/core/verified-run/authority-clock.d.ts.map +1 -0
  230. package/dist/core/verified-run/authority-clock.js +25 -0
  231. package/dist/core/verified-run/authority-clock.js.map +1 -0
  232. package/dist/core/verified-run/authority-errors.d.ts +10 -0
  233. package/dist/core/verified-run/authority-errors.d.ts.map +1 -0
  234. package/dist/core/verified-run/authority-errors.js +17 -0
  235. package/dist/core/verified-run/authority-errors.js.map +1 -0
  236. package/dist/core/verified-run/authority-events.d.ts +126 -0
  237. package/dist/core/verified-run/authority-events.d.ts.map +1 -0
  238. package/dist/core/verified-run/authority-events.js +482 -0
  239. package/dist/core/verified-run/authority-events.js.map +1 -0
  240. package/dist/core/verified-run/authority-journal.d.ts +27 -0
  241. package/dist/core/verified-run/authority-journal.d.ts.map +1 -0
  242. package/dist/core/verified-run/authority-journal.js +83 -0
  243. package/dist/core/verified-run/authority-journal.js.map +1 -0
  244. package/dist/core/verified-run/authority-meaning.d.ts +13 -0
  245. package/dist/core/verified-run/authority-meaning.d.ts.map +1 -0
  246. package/dist/core/verified-run/authority-meaning.js +40 -0
  247. package/dist/core/verified-run/authority-meaning.js.map +1 -0
  248. package/dist/core/verified-run/authority-runtime.d.ts +82 -0
  249. package/dist/core/verified-run/authority-runtime.d.ts.map +1 -0
  250. package/dist/core/verified-run/authority-runtime.js +169 -0
  251. package/dist/core/verified-run/authority-runtime.js.map +1 -0
  252. package/dist/core/verified-run/authority-store.d.ts +204 -0
  253. package/dist/core/verified-run/authority-store.d.ts.map +1 -0
  254. package/dist/core/verified-run/authority-store.js +852 -0
  255. package/dist/core/verified-run/authority-store.js.map +1 -0
  256. package/dist/core/verified-run/broker.d.ts +8 -1
  257. package/dist/core/verified-run/broker.d.ts.map +1 -1
  258. package/dist/core/verified-run/broker.js +94 -90
  259. package/dist/core/verified-run/broker.js.map +1 -1
  260. package/dist/core/verified-run/candidate-policy.d.ts +10 -0
  261. package/dist/core/verified-run/candidate-policy.d.ts.map +1 -0
  262. package/dist/core/verified-run/candidate-policy.js +33 -0
  263. package/dist/core/verified-run/candidate-policy.js.map +1 -0
  264. package/dist/core/verified-run/candidate.d.ts.map +1 -1
  265. package/dist/core/verified-run/candidate.js +5 -10
  266. package/dist/core/verified-run/candidate.js.map +1 -1
  267. package/dist/core/verified-run/coordinator.d.ts +35 -17
  268. package/dist/core/verified-run/coordinator.d.ts.map +1 -1
  269. package/dist/core/verified-run/coordinator.js +111 -57
  270. package/dist/core/verified-run/coordinator.js.map +1 -1
  271. package/dist/core/verified-run/dag-phase.d.ts.map +1 -1
  272. package/dist/core/verified-run/dag-phase.js +1 -1
  273. package/dist/core/verified-run/dag-phase.js.map +1 -1
  274. package/dist/core/verified-run/dag-recovery.d.ts +5 -1
  275. package/dist/core/verified-run/dag-recovery.d.ts.map +1 -1
  276. package/dist/core/verified-run/dag-recovery.js +10 -4
  277. package/dist/core/verified-run/dag-recovery.js.map +1 -1
  278. package/dist/core/verified-run/event-parser.d.ts.map +1 -1
  279. package/dist/core/verified-run/event-parser.js +33 -0
  280. package/dist/core/verified-run/event-parser.js.map +1 -1
  281. package/dist/core/verified-run/evidence-read-projection.d.ts +4 -0
  282. package/dist/core/verified-run/evidence-read-projection.d.ts.map +1 -0
  283. package/dist/core/verified-run/evidence-read-projection.js +27 -0
  284. package/dist/core/verified-run/evidence-read-projection.js.map +1 -0
  285. package/dist/core/verified-run/evidence.d.ts +12 -1
  286. package/dist/core/verified-run/evidence.d.ts.map +1 -1
  287. package/dist/core/verified-run/evidence.js +51 -2
  288. package/dist/core/verified-run/evidence.js.map +1 -1
  289. package/dist/core/verified-run/git-candidate-preflight.d.ts +4 -0
  290. package/dist/core/verified-run/git-candidate-preflight.d.ts.map +1 -0
  291. package/dist/core/verified-run/git-candidate-preflight.js +34 -0
  292. package/dist/core/verified-run/git-candidate-preflight.js.map +1 -0
  293. package/dist/core/verified-run/git-effect-supervisor.d.ts +24 -0
  294. package/dist/core/verified-run/git-effect-supervisor.d.ts.map +1 -0
  295. package/dist/core/verified-run/git-effect-supervisor.js +174 -0
  296. package/dist/core/verified-run/git-effect-supervisor.js.map +1 -0
  297. package/dist/core/verified-run/git-execution.d.ts +27 -0
  298. package/dist/core/verified-run/git-execution.d.ts.map +1 -0
  299. package/dist/core/verified-run/git-execution.js +118 -0
  300. package/dist/core/verified-run/git-execution.js.map +1 -0
  301. package/dist/core/verified-run/git-plumbing.d.ts +37 -0
  302. package/dist/core/verified-run/git-plumbing.d.ts.map +1 -0
  303. package/dist/core/verified-run/git-plumbing.js +171 -0
  304. package/dist/core/verified-run/git-plumbing.js.map +1 -0
  305. package/dist/core/verified-run/git-publication-worker.d.ts +2 -0
  306. package/dist/core/verified-run/git-publication-worker.d.ts.map +1 -0
  307. package/dist/core/verified-run/git-publication-worker.js +54 -0
  308. package/dist/core/verified-run/git-publication-worker.js.map +1 -0
  309. package/dist/core/verified-run/git-sandbox-layout.d.ts +4 -0
  310. package/dist/core/verified-run/git-sandbox-layout.d.ts.map +1 -0
  311. package/dist/core/verified-run/git-sandbox-layout.js +29 -0
  312. package/dist/core/verified-run/git-sandbox-layout.js.map +1 -0
  313. package/dist/core/verified-run/git-worker-protocol.d.ts +19 -0
  314. package/dist/core/verified-run/git-worker-protocol.d.ts.map +1 -0
  315. package/dist/core/verified-run/git-worker-protocol.js +109 -0
  316. package/dist/core/verified-run/git-worker-protocol.js.map +1 -0
  317. package/dist/core/verified-run/git-worker-runtime.d.ts +19 -0
  318. package/dist/core/verified-run/git-worker-runtime.d.ts.map +1 -0
  319. package/dist/core/verified-run/git-worker-runtime.js +74 -0
  320. package/dist/core/verified-run/git-worker-runtime.js.map +1 -0
  321. package/dist/core/verified-run/journal.d.ts +2 -3
  322. package/dist/core/verified-run/journal.d.ts.map +1 -1
  323. package/dist/core/verified-run/journal.js.map +1 -1
  324. package/dist/core/verified-run/namespace-identity.d.ts.map +1 -1
  325. package/dist/core/verified-run/namespace-identity.js +1 -1
  326. package/dist/core/verified-run/namespace-identity.js.map +1 -1
  327. package/dist/core/verified-run/owned-execution.d.ts +10 -1
  328. package/dist/core/verified-run/owned-execution.d.ts.map +1 -1
  329. package/dist/core/verified-run/owned-execution.js +124 -31
  330. package/dist/core/verified-run/owned-execution.js.map +1 -1
  331. package/dist/core/verified-run/phase-context.d.ts +2 -0
  332. package/dist/core/verified-run/phase-context.d.ts.map +1 -1
  333. package/dist/core/verified-run/phase-context.js.map +1 -1
  334. package/dist/core/verified-run/projection.d.ts.map +1 -1
  335. package/dist/core/verified-run/projection.js +51 -1
  336. package/dist/core/verified-run/projection.js.map +1 -1
  337. package/dist/core/verified-run/publish-preflight.d.ts +12 -0
  338. package/dist/core/verified-run/publish-preflight.d.ts.map +1 -0
  339. package/dist/core/verified-run/publish-preflight.js +38 -0
  340. package/dist/core/verified-run/publish-preflight.js.map +1 -0
  341. package/dist/core/verified-run/publish-start.d.ts +5 -0
  342. package/dist/core/verified-run/publish-start.d.ts.map +1 -0
  343. package/dist/core/verified-run/publish-start.js +7 -0
  344. package/dist/core/verified-run/publish-start.js.map +1 -0
  345. package/dist/core/verified-run/recovery-projection.d.ts.map +1 -1
  346. package/dist/core/verified-run/recovery-projection.js +5 -0
  347. package/dist/core/verified-run/recovery-projection.js.map +1 -1
  348. package/dist/core/verified-run/recovery.d.ts +5 -1
  349. package/dist/core/verified-run/recovery.d.ts.map +1 -1
  350. package/dist/core/verified-run/recovery.js +10 -4
  351. package/dist/core/verified-run/recovery.js.map +1 -1
  352. package/dist/core/verified-run/run-plan.d.ts +17 -0
  353. package/dist/core/verified-run/run-plan.d.ts.map +1 -0
  354. package/dist/core/verified-run/run-plan.js +18 -0
  355. package/dist/core/verified-run/run-plan.js.map +1 -0
  356. package/dist/core/verified-run/run-publish-owned.d.ts +6 -0
  357. package/dist/core/verified-run/run-publish-owned.d.ts.map +1 -0
  358. package/dist/core/verified-run/run-publish-owned.js +199 -0
  359. package/dist/core/verified-run/run-publish-owned.js.map +1 -0
  360. package/dist/core/verified-run/run-publish.d.ts +19 -0
  361. package/dist/core/verified-run/run-publish.d.ts.map +1 -0
  362. package/dist/core/verified-run/run-publish.js +210 -0
  363. package/dist/core/verified-run/run-publish.js.map +1 -0
  364. package/dist/core/verified-run/run-status-owned.d.ts +4 -0
  365. package/dist/core/verified-run/run-status-owned.d.ts.map +1 -0
  366. package/dist/core/verified-run/run-status-owned.js +14 -0
  367. package/dist/core/verified-run/run-status-owned.js.map +1 -0
  368. package/dist/core/verified-run/run-status.d.ts +133 -0
  369. package/dist/core/verified-run/run-status.d.ts.map +1 -0
  370. package/dist/core/verified-run/run-status.js +193 -0
  371. package/dist/core/verified-run/run-status.js.map +1 -0
  372. package/dist/core/verified-run/run-types.d.ts +31 -0
  373. package/dist/core/verified-run/run-types.d.ts.map +1 -1
  374. package/dist/core/verified-run/run-types.js.map +1 -1
  375. package/dist/core/verified-run/storage.d.ts.map +1 -1
  376. package/dist/core/verified-run/storage.js +1 -1
  377. package/dist/core/verified-run/storage.js.map +1 -1
  378. package/dist/core/verified-run/supervisor-adapter.d.ts +77 -0
  379. package/dist/core/verified-run/supervisor-adapter.d.ts.map +1 -0
  380. package/dist/core/verified-run/supervisor-adapter.js +164 -0
  381. package/dist/core/verified-run/supervisor-adapter.js.map +1 -0
  382. package/dist/core/verified-run/verification-phase.d.ts.map +1 -1
  383. package/dist/core/verified-run/verification-phase.js +1 -1
  384. package/dist/core/verified-run/verification-phase.js.map +1 -1
  385. package/dist/core/verified-run/writer-phase.d.ts.map +1 -1
  386. package/dist/core/verified-run/writer-phase.js +1 -0
  387. package/dist/core/verified-run/writer-phase.js.map +1 -1
  388. package/dist/core/verified-run/writer-recovery.d.ts +2 -0
  389. package/dist/core/verified-run/writer-recovery.d.ts.map +1 -1
  390. package/dist/core/verified-run/writer-recovery.js +1 -0
  391. package/dist/core/verified-run/writer-recovery.js.map +1 -1
  392. package/dist/core/workload-shard-execution-types.d.ts +4 -1
  393. package/dist/core/workload-shard-execution-types.d.ts.map +1 -1
  394. package/dist/core/workload-shard-execution-types.js.map +1 -1
  395. package/dist/core/workload-shard-executor.d.ts +2 -2
  396. package/dist/core/workload-shard-executor.d.ts.map +1 -1
  397. package/dist/core/workload-shard-executor.js +16 -37
  398. package/dist/core/workload-shard-executor.js.map +1 -1
  399. package/dist/core/workload-shard-frontier.d.ts +5 -0
  400. package/dist/core/workload-shard-frontier.d.ts.map +1 -0
  401. package/dist/core/workload-shard-frontier.js +47 -0
  402. package/dist/core/workload-shard-frontier.js.map +1 -0
  403. package/dist/core/workload-shard-runner.d.ts.map +1 -1
  404. package/dist/core/workload-shard-runner.js +28 -17
  405. package/dist/core/workload-shard-runner.js.map +1 -1
  406. package/dist/index.d.ts +4 -5
  407. package/dist/index.d.ts.map +1 -1
  408. package/dist/index.js +3 -4
  409. package/dist/index.js.map +1 -1
  410. package/dist/main.d.ts.map +1 -1
  411. package/dist/main.js +2 -1
  412. package/dist/main.js.map +1 -1
  413. package/dist/metacognition/bridge-result.d.ts +148 -0
  414. package/dist/metacognition/bridge-result.d.ts.map +1 -0
  415. package/dist/metacognition/bridge-result.js +46 -0
  416. package/dist/metacognition/bridge-result.js.map +1 -0
  417. package/dist/metacognition/calibration-selective.d.ts +78 -0
  418. package/dist/metacognition/calibration-selective.d.ts.map +1 -0
  419. package/dist/metacognition/calibration-selective.js +167 -0
  420. package/dist/metacognition/calibration-selective.js.map +1 -0
  421. package/dist/metacognition/calibration.d.ts +62 -0
  422. package/dist/metacognition/calibration.d.ts.map +1 -0
  423. package/dist/metacognition/calibration.js +122 -0
  424. package/dist/metacognition/calibration.js.map +1 -0
  425. package/dist/metacognition/checkpoint.d.ts +60 -0
  426. package/dist/metacognition/checkpoint.d.ts.map +1 -0
  427. package/dist/metacognition/checkpoint.js +57 -0
  428. package/dist/metacognition/checkpoint.js.map +1 -0
  429. package/dist/metacognition/context7.d.ts +48 -0
  430. package/dist/metacognition/context7.d.ts.map +1 -0
  431. package/dist/metacognition/context7.js +154 -0
  432. package/dist/metacognition/context7.js.map +1 -0
  433. package/dist/metacognition/decision.d.ts +44 -0
  434. package/dist/metacognition/decision.d.ts.map +1 -0
  435. package/dist/metacognition/decision.js +111 -0
  436. package/dist/metacognition/decision.js.map +1 -0
  437. package/dist/metacognition/evaluation.d.ts +73 -0
  438. package/dist/metacognition/evaluation.d.ts.map +1 -0
  439. package/dist/metacognition/evaluation.js +97 -0
  440. package/dist/metacognition/evaluation.js.map +1 -0
  441. package/dist/metacognition/index.d.ts +32 -0
  442. package/dist/metacognition/index.d.ts.map +1 -0
  443. package/dist/metacognition/index.js +32 -0
  444. package/dist/metacognition/index.js.map +1 -0
  445. package/dist/metacognition/knowledge-action.d.ts +43 -0
  446. package/dist/metacognition/knowledge-action.d.ts.map +1 -0
  447. package/dist/metacognition/knowledge-action.js +60 -0
  448. package/dist/metacognition/knowledge-action.js.map +1 -0
  449. package/dist/metacognition/knowledge.d.ts +84 -0
  450. package/dist/metacognition/knowledge.d.ts.map +1 -0
  451. package/dist/metacognition/knowledge.js +165 -0
  452. package/dist/metacognition/knowledge.js.map +1 -0
  453. package/dist/metacognition/obligations.d.ts +82 -0
  454. package/dist/metacognition/obligations.d.ts.map +1 -0
  455. package/dist/metacognition/obligations.js +139 -0
  456. package/dist/metacognition/obligations.js.map +1 -0
  457. package/dist/metacognition/observation-validity.d.ts +83 -0
  458. package/dist/metacognition/observation-validity.d.ts.map +1 -0
  459. package/dist/metacognition/observation-validity.js +118 -0
  460. package/dist/metacognition/observation-validity.js.map +1 -0
  461. package/dist/metacognition/observe.d.ts +45 -0
  462. package/dist/metacognition/observe.d.ts.map +1 -0
  463. package/dist/metacognition/observe.js +59 -0
  464. package/dist/metacognition/observe.js.map +1 -0
  465. package/dist/metacognition/policy.d.ts +53 -0
  466. package/dist/metacognition/policy.d.ts.map +1 -0
  467. package/dist/metacognition/policy.js +155 -0
  468. package/dist/metacognition/policy.js.map +1 -0
  469. package/dist/metacognition/predictions.d.ts +78 -0
  470. package/dist/metacognition/predictions.d.ts.map +1 -0
  471. package/dist/metacognition/predictions.js +181 -0
  472. package/dist/metacognition/predictions.js.map +1 -0
  473. package/dist/metacognition/retrieval.d.ts +53 -0
  474. package/dist/metacognition/retrieval.d.ts.map +1 -0
  475. package/dist/metacognition/retrieval.js +170 -0
  476. package/dist/metacognition/retrieval.js.map +1 -0
  477. package/dist/metacognition/risk.d.ts +48 -0
  478. package/dist/metacognition/risk.d.ts.map +1 -0
  479. package/dist/metacognition/risk.js +179 -0
  480. package/dist/metacognition/risk.js.map +1 -0
  481. package/dist/metacognition/route-economics.d.ts +95 -0
  482. package/dist/metacognition/route-economics.d.ts.map +1 -0
  483. package/dist/metacognition/route-economics.js +110 -0
  484. package/dist/metacognition/route-economics.js.map +1 -0
  485. package/dist/metacognition/runtime-bridge.d.ts +81 -0
  486. package/dist/metacognition/runtime-bridge.d.ts.map +1 -0
  487. package/dist/metacognition/runtime-bridge.js +458 -0
  488. package/dist/metacognition/runtime-bridge.js.map +1 -0
  489. package/dist/metacognition/skills.d.ts +42 -0
  490. package/dist/metacognition/skills.d.ts.map +1 -0
  491. package/dist/metacognition/skills.js +220 -0
  492. package/dist/metacognition/skills.js.map +1 -0
  493. package/dist/metacognition/state.d.ts +110 -0
  494. package/dist/metacognition/state.d.ts.map +1 -0
  495. package/dist/metacognition/state.js +52 -0
  496. package/dist/metacognition/state.js.map +1 -0
  497. package/dist/metacognition/validation.d.ts +19 -0
  498. package/dist/metacognition/validation.d.ts.map +1 -0
  499. package/dist/metacognition/validation.js +58 -0
  500. package/dist/metacognition/validation.js.map +1 -0
  501. package/dist/metacognition/verification.d.ts +93 -0
  502. package/dist/metacognition/verification.d.ts.map +1 -0
  503. package/dist/metacognition/verification.js +132 -0
  504. package/dist/metacognition/verification.js.map +1 -0
  505. package/dist/metacognition/verifier.d.ts +49 -0
  506. package/dist/metacognition/verifier.d.ts.map +1 -0
  507. package/dist/metacognition/verifier.js +88 -0
  508. package/dist/metacognition/verifier.js.map +1 -0
  509. package/dist/modes/interactive/components/assistant-message.d.ts +2 -0
  510. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  511. package/dist/modes/interactive/components/assistant-message.js +42 -26
  512. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  513. package/dist/modes/interactive/components/chat-container.d.ts +7 -0
  514. package/dist/modes/interactive/components/chat-container.d.ts.map +1 -0
  515. package/dist/modes/interactive/components/chat-container.js +14 -0
  516. package/dist/modes/interactive/components/chat-container.js.map +1 -0
  517. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  518. package/dist/modes/interactive/components/dynamic-border.js +4 -2
  519. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  520. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  521. package/dist/modes/interactive/components/login-dialog.js +0 -1
  522. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  523. package/dist/modes/interactive/components/session-selector-async-search.d.ts +33 -0
  524. package/dist/modes/interactive/components/session-selector-async-search.d.ts.map +1 -0
  525. package/dist/modes/interactive/components/session-selector-async-search.js +109 -0
  526. package/dist/modes/interactive/components/session-selector-async-search.js.map +1 -0
  527. package/dist/modes/interactive/components/session-selector-loaders.d.ts +6 -0
  528. package/dist/modes/interactive/components/session-selector-loaders.d.ts.map +1 -0
  529. package/dist/modes/interactive/components/session-selector-loaders.js +9 -0
  530. package/dist/modes/interactive/components/session-selector-loaders.js.map +1 -0
  531. package/dist/modes/interactive/components/session-selector-search.d.ts +1 -1
  532. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  533. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  534. package/dist/modes/interactive/components/session-selector-tree.d.ts +16 -0
  535. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -0
  536. package/dist/modes/interactive/components/session-selector-tree.js +46 -0
  537. package/dist/modes/interactive/components/session-selector-tree.js.map +1 -0
  538. package/dist/modes/interactive/components/session-selector.d.ts +6 -1
  539. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  540. package/dist/modes/interactive/components/session-selector.js +36 -76
  541. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  542. package/dist/modes/interactive/components/tool-execution-images.d.ts +32 -0
  543. package/dist/modes/interactive/components/tool-execution-images.d.ts.map +1 -0
  544. package/dist/modes/interactive/components/tool-execution-images.js +144 -0
  545. package/dist/modes/interactive/components/tool-execution-images.js.map +1 -0
  546. package/dist/modes/interactive/components/tool-execution.d.ts +3 -4
  547. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  548. package/dist/modes/interactive/components/tool-execution.js +22 -71
  549. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  550. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  551. package/dist/modes/interactive/interactive-mode.js +7 -9
  552. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  553. package/dist/modes/interactive/tui-diagnostics.d.ts +8 -4
  554. package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -1
  555. package/dist/modes/interactive/tui-diagnostics.js +35 -1
  556. package/dist/modes/interactive/tui-diagnostics.js.map +1 -1
  557. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  558. package/dist/modes/rpc/rpc-mode.js +14 -17
  559. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  560. package/dist/modes/rpc/rpc-response.d.ts +5 -0
  561. package/dist/modes/rpc/rpc-response.d.ts.map +1 -0
  562. package/dist/modes/rpc/rpc-response.js +17 -0
  563. package/dist/modes/rpc/rpc-response.js.map +1 -0
  564. package/dist/observation/identity.d.ts +14 -0
  565. package/dist/observation/identity.d.ts.map +1 -0
  566. package/dist/observation/identity.js +29 -0
  567. package/dist/observation/identity.js.map +1 -0
  568. package/dist/observation/index.d.ts +13 -0
  569. package/dist/observation/index.d.ts.map +1 -0
  570. package/dist/observation/index.js +13 -0
  571. package/dist/observation/index.js.map +1 -0
  572. package/dist/observation/observe-mode.d.ts +46 -0
  573. package/dist/observation/observe-mode.d.ts.map +1 -0
  574. package/dist/observation/observe-mode.js +83 -0
  575. package/dist/observation/observe-mode.js.map +1 -0
  576. package/dist/observation/store.d.ts +53 -0
  577. package/dist/observation/store.d.ts.map +1 -0
  578. package/dist/observation/store.js +126 -0
  579. package/dist/observation/store.js.map +1 -0
  580. package/dist/observation/types.d.ts +72 -0
  581. package/dist/observation/types.d.ts.map +1 -0
  582. package/dist/observation/types.js +9 -0
  583. package/dist/observation/types.js.map +1 -0
  584. package/dist/observation/view.d.ts +39 -0
  585. package/dist/observation/view.d.ts.map +1 -0
  586. package/dist/observation/view.js +173 -0
  587. package/dist/observation/view.js.map +1 -0
  588. package/dist/utils/exif-orientation.d.ts.map +1 -1
  589. package/dist/utils/exif-orientation.js +2 -3
  590. package/dist/utils/exif-orientation.js.map +1 -1
  591. package/dist/utils/tools-manager.d.ts.map +1 -1
  592. package/dist/utils/tools-manager.js +3 -5
  593. package/dist/utils/tools-manager.js.map +1 -1
  594. package/docs/adaptorch-preview-spec.md +7 -7
  595. package/docs/atomic-commit-planning.md +69 -0
  596. package/docs/compaction.md +26 -1
  597. package/docs/extensions.md +14 -0
  598. package/docs/mcp.md +22 -0
  599. package/docs/metacognition.md +130 -0
  600. package/docs/model-catalog-refresh.md +218 -1
  601. package/docs/models.md +1 -1
  602. package/docs/providers.md +53 -0
  603. package/docs/runtime-algorithms.md +156 -7
  604. package/docs/session-format.md +8 -0
  605. package/docs/sessions.md +19 -0
  606. package/docs/skills.md +19 -0
  607. package/docs/terminal-setup.md +15 -0
  608. package/docs/tui.md +41 -0
  609. package/docs/verified-run-safety.md +75 -0
  610. package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
  611. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  612. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  613. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  614. package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
  615. package/examples/extensions/gondolin/package-lock.json +2 -2
  616. package/examples/extensions/gondolin/package.json +1 -1
  617. package/examples/extensions/sandbox/package-lock.json +2 -2
  618. package/examples/extensions/sandbox/package.json +1 -1
  619. package/examples/extensions/with-deps/package-lock.json +2 -2
  620. package/examples/extensions/with-deps/package.json +1 -1
  621. package/npm-shrinkwrap.json +18 -18
  622. package/package.json +6 -8
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Bounded raw-observation store — U1.
3
+ *
4
+ * Session-scoped, in-memory, deterministic. Stores exact bytes with their
5
+ * execution identity, and serves byte-range reads that snap to UTF-8 code-point
6
+ * boundaries rather than emitting silently-corrupted text. Over-capacity puts
7
+ * fail closed with `archive-unavailable` instead of silently evicting evidence.
8
+ */
9
+ import { ensure, integer, text } from "../metacognition/validation.js";
10
+ import { observationId as makeObservationId, sha256Hex } from "./identity.js";
11
+ const DEFAULT_MAX_OBSERVATIONS = 512;
12
+ const DEFAULT_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
13
+ const DEFAULT_MAX_OBSERVATION_BYTES = 8 * 1024 * 1024;
14
+ const UTF8_DECODER = new TextDecoder("utf-8", { fatal: false });
15
+ /** Snap a byte offset down to the nearest UTF-8 code-point boundary. */
16
+ function snapUtf8Boundary(bytes, offset) {
17
+ if (offset <= 0 || offset >= bytes.length)
18
+ return { offset: Math.max(0, Math.min(offset, bytes.length)), normalized: false };
19
+ // A continuation byte is 0b10xxxxxx; walk back to a lead byte or ASCII.
20
+ let i = offset;
21
+ while (i > 0 && (bytes[i] & 0xc0) === 0x80)
22
+ i -= 1;
23
+ return { offset: i, normalized: i !== offset };
24
+ }
25
+ export class ObservationStore {
26
+ maxObservations;
27
+ maxTotalBytes;
28
+ maxObservationBytes;
29
+ byId = new Map();
30
+ totalBytes = 0;
31
+ constructor(options = {}) {
32
+ this.maxObservations = options.maxObservations ?? DEFAULT_MAX_OBSERVATIONS;
33
+ this.maxTotalBytes = options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;
34
+ this.maxObservationBytes = options.maxObservationBytes ?? DEFAULT_MAX_OBSERVATION_BYTES;
35
+ }
36
+ /** Store one raw observation. Throws (fails closed) on malformed input or capacity. */
37
+ put(input) {
38
+ text(input.sessionId, "sessionId", 256);
39
+ text(input.runId, "runId", 256);
40
+ text(input.operationId, "operationId", 256);
41
+ integer(input.sequence, "sequence");
42
+ ensure(input.bytes instanceof Uint8Array, "bytes must be a Uint8Array");
43
+ ensure(input.bytes.length <= this.maxObservationBytes, "observation exceeds per-item cap");
44
+ if (this.byId.size >= this.maxObservations || this.totalBytes + input.bytes.length > this.maxTotalBytes) {
45
+ throw new Error("archive-unavailable: observation store capacity exceeded");
46
+ }
47
+ const rawDigest = sha256Hex(input.bytes);
48
+ const id = makeObservationId({
49
+ runId: input.runId,
50
+ operationId: input.operationId,
51
+ sequence: input.sequence,
52
+ rawDigest,
53
+ });
54
+ const observation = Object.freeze({
55
+ observationId: id,
56
+ sessionId: input.sessionId,
57
+ runId: input.runId,
58
+ operationId: input.operationId,
59
+ sequence: input.sequence,
60
+ rawDigest,
61
+ byteLength: input.bytes.length,
62
+ bytes: input.bytes,
63
+ status: input.status,
64
+ kind: input.kind ?? "generic",
65
+ privacy: input.privacy ?? "raw-private",
66
+ sourceComplete: input.sourceComplete ?? true,
67
+ ...(input.toolCallId === undefined ? {} : { toolCallId: input.toolCallId }),
68
+ });
69
+ this.byId.set(id, observation);
70
+ this.totalBytes += input.bytes.length;
71
+ return observation;
72
+ }
73
+ get(observationId) {
74
+ return this.byId.get(observationId);
75
+ }
76
+ has(observationId) {
77
+ return this.byId.has(observationId);
78
+ }
79
+ /**
80
+ * Scoped byte-range read. `scopeSessionId` must equal the observation's own
81
+ * session — a handle never crosses a session boundary. Byte offsets snap to
82
+ * UTF-8 boundaries; a `strict` range that lands mid-codepoint returns
83
+ * `utf8-boundary` instead of emitting corrupted text.
84
+ */
85
+ read(input) {
86
+ text(input.observationId, "observationId", 128);
87
+ text(input.scopeSessionId, "scopeSessionId", 256);
88
+ integer(input.byteOffset, "byteOffset");
89
+ integer(input.maxBytes, "maxBytes");
90
+ ensure(input.maxBytes > 0, "maxBytes must be positive");
91
+ const observation = this.byId.get(input.observationId);
92
+ if (!observation)
93
+ return { ok: false, error: "observation-not-found" };
94
+ if (observation.sessionId !== input.scopeSessionId) {
95
+ return { ok: false, error: "scope-mismatch" };
96
+ }
97
+ if (input.byteOffset > observation.byteLength) {
98
+ return { ok: false, error: "invalid-range" };
99
+ }
100
+ const snapped = snapUtf8Boundary(observation.bytes, input.byteOffset);
101
+ if (input.strict && snapped.normalized)
102
+ return { ok: false, error: "utf8-boundary" };
103
+ const start = snapped.offset;
104
+ const end = Math.min(observation.byteLength, start + input.maxBytes);
105
+ const chunk = observation.bytes.slice(start, end);
106
+ const read = {
107
+ observationId: observation.observationId,
108
+ text: UTF8_DECODER.decode(chunk),
109
+ byteOffset: start,
110
+ byteLength: chunk.length,
111
+ nextOffset: end,
112
+ eof: end >= observation.byteLength,
113
+ truncated: end < observation.byteLength,
114
+ sourceComplete: observation.sourceComplete,
115
+ normalized: snapped.normalized,
116
+ };
117
+ return { ok: true, read };
118
+ }
119
+ get size() {
120
+ return this.byId.size;
121
+ }
122
+ get bytes() {
123
+ return this.totalBytes;
124
+ }
125
+ }
126
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/observation/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bounded raw-observation store — U1.\n *\n * Session-scoped, in-memory, deterministic. Stores exact bytes with their\n * execution identity, and serves byte-range reads that snap to UTF-8 code-point\n * boundaries rather than emitting silently-corrupted text. Over-capacity puts\n * fail closed with `archive-unavailable` instead of silently evicting evidence.\n */\n\nimport { ensure, integer, text } from \"../metacognition/validation.ts\";\nimport { observationId as makeObservationId, sha256Hex } from \"./identity.ts\";\nimport type {\n\tObservationKind,\n\tObservationPrivacy,\n\tObservationRead,\n\tObservationReadResult,\n\tObservationStatus,\n\tRawObservation,\n} from \"./types.ts\";\n\nexport interface ObservationStoreOptions {\n\treadonly maxObservations?: number;\n\treadonly maxTotalBytes?: number;\n\treadonly maxObservationBytes?: number;\n}\n\nconst DEFAULT_MAX_OBSERVATIONS = 512;\nconst DEFAULT_MAX_TOTAL_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_MAX_OBSERVATION_BYTES = 8 * 1024 * 1024;\n\nconst UTF8_DECODER = new TextDecoder(\"utf-8\", { fatal: false });\n\n/** Snap a byte offset down to the nearest UTF-8 code-point boundary. */\nfunction snapUtf8Boundary(bytes: Uint8Array, offset: number): { offset: number; normalized: boolean } {\n\tif (offset <= 0 || offset >= bytes.length)\n\t\treturn { offset: Math.max(0, Math.min(offset, bytes.length)), normalized: false };\n\t// A continuation byte is 0b10xxxxxx; walk back to a lead byte or ASCII.\n\tlet i = offset;\n\twhile (i > 0 && (bytes[i]! & 0xc0) === 0x80) i -= 1;\n\treturn { offset: i, normalized: i !== offset };\n}\n\nexport class ObservationStore {\n\tprivate readonly maxObservations: number;\n\tprivate readonly maxTotalBytes: number;\n\tprivate readonly maxObservationBytes: number;\n\tprivate readonly byId = new Map<string, RawObservation>();\n\tprivate totalBytes = 0;\n\n\tconstructor(options: ObservationStoreOptions = {}) {\n\t\tthis.maxObservations = options.maxObservations ?? DEFAULT_MAX_OBSERVATIONS;\n\t\tthis.maxTotalBytes = options.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES;\n\t\tthis.maxObservationBytes = options.maxObservationBytes ?? DEFAULT_MAX_OBSERVATION_BYTES;\n\t}\n\n\t/** Store one raw observation. Throws (fails closed) on malformed input or capacity. */\n\tput(input: {\n\t\treadonly sessionId: string;\n\t\treadonly runId: string;\n\t\treadonly operationId: string;\n\t\treadonly sequence: number;\n\t\treadonly bytes: Uint8Array;\n\t\treadonly status: ObservationStatus;\n\t\treadonly kind?: ObservationKind;\n\t\treadonly privacy?: ObservationPrivacy;\n\t\treadonly sourceComplete?: boolean;\n\t\treadonly toolCallId?: string;\n\t}): RawObservation {\n\t\ttext(input.sessionId, \"sessionId\", 256);\n\t\ttext(input.runId, \"runId\", 256);\n\t\ttext(input.operationId, \"operationId\", 256);\n\t\tinteger(input.sequence, \"sequence\");\n\t\tensure(input.bytes instanceof Uint8Array, \"bytes must be a Uint8Array\");\n\t\tensure(input.bytes.length <= this.maxObservationBytes, \"observation exceeds per-item cap\");\n\t\tif (this.byId.size >= this.maxObservations || this.totalBytes + input.bytes.length > this.maxTotalBytes) {\n\t\t\tthrow new Error(\"archive-unavailable: observation store capacity exceeded\");\n\t\t}\n\t\tconst rawDigest = sha256Hex(input.bytes);\n\t\tconst id = makeObservationId({\n\t\t\trunId: input.runId,\n\t\t\toperationId: input.operationId,\n\t\t\tsequence: input.sequence,\n\t\t\trawDigest,\n\t\t});\n\t\tconst observation: RawObservation = Object.freeze({\n\t\t\tobservationId: id,\n\t\t\tsessionId: input.sessionId,\n\t\t\trunId: input.runId,\n\t\t\toperationId: input.operationId,\n\t\t\tsequence: input.sequence,\n\t\t\trawDigest,\n\t\t\tbyteLength: input.bytes.length,\n\t\t\tbytes: input.bytes,\n\t\t\tstatus: input.status,\n\t\t\tkind: input.kind ?? \"generic\",\n\t\t\tprivacy: input.privacy ?? \"raw-private\",\n\t\t\tsourceComplete: input.sourceComplete ?? true,\n\t\t\t...(input.toolCallId === undefined ? {} : { toolCallId: input.toolCallId }),\n\t\t});\n\t\tthis.byId.set(id, observation);\n\t\tthis.totalBytes += input.bytes.length;\n\t\treturn observation;\n\t}\n\n\tget(observationId: string): RawObservation | undefined {\n\t\treturn this.byId.get(observationId);\n\t}\n\n\thas(observationId: string): boolean {\n\t\treturn this.byId.has(observationId);\n\t}\n\n\t/**\n\t * Scoped byte-range read. `scopeSessionId` must equal the observation's own\n\t * session — a handle never crosses a session boundary. Byte offsets snap to\n\t * UTF-8 boundaries; a `strict` range that lands mid-codepoint returns\n\t * `utf8-boundary` instead of emitting corrupted text.\n\t */\n\tread(input: {\n\t\treadonly observationId: string;\n\t\treadonly scopeSessionId: string;\n\t\treadonly byteOffset: number;\n\t\treadonly maxBytes: number;\n\t\treadonly strict?: boolean;\n\t}): ObservationReadResult {\n\t\ttext(input.observationId, \"observationId\", 128);\n\t\ttext(input.scopeSessionId, \"scopeSessionId\", 256);\n\t\tinteger(input.byteOffset, \"byteOffset\");\n\t\tinteger(input.maxBytes, \"maxBytes\");\n\t\tensure(input.maxBytes > 0, \"maxBytes must be positive\");\n\t\tconst observation = this.byId.get(input.observationId);\n\t\tif (!observation) return { ok: false, error: \"observation-not-found\" };\n\t\tif (observation.sessionId !== input.scopeSessionId) {\n\t\t\treturn { ok: false, error: \"scope-mismatch\" };\n\t\t}\n\t\tif (input.byteOffset > observation.byteLength) {\n\t\t\treturn { ok: false, error: \"invalid-range\" };\n\t\t}\n\t\tconst snapped = snapUtf8Boundary(observation.bytes, input.byteOffset);\n\t\tif (input.strict && snapped.normalized) return { ok: false, error: \"utf8-boundary\" };\n\t\tconst start = snapped.offset;\n\t\tconst end = Math.min(observation.byteLength, start + input.maxBytes);\n\t\tconst chunk = observation.bytes.slice(start, end);\n\t\tconst read: ObservationRead = {\n\t\t\tobservationId: observation.observationId,\n\t\t\ttext: UTF8_DECODER.decode(chunk),\n\t\t\tbyteOffset: start,\n\t\t\tbyteLength: chunk.length,\n\t\t\tnextOffset: end,\n\t\t\teof: end >= observation.byteLength,\n\t\t\ttruncated: end < observation.byteLength,\n\t\t\tsourceComplete: observation.sourceComplete,\n\t\t\tnormalized: snapped.normalized,\n\t\t};\n\t\treturn { ok: true, read };\n\t}\n\n\tget size(): number {\n\t\treturn this.byId.size;\n\t}\n\n\tget bytes(): number {\n\t\treturn this.totalBytes;\n\t}\n}\n"]}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Observation kernel types — U1/U2 of the upgraded SoL-Pi design.
3
+ *
4
+ * Raw observations are host-owned execution artifacts. Model-facing views are
5
+ * derived projections and never replace the raw authority: `taskVerdict` is
6
+ * always `not-assessed` here because a view cannot prove completion.
7
+ */
8
+ export type ObservationStatus = "exit" | "signal" | "partial" | "unavailable";
9
+ export type ObservationPrivacy = "raw-private" | "approved-sanitized";
10
+ export type ObservationKind = "log" | "test" | "command" | "file" | "dom" | "network" | "generic";
11
+ /**
12
+ * A stored execution artifact. `observationId` binds the run, operation and
13
+ * sequence to the content digest, so two identical byte outputs from different
14
+ * executions remain distinct events (never content-hash deduped into one).
15
+ */
16
+ export interface RawObservation {
17
+ readonly observationId: string;
18
+ readonly sessionId: string;
19
+ readonly runId: string;
20
+ readonly operationId: string;
21
+ readonly sequence: number;
22
+ readonly rawDigest: string;
23
+ readonly byteLength: number;
24
+ readonly bytes: Uint8Array;
25
+ readonly status: ObservationStatus;
26
+ readonly kind: ObservationKind;
27
+ readonly privacy: ObservationPrivacy;
28
+ /** False when the capture itself was truncated upstream — never claim "full raw". */
29
+ readonly sourceComplete: boolean;
30
+ readonly toolCallId?: string;
31
+ }
32
+ export type ObservationViewKind = "full" | "excerpt" | "evidence" | "pointer";
33
+ export type ObservationCoverageStatus = "complete" | "partial" | "unknown";
34
+ /**
35
+ * A model-facing projection of a stored observation. `transformationDigest`
36
+ * binds the view to the deterministic transform that produced it, and
37
+ * `coverageStatus` records whether the view preserves the required fact atoms.
38
+ */
39
+ export interface ObservationView {
40
+ readonly viewKind: ObservationViewKind;
41
+ readonly observationId: string;
42
+ readonly parentDigest: string;
43
+ readonly transformationDigest: string;
44
+ readonly text: string;
45
+ readonly estimatedTokens: number;
46
+ readonly coveredFactIds: readonly string[];
47
+ readonly missingRequiredFactIds: readonly string[];
48
+ readonly coverageStatus: ObservationCoverageStatus;
49
+ readonly taskVerdict: "not-assessed";
50
+ }
51
+ /** Result of a scoped, byte-bounded read against a stored observation. */
52
+ export interface ObservationRead {
53
+ readonly observationId: string;
54
+ readonly text: string;
55
+ readonly byteOffset: number;
56
+ readonly byteLength: number;
57
+ readonly nextOffset: number;
58
+ readonly eof: boolean;
59
+ readonly truncated: boolean;
60
+ readonly sourceComplete: boolean;
61
+ /** True when the requested byte range was snapped to UTF-8 boundaries. */
62
+ readonly normalized: boolean;
63
+ }
64
+ export type ObservationReadError = "observation-not-found" | "scope-mismatch" | "invalid-range" | "utf8-boundary" | "archive-unavailable";
65
+ export type ObservationReadResult = {
66
+ readonly ok: true;
67
+ readonly read: ObservationRead;
68
+ } | {
69
+ readonly ok: false;
70
+ readonly error: ObservationReadError;
71
+ };
72
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/observation/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;AAC9E,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG,oBAAoB,CAAC;AACtE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AAElG;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,uFAAqF;IACrF,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAC9E,MAAM,MAAM,yBAAyB,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAE3E;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAC;IACnD,QAAQ,CAAC,cAAc,EAAE,yBAAyB,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;CACrC;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAC7B,uBAAuB,GACvB,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,qBAAqB,CAAC;AAEzB,MAAM,MAAM,qBAAqB,GAC9B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAA;CAAE,CAAC","sourcesContent":["/**\n * Observation kernel types — U1/U2 of the upgraded SoL-Pi design.\n *\n * Raw observations are host-owned execution artifacts. Model-facing views are\n * derived projections and never replace the raw authority: `taskVerdict` is\n * always `not-assessed` here because a view cannot prove completion.\n */\n\nexport type ObservationStatus = \"exit\" | \"signal\" | \"partial\" | \"unavailable\";\nexport type ObservationPrivacy = \"raw-private\" | \"approved-sanitized\";\nexport type ObservationKind = \"log\" | \"test\" | \"command\" | \"file\" | \"dom\" | \"network\" | \"generic\";\n\n/**\n * A stored execution artifact. `observationId` binds the run, operation and\n * sequence to the content digest, so two identical byte outputs from different\n * executions remain distinct events (never content-hash deduped into one).\n */\nexport interface RawObservation {\n\treadonly observationId: string;\n\treadonly sessionId: string;\n\treadonly runId: string;\n\treadonly operationId: string;\n\treadonly sequence: number;\n\treadonly rawDigest: string;\n\treadonly byteLength: number;\n\treadonly bytes: Uint8Array;\n\treadonly status: ObservationStatus;\n\treadonly kind: ObservationKind;\n\treadonly privacy: ObservationPrivacy;\n\t/** False when the capture itself was truncated upstream — never claim \"full raw\". */\n\treadonly sourceComplete: boolean;\n\treadonly toolCallId?: string;\n}\n\nexport type ObservationViewKind = \"full\" | \"excerpt\" | \"evidence\" | \"pointer\";\nexport type ObservationCoverageStatus = \"complete\" | \"partial\" | \"unknown\";\n\n/**\n * A model-facing projection of a stored observation. `transformationDigest`\n * binds the view to the deterministic transform that produced it, and\n * `coverageStatus` records whether the view preserves the required fact atoms.\n */\nexport interface ObservationView {\n\treadonly viewKind: ObservationViewKind;\n\treadonly observationId: string;\n\treadonly parentDigest: string;\n\treadonly transformationDigest: string;\n\treadonly text: string;\n\treadonly estimatedTokens: number;\n\treadonly coveredFactIds: readonly string[];\n\treadonly missingRequiredFactIds: readonly string[];\n\treadonly coverageStatus: ObservationCoverageStatus;\n\treadonly taskVerdict: \"not-assessed\";\n}\n\n/** Result of a scoped, byte-bounded read against a stored observation. */\nexport interface ObservationRead {\n\treadonly observationId: string;\n\treadonly text: string;\n\treadonly byteOffset: number;\n\treadonly byteLength: number;\n\treadonly nextOffset: number;\n\treadonly eof: boolean;\n\treadonly truncated: boolean;\n\treadonly sourceComplete: boolean;\n\t/** True when the requested byte range was snapped to UTF-8 boundaries. */\n\treadonly normalized: boolean;\n}\n\nexport type ObservationReadError =\n\t| \"observation-not-found\"\n\t| \"scope-mismatch\"\n\t| \"invalid-range\"\n\t| \"utf8-boundary\"\n\t| \"archive-unavailable\";\n\nexport type ObservationReadResult =\n\t| { readonly ok: true; readonly read: ObservationRead }\n\t| { readonly ok: false; readonly error: ObservationReadError };\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Observation kernel types — U1/U2 of the upgraded SoL-Pi design.
3
+ *
4
+ * Raw observations are host-owned execution artifacts. Model-facing views are
5
+ * derived projections and never replace the raw authority: `taskVerdict` is
6
+ * always `not-assessed` here because a view cannot prove completion.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/observation/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Observation kernel types — U1/U2 of the upgraded SoL-Pi design.\n *\n * Raw observations are host-owned execution artifacts. Model-facing views are\n * derived projections and never replace the raw authority: `taskVerdict` is\n * always `not-assessed` here because a view cannot prove completion.\n */\n\nexport type ObservationStatus = \"exit\" | \"signal\" | \"partial\" | \"unavailable\";\nexport type ObservationPrivacy = \"raw-private\" | \"approved-sanitized\";\nexport type ObservationKind = \"log\" | \"test\" | \"command\" | \"file\" | \"dom\" | \"network\" | \"generic\";\n\n/**\n * A stored execution artifact. `observationId` binds the run, operation and\n * sequence to the content digest, so two identical byte outputs from different\n * executions remain distinct events (never content-hash deduped into one).\n */\nexport interface RawObservation {\n\treadonly observationId: string;\n\treadonly sessionId: string;\n\treadonly runId: string;\n\treadonly operationId: string;\n\treadonly sequence: number;\n\treadonly rawDigest: string;\n\treadonly byteLength: number;\n\treadonly bytes: Uint8Array;\n\treadonly status: ObservationStatus;\n\treadonly kind: ObservationKind;\n\treadonly privacy: ObservationPrivacy;\n\t/** False when the capture itself was truncated upstream — never claim \"full raw\". */\n\treadonly sourceComplete: boolean;\n\treadonly toolCallId?: string;\n}\n\nexport type ObservationViewKind = \"full\" | \"excerpt\" | \"evidence\" | \"pointer\";\nexport type ObservationCoverageStatus = \"complete\" | \"partial\" | \"unknown\";\n\n/**\n * A model-facing projection of a stored observation. `transformationDigest`\n * binds the view to the deterministic transform that produced it, and\n * `coverageStatus` records whether the view preserves the required fact atoms.\n */\nexport interface ObservationView {\n\treadonly viewKind: ObservationViewKind;\n\treadonly observationId: string;\n\treadonly parentDigest: string;\n\treadonly transformationDigest: string;\n\treadonly text: string;\n\treadonly estimatedTokens: number;\n\treadonly coveredFactIds: readonly string[];\n\treadonly missingRequiredFactIds: readonly string[];\n\treadonly coverageStatus: ObservationCoverageStatus;\n\treadonly taskVerdict: \"not-assessed\";\n}\n\n/** Result of a scoped, byte-bounded read against a stored observation. */\nexport interface ObservationRead {\n\treadonly observationId: string;\n\treadonly text: string;\n\treadonly byteOffset: number;\n\treadonly byteLength: number;\n\treadonly nextOffset: number;\n\treadonly eof: boolean;\n\treadonly truncated: boolean;\n\treadonly sourceComplete: boolean;\n\t/** True when the requested byte range was snapped to UTF-8 boundaries. */\n\treadonly normalized: boolean;\n}\n\nexport type ObservationReadError =\n\t| \"observation-not-found\"\n\t| \"scope-mismatch\"\n\t| \"invalid-range\"\n\t| \"utf8-boundary\"\n\t| \"archive-unavailable\";\n\nexport type ObservationReadResult =\n\t| { readonly ok: true; readonly read: ObservationRead }\n\t| { readonly ok: false; readonly error: ObservationReadError };\n"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Deterministic observation views with a coverage gate — U1/U3.
3
+ *
4
+ * A view is a projection of a stored raw observation, never a rewrite of it.
5
+ * `chooseView` refuses any candidate that drops a required fact atom: a cheap
6
+ * representation is admissible only if it preserves the decision-relevant
7
+ * facts the current obligations need. `coverageStatus` is `unknown` when the
8
+ * input carries no required fact set — never silently "covered".
9
+ */
10
+ import type { ObservationView, RawObservation } from "./types.ts";
11
+ export declare function estimateViewTokens(text: string): number;
12
+ /** A host-verifiable fact atom extracted from raw bytes, e.g. exitCode=1. */
13
+ export interface FactAtom {
14
+ readonly id: string;
15
+ /** The verbatim text that must appear in a view for the atom to count as covered. */
16
+ readonly evidence: string;
17
+ readonly byteOffset: number;
18
+ readonly byteLength: number;
19
+ }
20
+ /**
21
+ * Extract host-verifiable fact atoms from raw bytes. `requiredFactIds` limits
22
+ * extraction to the caller's obligation set; when omitted, all recognized
23
+ * atoms are returned so a view can advertise what it preserves.
24
+ */
25
+ export declare function extractFacts(raw: Uint8Array, requiredFactIds?: readonly string[]): FactAtom[];
26
+ /**
27
+ * Build the candidate views for one observation. Every view records which
28
+ * fact atoms it preserves and which required atoms it would drop, so the
29
+ * selector can gate on coverage rather than token density alone.
30
+ */
31
+ export declare function makeViews(observation: RawObservation, requiredFactIds?: readonly string[]): readonly ObservationView[];
32
+ /**
33
+ * Coverage-gated deterministic selection: the smallest view that preserves
34
+ * every required fact within budget. Returns null (infeasible) rather than
35
+ * silently dropping a required atom — the caller must widen the budget or keep
36
+ * the raw observation.
37
+ */
38
+ export declare function chooseView(views: readonly ObservationView[], budgetTokens: number, requiredFactIds?: readonly string[]): ObservationView | null;
39
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/observation/view.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAA6B,eAAe,EAAuB,cAAc,EAAE,MAAM,YAAY,CAAC;AAOlH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AASD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,EAAE,CAyB7F;AAwDD;;;;GAIG;AACH,wBAAgB,SAAS,CACxB,WAAW,EAAE,cAAc,EAC3B,eAAe,GAAE,SAAS,MAAM,EAAO,GACrC,SAAS,eAAe,EAAE,CAkC5B;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,SAAS,eAAe,EAAE,EACjC,YAAY,EAAE,MAAM,EACpB,eAAe,GAAE,SAAS,MAAM,EAAO,GACrC,eAAe,GAAG,IAAI,CAqBxB","sourcesContent":["/**\n * Deterministic observation views with a coverage gate — U1/U3.\n *\n * A view is a projection of a stored raw observation, never a rewrite of it.\n * `chooseView` refuses any candidate that drops a required fact atom: a cheap\n * representation is admissible only if it preserves the decision-relevant\n * facts the current obligations need. `coverageStatus` is `unknown` when the\n * input carries no required fact set — never silently \"covered\".\n */\n\nimport { ensure, integer, text } from \"../metacognition/validation.ts\";\nimport { viewDigestOf } from \"./identity.ts\";\nimport type { ObservationCoverageStatus, ObservationView, ObservationViewKind, RawObservation } from \"./types.ts\";\n\nconst CHARS_PER_TOKEN = 4;\n/** Per-atom-class occurrence cap. Four classes stay well under any global bound. */\nconst MAX_FACTS_PER_ID = 32;\nconst MAX_VIEW_TEXT = 32_768;\n\nexport function estimateViewTokens(text: string): number {\n\treturn Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN));\n}\n\n/** A host-verifiable fact atom extracted from raw bytes, e.g. exitCode=1. */\nexport interface FactAtom {\n\treadonly id: string;\n\t/** The verbatim text that must appear in a view for the atom to count as covered. */\n\treadonly evidence: string;\n\treadonly byteOffset: number;\n\treadonly byteLength: number;\n}\n\nconst FACT_PATTERNS: readonly { readonly id: string; readonly re: RegExp }[] = [\n\t{ id: \"exit-code\", re: /exit code[:\\s]+(-?\\d+)|exitCode\\s*[=:]\\s*(-?\\d+)/i },\n\t{ id: \"test-failure\", re: /FAIL[:\\s]+([A-Za-z0-9_.\\-/]+)|✗\\s*([A-Za-z0-9_.\\-/]+)/ },\n\t{ id: \"permission-denied\", re: /permission[ _-]?denied|EACCES/i },\n\t{ id: \"error-marker\", re: /(?:error|errno|exception|traceback)[:\\s]/i },\n];\n\n/**\n * Extract host-verifiable fact atoms from raw bytes. `requiredFactIds` limits\n * extraction to the caller's obligation set; when omitted, all recognized\n * atoms are returned so a view can advertise what it preserves.\n */\nexport function extractFacts(raw: Uint8Array, requiredFactIds?: readonly string[]): FactAtom[] {\n\tconst decoded = new TextDecoder(\"utf-8\", { fatal: false }).decode(raw);\n\tconst encoder = new TextEncoder();\n\tconst wanted = requiredFactIds === undefined ? undefined : new Set(requiredFactIds);\n\tconst facts: FactAtom[] = [];\n\tfor (const { id, re } of FACT_PATTERNS) {\n\t\tif (wanted !== undefined && !wanted.has(id)) continue;\n\t\t// Per-id cap, not a shared running total: a log that repeats one atom\n\t\t// thousands of times must not starve a later pattern out of extraction\n\t\t// entirely, or a required atom silently disappears from every view.\n\t\tlet perId = 0;\n\t\tfor (const match of decoded.matchAll(new RegExp(re.source, re.flags.includes(\"g\") ? re.flags : `${re.flags}g`))) {\n\t\t\tif (perId >= MAX_FACTS_PER_ID) break;\n\t\t\tconst evidence = match[0];\n\t\t\tconst byteOffset = encoder.encode(decoded.slice(0, match.index ?? 0)).length;\n\t\t\tfacts.push({\n\t\t\t\tid,\n\t\t\t\tevidence,\n\t\t\t\tbyteOffset,\n\t\t\t\tbyteLength: encoder.encode(evidence).length,\n\t\t\t});\n\t\t\tperId += 1;\n\t\t}\n\t}\n\treturn facts;\n}\n\n/**\n * Collapse repeated identical atoms into one line that keeps the occurrence\n * count. Without this the evidence view of a log that repeats one failure is\n * larger than the raw text it was supposed to shrink, and the selector\n * correctly but uselessly falls back to full text.\n */\nfunction dedupeFacts(facts: readonly FactAtom[]): readonly { readonly fact: FactAtom; readonly count: number }[] {\n\tconst byKey = new Map<string, { fact: FactAtom; count: number }>();\n\tfor (const fact of facts) {\n\t\tconst key = `${fact.id}\\u0000${fact.evidence}`;\n\t\tconst existing = byKey.get(key);\n\t\tif (existing === undefined) byKey.set(key, { fact, count: 1 });\n\t\telse existing.count += 1;\n\t}\n\treturn [...byKey.values()];\n}\n\nfunction viewText(raw: Uint8Array, facts: readonly FactAtom[], kind: ObservationViewKind): string {\n\tconst decoded = new TextDecoder(\"utf-8\", { fatal: false }).decode(raw);\n\tswitch (kind) {\n\t\tcase \"full\":\n\t\t\treturn decoded;\n\t\tcase \"excerpt\": {\n\t\t\tif (decoded.length <= MAX_VIEW_TEXT) return decoded;\n\t\t\tconst head = decoded.slice(0, Math.floor(MAX_VIEW_TEXT / 2));\n\t\t\tconst tail = decoded.slice(-Math.floor(MAX_VIEW_TEXT / 2));\n\t\t\treturn `${head}\\n…[omitted ${decoded.length - MAX_VIEW_TEXT} chars]…\\n${tail}`;\n\t\t}\n\t\tcase \"evidence\":\n\t\t\treturn facts.length === 0\n\t\t\t\t? \"\"\n\t\t\t\t: dedupeFacts(facts)\n\t\t\t\t\t\t.map(({ fact, count }) =>\n\t\t\t\t\t\t\tcount === 1\n\t\t\t\t\t\t\t\t? `${fact.id} @${fact.byteOffset}: ${fact.evidence}`\n\t\t\t\t\t\t\t\t: `${fact.id} @${fact.byteOffset} \\u00d7${count}: ${fact.evidence}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\\n\")\n\t\t\t\t\t\t.slice(0, MAX_VIEW_TEXT);\n\t\tcase \"pointer\":\n\t\t\treturn `[observation ${raw.length} bytes; digest-bound; read via handle]`;\n\t}\n}\n\nfunction coverageFor(\n\tcovered: readonly string[],\n\trequired: readonly string[],\n): { status: ObservationCoverageStatus; missing: readonly string[] } {\n\tif (required.length === 0) return { status: \"unknown\", missing: [] };\n\tconst coveredSet = new Set(covered);\n\tconst missing = required.filter((id) => !coveredSet.has(id));\n\treturn { status: missing.length === 0 ? \"complete\" : \"partial\", missing };\n}\n\n/**\n * Build the candidate views for one observation. Every view records which\n * fact atoms it preserves and which required atoms it would drop, so the\n * selector can gate on coverage rather than token density alone.\n */\nexport function makeViews(\n\tobservation: RawObservation,\n\trequiredFactIds: readonly string[] = [],\n): readonly ObservationView[] {\n\ttext(observation.observationId, \"observationId\", 128);\n\tfor (const id of requiredFactIds) text(id, \"requiredFactId\", 256);\n\tconst facts = extractFacts(observation.bytes);\n\tconst coveredByKind: Record<ObservationViewKind, readonly string[]> = {\n\t\tfull: facts.map((f) => f.id),\n\t\texcerpt: facts.map((f) => f.id), // excerpt keeps head+tail; see note\n\t\tevidence: facts.map((f) => f.id),\n\t\tpointer: [],\n\t};\n\tconst kinds: readonly ObservationViewKind[] = [\"full\", \"excerpt\", \"evidence\", \"pointer\"];\n\treturn kinds.map((kind) => {\n\t\tconst body = viewText(observation.bytes, facts, kind);\n\t\tconst covered = coveredByKind[kind].filter(\n\t\t\t(id) => body.includes(id) || kind !== \"evidence\" || facts.some((f) => f.id === id),\n\t\t);\n\t\tconst { status, missing } = coverageFor(covered, requiredFactIds);\n\t\treturn Object.freeze({\n\t\t\tviewKind: kind,\n\t\t\tobservationId: observation.observationId,\n\t\t\tparentDigest: observation.rawDigest,\n\t\t\ttransformationDigest: viewDigestOf({\n\t\t\t\tobservationId: observation.observationId,\n\t\t\t\tviewKind: kind,\n\t\t\t\tparams: `req:${[...requiredFactIds].sort().join(\",\")}`,\n\t\t\t}),\n\t\t\ttext: body,\n\t\t\testimatedTokens: estimateViewTokens(body),\n\t\t\tcoveredFactIds: covered,\n\t\t\tmissingRequiredFactIds: missing,\n\t\t\tcoverageStatus: status,\n\t\t\ttaskVerdict: \"not-assessed\" as const,\n\t\t});\n\t});\n}\n\n/**\n * Coverage-gated deterministic selection: the smallest view that preserves\n * every required fact within budget. Returns null (infeasible) rather than\n * silently dropping a required atom — the caller must widen the budget or keep\n * the raw observation.\n */\nexport function chooseView(\n\tviews: readonly ObservationView[],\n\tbudgetTokens: number,\n\trequiredFactIds: readonly string[] = [],\n): ObservationView | null {\n\tinteger(budgetTokens, \"budgetTokens\");\n\tensure(views.length > 0, \"views must be nonempty\");\n\tfor (const id of requiredFactIds) text(id, \"requiredFactId\", 256);\n\tconst admissible = views.filter(\n\t\t(v) => v.estimatedTokens <= budgetTokens && requiredFactIds.every((id) => v.coveredFactIds.includes(id)),\n\t);\n\tif (admissible.length === 0) return null;\n\t// Smallest tokens wins; break ties toward the higher-fidelity kind.\n\tconst rank: Record<ObservationViewKind, number> = { full: 0, excerpt: 1, evidence: 2, pointer: 3 };\n\tlet best: ObservationView | undefined;\n\tfor (const view of admissible) {\n\t\tif (\n\t\t\tbest === undefined ||\n\t\t\tview.estimatedTokens < best.estimatedTokens ||\n\t\t\t(view.estimatedTokens === best.estimatedTokens && rank[view.viewKind] < rank[best.viewKind])\n\t\t) {\n\t\t\tbest = view;\n\t\t}\n\t}\n\treturn best ?? null;\n}\n"]}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Deterministic observation views with a coverage gate — U1/U3.
3
+ *
4
+ * A view is a projection of a stored raw observation, never a rewrite of it.
5
+ * `chooseView` refuses any candidate that drops a required fact atom: a cheap
6
+ * representation is admissible only if it preserves the decision-relevant
7
+ * facts the current obligations need. `coverageStatus` is `unknown` when the
8
+ * input carries no required fact set — never silently "covered".
9
+ */
10
+ import { ensure, integer, text } from "../metacognition/validation.js";
11
+ import { viewDigestOf } from "./identity.js";
12
+ const CHARS_PER_TOKEN = 4;
13
+ /** Per-atom-class occurrence cap. Four classes stay well under any global bound. */
14
+ const MAX_FACTS_PER_ID = 32;
15
+ const MAX_VIEW_TEXT = 32_768;
16
+ export function estimateViewTokens(text) {
17
+ return Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN));
18
+ }
19
+ const FACT_PATTERNS = [
20
+ { id: "exit-code", re: /exit code[:\s]+(-?\d+)|exitCode\s*[=:]\s*(-?\d+)/i },
21
+ { id: "test-failure", re: /FAIL[:\s]+([A-Za-z0-9_.\-/]+)|✗\s*([A-Za-z0-9_.\-/]+)/ },
22
+ { id: "permission-denied", re: /permission[ _-]?denied|EACCES/i },
23
+ { id: "error-marker", re: /(?:error|errno|exception|traceback)[:\s]/i },
24
+ ];
25
+ /**
26
+ * Extract host-verifiable fact atoms from raw bytes. `requiredFactIds` limits
27
+ * extraction to the caller's obligation set; when omitted, all recognized
28
+ * atoms are returned so a view can advertise what it preserves.
29
+ */
30
+ export function extractFacts(raw, requiredFactIds) {
31
+ const decoded = new TextDecoder("utf-8", { fatal: false }).decode(raw);
32
+ const encoder = new TextEncoder();
33
+ const wanted = requiredFactIds === undefined ? undefined : new Set(requiredFactIds);
34
+ const facts = [];
35
+ for (const { id, re } of FACT_PATTERNS) {
36
+ if (wanted !== undefined && !wanted.has(id))
37
+ continue;
38
+ // Per-id cap, not a shared running total: a log that repeats one atom
39
+ // thousands of times must not starve a later pattern out of extraction
40
+ // entirely, or a required atom silently disappears from every view.
41
+ let perId = 0;
42
+ for (const match of decoded.matchAll(new RegExp(re.source, re.flags.includes("g") ? re.flags : `${re.flags}g`))) {
43
+ if (perId >= MAX_FACTS_PER_ID)
44
+ break;
45
+ const evidence = match[0];
46
+ const byteOffset = encoder.encode(decoded.slice(0, match.index ?? 0)).length;
47
+ facts.push({
48
+ id,
49
+ evidence,
50
+ byteOffset,
51
+ byteLength: encoder.encode(evidence).length,
52
+ });
53
+ perId += 1;
54
+ }
55
+ }
56
+ return facts;
57
+ }
58
+ /**
59
+ * Collapse repeated identical atoms into one line that keeps the occurrence
60
+ * count. Without this the evidence view of a log that repeats one failure is
61
+ * larger than the raw text it was supposed to shrink, and the selector
62
+ * correctly but uselessly falls back to full text.
63
+ */
64
+ function dedupeFacts(facts) {
65
+ const byKey = new Map();
66
+ for (const fact of facts) {
67
+ const key = `${fact.id}\u0000${fact.evidence}`;
68
+ const existing = byKey.get(key);
69
+ if (existing === undefined)
70
+ byKey.set(key, { fact, count: 1 });
71
+ else
72
+ existing.count += 1;
73
+ }
74
+ return [...byKey.values()];
75
+ }
76
+ function viewText(raw, facts, kind) {
77
+ const decoded = new TextDecoder("utf-8", { fatal: false }).decode(raw);
78
+ switch (kind) {
79
+ case "full":
80
+ return decoded;
81
+ case "excerpt": {
82
+ if (decoded.length <= MAX_VIEW_TEXT)
83
+ return decoded;
84
+ const head = decoded.slice(0, Math.floor(MAX_VIEW_TEXT / 2));
85
+ const tail = decoded.slice(-Math.floor(MAX_VIEW_TEXT / 2));
86
+ return `${head}\n…[omitted ${decoded.length - MAX_VIEW_TEXT} chars]…\n${tail}`;
87
+ }
88
+ case "evidence":
89
+ return facts.length === 0
90
+ ? ""
91
+ : dedupeFacts(facts)
92
+ .map(({ fact, count }) => count === 1
93
+ ? `${fact.id} @${fact.byteOffset}: ${fact.evidence}`
94
+ : `${fact.id} @${fact.byteOffset} \u00d7${count}: ${fact.evidence}`)
95
+ .join("\n")
96
+ .slice(0, MAX_VIEW_TEXT);
97
+ case "pointer":
98
+ return `[observation ${raw.length} bytes; digest-bound; read via handle]`;
99
+ }
100
+ }
101
+ function coverageFor(covered, required) {
102
+ if (required.length === 0)
103
+ return { status: "unknown", missing: [] };
104
+ const coveredSet = new Set(covered);
105
+ const missing = required.filter((id) => !coveredSet.has(id));
106
+ return { status: missing.length === 0 ? "complete" : "partial", missing };
107
+ }
108
+ /**
109
+ * Build the candidate views for one observation. Every view records which
110
+ * fact atoms it preserves and which required atoms it would drop, so the
111
+ * selector can gate on coverage rather than token density alone.
112
+ */
113
+ export function makeViews(observation, requiredFactIds = []) {
114
+ text(observation.observationId, "observationId", 128);
115
+ for (const id of requiredFactIds)
116
+ text(id, "requiredFactId", 256);
117
+ const facts = extractFacts(observation.bytes);
118
+ const coveredByKind = {
119
+ full: facts.map((f) => f.id),
120
+ excerpt: facts.map((f) => f.id), // excerpt keeps head+tail; see note
121
+ evidence: facts.map((f) => f.id),
122
+ pointer: [],
123
+ };
124
+ const kinds = ["full", "excerpt", "evidence", "pointer"];
125
+ return kinds.map((kind) => {
126
+ const body = viewText(observation.bytes, facts, kind);
127
+ const covered = coveredByKind[kind].filter((id) => body.includes(id) || kind !== "evidence" || facts.some((f) => f.id === id));
128
+ const { status, missing } = coverageFor(covered, requiredFactIds);
129
+ return Object.freeze({
130
+ viewKind: kind,
131
+ observationId: observation.observationId,
132
+ parentDigest: observation.rawDigest,
133
+ transformationDigest: viewDigestOf({
134
+ observationId: observation.observationId,
135
+ viewKind: kind,
136
+ params: `req:${[...requiredFactIds].sort().join(",")}`,
137
+ }),
138
+ text: body,
139
+ estimatedTokens: estimateViewTokens(body),
140
+ coveredFactIds: covered,
141
+ missingRequiredFactIds: missing,
142
+ coverageStatus: status,
143
+ taskVerdict: "not-assessed",
144
+ });
145
+ });
146
+ }
147
+ /**
148
+ * Coverage-gated deterministic selection: the smallest view that preserves
149
+ * every required fact within budget. Returns null (infeasible) rather than
150
+ * silently dropping a required atom — the caller must widen the budget or keep
151
+ * the raw observation.
152
+ */
153
+ export function chooseView(views, budgetTokens, requiredFactIds = []) {
154
+ integer(budgetTokens, "budgetTokens");
155
+ ensure(views.length > 0, "views must be nonempty");
156
+ for (const id of requiredFactIds)
157
+ text(id, "requiredFactId", 256);
158
+ const admissible = views.filter((v) => v.estimatedTokens <= budgetTokens && requiredFactIds.every((id) => v.coveredFactIds.includes(id)));
159
+ if (admissible.length === 0)
160
+ return null;
161
+ // Smallest tokens wins; break ties toward the higher-fidelity kind.
162
+ const rank = { full: 0, excerpt: 1, evidence: 2, pointer: 3 };
163
+ let best;
164
+ for (const view of admissible) {
165
+ if (best === undefined ||
166
+ view.estimatedTokens < best.estimatedTokens ||
167
+ (view.estimatedTokens === best.estimatedTokens && rank[view.viewKind] < rank[best.viewKind])) {
168
+ best = view;
169
+ }
170
+ }
171
+ return best ?? null;
172
+ }
173
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../src/observation/view.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Deterministic observation views with a coverage gate — U1/U3.\n *\n * A view is a projection of a stored raw observation, never a rewrite of it.\n * `chooseView` refuses any candidate that drops a required fact atom: a cheap\n * representation is admissible only if it preserves the decision-relevant\n * facts the current obligations need. `coverageStatus` is `unknown` when the\n * input carries no required fact set — never silently \"covered\".\n */\n\nimport { ensure, integer, text } from \"../metacognition/validation.ts\";\nimport { viewDigestOf } from \"./identity.ts\";\nimport type { ObservationCoverageStatus, ObservationView, ObservationViewKind, RawObservation } from \"./types.ts\";\n\nconst CHARS_PER_TOKEN = 4;\n/** Per-atom-class occurrence cap. Four classes stay well under any global bound. */\nconst MAX_FACTS_PER_ID = 32;\nconst MAX_VIEW_TEXT = 32_768;\n\nexport function estimateViewTokens(text: string): number {\n\treturn Math.max(1, Math.ceil(text.length / CHARS_PER_TOKEN));\n}\n\n/** A host-verifiable fact atom extracted from raw bytes, e.g. exitCode=1. */\nexport interface FactAtom {\n\treadonly id: string;\n\t/** The verbatim text that must appear in a view for the atom to count as covered. */\n\treadonly evidence: string;\n\treadonly byteOffset: number;\n\treadonly byteLength: number;\n}\n\nconst FACT_PATTERNS: readonly { readonly id: string; readonly re: RegExp }[] = [\n\t{ id: \"exit-code\", re: /exit code[:\\s]+(-?\\d+)|exitCode\\s*[=:]\\s*(-?\\d+)/i },\n\t{ id: \"test-failure\", re: /FAIL[:\\s]+([A-Za-z0-9_.\\-/]+)|✗\\s*([A-Za-z0-9_.\\-/]+)/ },\n\t{ id: \"permission-denied\", re: /permission[ _-]?denied|EACCES/i },\n\t{ id: \"error-marker\", re: /(?:error|errno|exception|traceback)[:\\s]/i },\n];\n\n/**\n * Extract host-verifiable fact atoms from raw bytes. `requiredFactIds` limits\n * extraction to the caller's obligation set; when omitted, all recognized\n * atoms are returned so a view can advertise what it preserves.\n */\nexport function extractFacts(raw: Uint8Array, requiredFactIds?: readonly string[]): FactAtom[] {\n\tconst decoded = new TextDecoder(\"utf-8\", { fatal: false }).decode(raw);\n\tconst encoder = new TextEncoder();\n\tconst wanted = requiredFactIds === undefined ? undefined : new Set(requiredFactIds);\n\tconst facts: FactAtom[] = [];\n\tfor (const { id, re } of FACT_PATTERNS) {\n\t\tif (wanted !== undefined && !wanted.has(id)) continue;\n\t\t// Per-id cap, not a shared running total: a log that repeats one atom\n\t\t// thousands of times must not starve a later pattern out of extraction\n\t\t// entirely, or a required atom silently disappears from every view.\n\t\tlet perId = 0;\n\t\tfor (const match of decoded.matchAll(new RegExp(re.source, re.flags.includes(\"g\") ? re.flags : `${re.flags}g`))) {\n\t\t\tif (perId >= MAX_FACTS_PER_ID) break;\n\t\t\tconst evidence = match[0];\n\t\t\tconst byteOffset = encoder.encode(decoded.slice(0, match.index ?? 0)).length;\n\t\t\tfacts.push({\n\t\t\t\tid,\n\t\t\t\tevidence,\n\t\t\t\tbyteOffset,\n\t\t\t\tbyteLength: encoder.encode(evidence).length,\n\t\t\t});\n\t\t\tperId += 1;\n\t\t}\n\t}\n\treturn facts;\n}\n\n/**\n * Collapse repeated identical atoms into one line that keeps the occurrence\n * count. Without this the evidence view of a log that repeats one failure is\n * larger than the raw text it was supposed to shrink, and the selector\n * correctly but uselessly falls back to full text.\n */\nfunction dedupeFacts(facts: readonly FactAtom[]): readonly { readonly fact: FactAtom; readonly count: number }[] {\n\tconst byKey = new Map<string, { fact: FactAtom; count: number }>();\n\tfor (const fact of facts) {\n\t\tconst key = `${fact.id}\\u0000${fact.evidence}`;\n\t\tconst existing = byKey.get(key);\n\t\tif (existing === undefined) byKey.set(key, { fact, count: 1 });\n\t\telse existing.count += 1;\n\t}\n\treturn [...byKey.values()];\n}\n\nfunction viewText(raw: Uint8Array, facts: readonly FactAtom[], kind: ObservationViewKind): string {\n\tconst decoded = new TextDecoder(\"utf-8\", { fatal: false }).decode(raw);\n\tswitch (kind) {\n\t\tcase \"full\":\n\t\t\treturn decoded;\n\t\tcase \"excerpt\": {\n\t\t\tif (decoded.length <= MAX_VIEW_TEXT) return decoded;\n\t\t\tconst head = decoded.slice(0, Math.floor(MAX_VIEW_TEXT / 2));\n\t\t\tconst tail = decoded.slice(-Math.floor(MAX_VIEW_TEXT / 2));\n\t\t\treturn `${head}\\n…[omitted ${decoded.length - MAX_VIEW_TEXT} chars]…\\n${tail}`;\n\t\t}\n\t\tcase \"evidence\":\n\t\t\treturn facts.length === 0\n\t\t\t\t? \"\"\n\t\t\t\t: dedupeFacts(facts)\n\t\t\t\t\t\t.map(({ fact, count }) =>\n\t\t\t\t\t\t\tcount === 1\n\t\t\t\t\t\t\t\t? `${fact.id} @${fact.byteOffset}: ${fact.evidence}`\n\t\t\t\t\t\t\t\t: `${fact.id} @${fact.byteOffset} \\u00d7${count}: ${fact.evidence}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join(\"\\n\")\n\t\t\t\t\t\t.slice(0, MAX_VIEW_TEXT);\n\t\tcase \"pointer\":\n\t\t\treturn `[observation ${raw.length} bytes; digest-bound; read via handle]`;\n\t}\n}\n\nfunction coverageFor(\n\tcovered: readonly string[],\n\trequired: readonly string[],\n): { status: ObservationCoverageStatus; missing: readonly string[] } {\n\tif (required.length === 0) return { status: \"unknown\", missing: [] };\n\tconst coveredSet = new Set(covered);\n\tconst missing = required.filter((id) => !coveredSet.has(id));\n\treturn { status: missing.length === 0 ? \"complete\" : \"partial\", missing };\n}\n\n/**\n * Build the candidate views for one observation. Every view records which\n * fact atoms it preserves and which required atoms it would drop, so the\n * selector can gate on coverage rather than token density alone.\n */\nexport function makeViews(\n\tobservation: RawObservation,\n\trequiredFactIds: readonly string[] = [],\n): readonly ObservationView[] {\n\ttext(observation.observationId, \"observationId\", 128);\n\tfor (const id of requiredFactIds) text(id, \"requiredFactId\", 256);\n\tconst facts = extractFacts(observation.bytes);\n\tconst coveredByKind: Record<ObservationViewKind, readonly string[]> = {\n\t\tfull: facts.map((f) => f.id),\n\t\texcerpt: facts.map((f) => f.id), // excerpt keeps head+tail; see note\n\t\tevidence: facts.map((f) => f.id),\n\t\tpointer: [],\n\t};\n\tconst kinds: readonly ObservationViewKind[] = [\"full\", \"excerpt\", \"evidence\", \"pointer\"];\n\treturn kinds.map((kind) => {\n\t\tconst body = viewText(observation.bytes, facts, kind);\n\t\tconst covered = coveredByKind[kind].filter(\n\t\t\t(id) => body.includes(id) || kind !== \"evidence\" || facts.some((f) => f.id === id),\n\t\t);\n\t\tconst { status, missing } = coverageFor(covered, requiredFactIds);\n\t\treturn Object.freeze({\n\t\t\tviewKind: kind,\n\t\t\tobservationId: observation.observationId,\n\t\t\tparentDigest: observation.rawDigest,\n\t\t\ttransformationDigest: viewDigestOf({\n\t\t\t\tobservationId: observation.observationId,\n\t\t\t\tviewKind: kind,\n\t\t\t\tparams: `req:${[...requiredFactIds].sort().join(\",\")}`,\n\t\t\t}),\n\t\t\ttext: body,\n\t\t\testimatedTokens: estimateViewTokens(body),\n\t\t\tcoveredFactIds: covered,\n\t\t\tmissingRequiredFactIds: missing,\n\t\t\tcoverageStatus: status,\n\t\t\ttaskVerdict: \"not-assessed\" as const,\n\t\t});\n\t});\n}\n\n/**\n * Coverage-gated deterministic selection: the smallest view that preserves\n * every required fact within budget. Returns null (infeasible) rather than\n * silently dropping a required atom — the caller must widen the budget or keep\n * the raw observation.\n */\nexport function chooseView(\n\tviews: readonly ObservationView[],\n\tbudgetTokens: number,\n\trequiredFactIds: readonly string[] = [],\n): ObservationView | null {\n\tinteger(budgetTokens, \"budgetTokens\");\n\tensure(views.length > 0, \"views must be nonempty\");\n\tfor (const id of requiredFactIds) text(id, \"requiredFactId\", 256);\n\tconst admissible = views.filter(\n\t\t(v) => v.estimatedTokens <= budgetTokens && requiredFactIds.every((id) => v.coveredFactIds.includes(id)),\n\t);\n\tif (admissible.length === 0) return null;\n\t// Smallest tokens wins; break ties toward the higher-fidelity kind.\n\tconst rank: Record<ObservationViewKind, number> = { full: 0, excerpt: 1, evidence: 2, pointer: 3 };\n\tlet best: ObservationView | undefined;\n\tfor (const view of admissible) {\n\t\tif (\n\t\t\tbest === undefined ||\n\t\t\tview.estimatedTokens < best.estimatedTokens ||\n\t\t\t(view.estimatedTokens === best.estimatedTokens && rank[view.viewKind] < rank[best.viewKind])\n\t\t) {\n\t\t\tbest = view;\n\t\t}\n\t}\n\treturn best ?? null;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"exif-orientation.d.ts","sourceRoot":"","sources":["../../src/utils/exif-orientation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,MAAM,GAAG,cAAc,4BAA4B,CAAC,CAAC;AAgJ1D,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,UAAU,GACvB,eAAe,CAgCjB","sourcesContent":["import type { PhotonImageType } from \"./photon.ts\";\n\ntype Photon = typeof import(\"@silvia-odwyer/photon-node\");\n\nfunction readOrientationFromTiff(bytes: Uint8Array, tiffStart: number): number {\n\tif (tiffStart + 8 > bytes.length) return 1;\n\n\tconst byteOrder = (bytes[tiffStart] << 8) | bytes[tiffStart + 1];\n\tconst le = byteOrder === 0x4949;\n\n\tconst read16 = (pos: number): number => {\n\t\tif (le) return bytes[pos] | (bytes[pos + 1] << 8);\n\t\treturn (bytes[pos] << 8) | bytes[pos + 1];\n\t};\n\n\tconst read32 = (pos: number): number => {\n\t\tif (le) return bytes[pos] | (bytes[pos + 1] << 8) | (bytes[pos + 2] << 16) | (bytes[pos + 3] << 24);\n\t\treturn ((bytes[pos] << 24) | (bytes[pos + 1] << 16) | (bytes[pos + 2] << 8) | bytes[pos + 3]) >>> 0;\n\t};\n\n\tconst ifdOffset = read32(tiffStart + 4);\n\tconst ifdStart = tiffStart + ifdOffset;\n\tif (ifdStart + 2 > bytes.length) return 1;\n\n\tconst entryCount = read16(ifdStart);\n\tfor (let i = 0; i < entryCount; i++) {\n\t\tconst entryPos = ifdStart + 2 + i * 12;\n\t\tif (entryPos + 12 > bytes.length) return 1;\n\n\t\tif (read16(entryPos) === 0x0112) {\n\t\t\tconst value = read16(entryPos + 8);\n\t\t\treturn value >= 1 && value <= 8 ? value : 1;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nfunction findJpegTiffOffset(bytes: Uint8Array): number {\n\tlet offset = 2;\n\twhile (offset < bytes.length - 1) {\n\t\tif (bytes[offset] !== 0xff) return -1;\n\t\tconst marker = bytes[offset + 1];\n\t\tif (marker === 0xff) {\n\t\t\toffset++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (marker === 0xe1) {\n\t\t\tif (offset + 4 >= bytes.length) return -1;\n\t\t\tconst segmentStart = offset + 4;\n\t\t\tif (segmentStart + 6 > bytes.length) return -1;\n\t\t\tif (!hasExifHeader(bytes, segmentStart)) return -1;\n\t\t\treturn segmentStart + 6;\n\t\t}\n\n\t\tif (offset + 4 > bytes.length) return -1;\n\t\tconst length = (bytes[offset + 2] << 8) | bytes[offset + 3];\n\t\toffset += 2 + length;\n\t}\n\n\treturn -1;\n}\n\nfunction findWebpTiffOffset(bytes: Uint8Array): number {\n\tlet offset = 12;\n\twhile (offset + 8 <= bytes.length) {\n\t\tconst chunkId = String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);\n\t\tconst chunkSize =\n\t\t\tbytes[offset + 4] | (bytes[offset + 5] << 8) | (bytes[offset + 6] << 16) | (bytes[offset + 7] << 24);\n\t\tconst dataStart = offset + 8;\n\n\t\tif (chunkId === \"EXIF\") {\n\t\t\tif (dataStart + chunkSize > bytes.length) return -1;\n\t\t\t// Some WebP files have \"Exif\\0\\0\" prefix before the TIFF header\n\t\t\tconst tiffStart = chunkSize >= 6 && hasExifHeader(bytes, dataStart) ? dataStart + 6 : dataStart;\n\t\t\treturn tiffStart;\n\t\t}\n\n\t\t// RIFF chunks are padded to even size\n\t\toffset = dataStart + chunkSize + (chunkSize % 2);\n\t}\n\n\treturn -1;\n}\n\nfunction hasExifHeader(bytes: Uint8Array, offset: number): boolean {\n\treturn (\n\t\tbytes[offset] === 0x45 &&\n\t\tbytes[offset + 1] === 0x78 &&\n\t\tbytes[offset + 2] === 0x69 &&\n\t\tbytes[offset + 3] === 0x66 &&\n\t\tbytes[offset + 4] === 0x00 &&\n\t\tbytes[offset + 5] === 0x00\n\t);\n}\n\nfunction getExifOrientation(bytes: Uint8Array): number {\n\tlet tiffOffset = -1;\n\n\t// JPEG: starts with FF D8\n\tif (bytes.length >= 2 && bytes[0] === 0xff && bytes[1] === 0xd8) {\n\t\ttiffOffset = findJpegTiffOffset(bytes);\n\t}\n\t// WebP: starts with RIFF....WEBP\n\telse if (\n\t\tbytes.length >= 12 &&\n\t\tbytes[0] === 0x52 &&\n\t\tbytes[1] === 0x49 &&\n\t\tbytes[2] === 0x46 &&\n\t\tbytes[3] === 0x46 &&\n\t\tbytes[8] === 0x57 &&\n\t\tbytes[9] === 0x45 &&\n\t\tbytes[10] === 0x42 &&\n\t\tbytes[11] === 0x50\n\t) {\n\t\ttiffOffset = findWebpTiffOffset(bytes);\n\t}\n\n\tif (tiffOffset === -1) return 1;\n\treturn readOrientationFromTiff(bytes, tiffOffset);\n}\n\ntype DstIndexFn = (x: number, y: number, w: number, h: number) => number;\n\nfunction rotate90(photon: Photon, image: PhotonImageType, dstIndex: DstIndexFn): PhotonImageType {\n\tconst w = image.get_width();\n\tconst h = image.get_height();\n\tconst src = image.get_raw_pixels();\n\tconst dst = new Uint8Array(src.length);\n\n\tfor (let y = 0; y < h; y++) {\n\t\tfor (let x = 0; x < w; x++) {\n\t\t\tconst srcIdx = (y * w + x) * 4;\n\t\t\tconst dstIdx = dstIndex(x, y, w, h) * 4;\n\t\t\tdst[dstIdx] = src[srcIdx];\n\t\t\tdst[dstIdx + 1] = src[srcIdx + 1];\n\t\t\tdst[dstIdx + 2] = src[srcIdx + 2];\n\t\t\tdst[dstIdx + 3] = src[srcIdx + 3];\n\t\t}\n\t}\n\n\treturn new photon.PhotonImage(dst, h, w);\n}\n\n// Flip orientations mutate in-place. Rotations return a new image (caller must free the old one if different).\nexport function applyExifOrientation(\n\tphoton: Photon,\n\timage: PhotonImageType,\n\toriginalBytes: Uint8Array,\n): PhotonImageType {\n\tconst orientation = getExifOrientation(originalBytes);\n\tif (orientation === 1) return image;\n\n\tswitch (orientation) {\n\t\tcase 2:\n\t\t\tphoton.fliph(image);\n\t\t\treturn image;\n\t\tcase 3:\n\t\t\tphoton.fliph(image);\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 4:\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 5: {\n\t\t\tconst rotated = rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 6:\n\t\t\treturn rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));\n\t\tcase 7: {\n\t\t\tconst rotated = rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 8:\n\t\t\treturn rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);\n\t\tdefault:\n\t\t\treturn image;\n\t}\n}\n"]}
1
+ {"version":3,"file":"exif-orientation.d.ts","sourceRoot":"","sources":["../../src/utils/exif-orientation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,KAAK,MAAM,GAAG,cAAc,4BAA4B,CAAC,CAAC;AA+I1D,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,UAAU,GACvB,eAAe,CAgCjB","sourcesContent":["import type { PhotonImageType } from \"./photon.ts\";\n\ntype Photon = typeof import(\"@silvia-odwyer/photon-node\");\n\nfunction readOrientationFromTiff(bytes: Uint8Array, tiffStart: number): number {\n\tif (tiffStart + 8 > bytes.length) return 1;\n\n\tconst byteOrder = (bytes[tiffStart] << 8) | bytes[tiffStart + 1];\n\tconst le = byteOrder === 0x4949;\n\n\tconst read16 = (pos: number): number => {\n\t\tif (le) return bytes[pos] | (bytes[pos + 1] << 8);\n\t\treturn (bytes[pos] << 8) | bytes[pos + 1];\n\t};\n\n\tconst read32 = (pos: number): number => {\n\t\tif (le) return bytes[pos] | (bytes[pos + 1] << 8) | (bytes[pos + 2] << 16) | (bytes[pos + 3] << 24);\n\t\treturn ((bytes[pos] << 24) | (bytes[pos + 1] << 16) | (bytes[pos + 2] << 8) | bytes[pos + 3]) >>> 0;\n\t};\n\n\tconst ifdOffset = read32(tiffStart + 4);\n\tconst ifdStart = tiffStart + ifdOffset;\n\tif (ifdStart + 2 > bytes.length) return 1;\n\n\tconst entryCount = read16(ifdStart);\n\tfor (let i = 0; i < entryCount; i++) {\n\t\tconst entryPos = ifdStart + 2 + i * 12;\n\t\tif (entryPos + 12 > bytes.length) return 1;\n\n\t\tif (read16(entryPos) === 0x0112) {\n\t\t\tconst value = read16(entryPos + 8);\n\t\t\treturn value >= 1 && value <= 8 ? value : 1;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nfunction findJpegTiffOffset(bytes: Uint8Array): number {\n\tlet offset = 2;\n\twhile (offset < bytes.length - 1) {\n\t\tif (bytes[offset] !== 0xff) return -1;\n\t\tconst marker = bytes[offset + 1];\n\t\tif (marker === 0xff) {\n\t\t\toffset++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (marker === 0xe1) {\n\t\t\tif (offset + 4 >= bytes.length) return -1;\n\t\t\tconst segmentStart = offset + 4;\n\t\t\tif (segmentStart + 6 > bytes.length) return -1;\n\t\t\tif (hasExifHeader(bytes, segmentStart)) return segmentStart + 6;\n\t\t}\n\n\t\tif (offset + 4 > bytes.length) return -1;\n\t\tconst length = (bytes[offset + 2] << 8) | bytes[offset + 3];\n\t\toffset += 2 + length;\n\t}\n\n\treturn -1;\n}\n\nfunction findWebpTiffOffset(bytes: Uint8Array): number {\n\tlet offset = 12;\n\twhile (offset + 8 <= bytes.length) {\n\t\tconst chunkId = String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);\n\t\tconst chunkSize =\n\t\t\tbytes[offset + 4] | (bytes[offset + 5] << 8) | (bytes[offset + 6] << 16) | (bytes[offset + 7] << 24);\n\t\tconst dataStart = offset + 8;\n\n\t\tif (chunkId === \"EXIF\") {\n\t\t\tif (dataStart + chunkSize > bytes.length) return -1;\n\t\t\t// Some WebP files have \"Exif\\0\\0\" prefix before the TIFF header\n\t\t\tconst tiffStart = chunkSize >= 6 && hasExifHeader(bytes, dataStart) ? dataStart + 6 : dataStart;\n\t\t\treturn tiffStart;\n\t\t}\n\n\t\t// RIFF chunks are padded to even size\n\t\toffset = dataStart + chunkSize + (chunkSize % 2);\n\t}\n\n\treturn -1;\n}\n\nfunction hasExifHeader(bytes: Uint8Array, offset: number): boolean {\n\treturn (\n\t\tbytes[offset] === 0x45 &&\n\t\tbytes[offset + 1] === 0x78 &&\n\t\tbytes[offset + 2] === 0x69 &&\n\t\tbytes[offset + 3] === 0x66 &&\n\t\tbytes[offset + 4] === 0x00 &&\n\t\tbytes[offset + 5] === 0x00\n\t);\n}\n\nfunction getExifOrientation(bytes: Uint8Array): number {\n\tlet tiffOffset = -1;\n\n\t// JPEG: starts with FF D8\n\tif (bytes.length >= 2 && bytes[0] === 0xff && bytes[1] === 0xd8) {\n\t\ttiffOffset = findJpegTiffOffset(bytes);\n\t}\n\t// WebP: starts with RIFF....WEBP\n\telse if (\n\t\tbytes.length >= 12 &&\n\t\tbytes[0] === 0x52 &&\n\t\tbytes[1] === 0x49 &&\n\t\tbytes[2] === 0x46 &&\n\t\tbytes[3] === 0x46 &&\n\t\tbytes[8] === 0x57 &&\n\t\tbytes[9] === 0x45 &&\n\t\tbytes[10] === 0x42 &&\n\t\tbytes[11] === 0x50\n\t) {\n\t\ttiffOffset = findWebpTiffOffset(bytes);\n\t}\n\n\tif (tiffOffset === -1) return 1;\n\treturn readOrientationFromTiff(bytes, tiffOffset);\n}\n\ntype DstIndexFn = (x: number, y: number, w: number, h: number) => number;\n\nfunction rotate90(photon: Photon, image: PhotonImageType, dstIndex: DstIndexFn): PhotonImageType {\n\tconst w = image.get_width();\n\tconst h = image.get_height();\n\tconst src = image.get_raw_pixels();\n\tconst dst = new Uint8Array(src.length);\n\n\tfor (let y = 0; y < h; y++) {\n\t\tfor (let x = 0; x < w; x++) {\n\t\t\tconst srcIdx = (y * w + x) * 4;\n\t\t\tconst dstIdx = dstIndex(x, y, w, h) * 4;\n\t\t\tdst[dstIdx] = src[srcIdx];\n\t\t\tdst[dstIdx + 1] = src[srcIdx + 1];\n\t\t\tdst[dstIdx + 2] = src[srcIdx + 2];\n\t\t\tdst[dstIdx + 3] = src[srcIdx + 3];\n\t\t}\n\t}\n\n\treturn new photon.PhotonImage(dst, h, w);\n}\n\n// Flip orientations mutate in-place. Rotations return a new image (caller must free the old one if different).\nexport function applyExifOrientation(\n\tphoton: Photon,\n\timage: PhotonImageType,\n\toriginalBytes: Uint8Array,\n): PhotonImageType {\n\tconst orientation = getExifOrientation(originalBytes);\n\tif (orientation === 1) return image;\n\n\tswitch (orientation) {\n\t\tcase 2:\n\t\t\tphoton.fliph(image);\n\t\t\treturn image;\n\t\tcase 3:\n\t\t\tphoton.fliph(image);\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 4:\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 5: {\n\t\t\tconst rotated = rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 6:\n\t\t\treturn rotate90(photon, image, (x, y, _w, h) => x * h + (h - 1 - y));\n\t\tcase 7: {\n\t\t\tconst rotated = rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 8:\n\t\t\treturn rotate90(photon, image, (x, y, w, h) => (w - 1 - x) * h + y);\n\t\tdefault:\n\t\t\treturn image;\n\t}\n}\n"]}
@@ -45,9 +45,8 @@ function findJpegTiffOffset(bytes) {
45
45
  const segmentStart = offset + 4;
46
46
  if (segmentStart + 6 > bytes.length)
47
47
  return -1;
48
- if (!hasExifHeader(bytes, segmentStart))
49
- return -1;
50
- return segmentStart + 6;
48
+ if (hasExifHeader(bytes, segmentStart))
49
+ return segmentStart + 6;
51
50
  }
52
51
  if (offset + 4 > bytes.length)
53
52
  return -1;