openplanr 2.2641.1 → 3.0.0

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 (315) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +1 -1
  3. package/dist/cli/commands/artifact.js +1 -1
  4. package/dist/cli/commands/artifact.js.map +1 -1
  5. package/dist/cli/commands/doctor.d.ts.map +1 -1
  6. package/dist/cli/commands/doctor.js +19 -4
  7. package/dist/cli/commands/doctor.js.map +1 -1
  8. package/dist/cli/commands/github.d.ts.map +1 -1
  9. package/dist/cli/commands/github.js +7 -10
  10. package/dist/cli/commands/github.js.map +1 -1
  11. package/dist/cli/commands/linear.js +1 -1
  12. package/dist/cli/commands/linear.js.map +1 -1
  13. package/dist/cli/commands/report.d.ts.map +1 -1
  14. package/dist/cli/commands/report.js +28 -32
  15. package/dist/cli/commands/report.js.map +1 -1
  16. package/dist/cli/commands/upgrade.d.ts.map +1 -1
  17. package/dist/cli/commands/upgrade.js +12 -10
  18. package/dist/cli/commands/upgrade.js.map +1 -1
  19. package/dist/cli/error-boundary.d.ts.map +1 -1
  20. package/dist/cli/error-boundary.js +2 -1
  21. package/dist/cli/error-boundary.js.map +1 -1
  22. package/dist/dashboard/.vite/manifest.json +1 -1
  23. package/dist/dashboard/assets/{index-DGyC7RDz.js → index-Csby7_ur.js} +2 -2
  24. package/dist/dashboard/assets/{index-DGyC7RDz.js.map → index-Csby7_ur.js.map} +1 -1
  25. package/dist/dashboard/dashboard-manifest.json +8 -8
  26. package/dist/dashboard/index.html +1 -1
  27. package/dist/models/schema.d.ts +0 -4
  28. package/dist/models/schema.d.ts.map +1 -1
  29. package/dist/models/schema.js +0 -2
  30. package/dist/models/schema.js.map +1 -1
  31. package/dist/models/types.d.ts +1 -4
  32. package/dist/models/types.d.ts.map +1 -1
  33. package/dist/services/credentials-service.js +1 -1
  34. package/dist/services/credentials-service.js.map +1 -1
  35. package/dist/services/distribution-service.d.ts +1 -4
  36. package/dist/services/distribution-service.d.ts.map +1 -1
  37. package/dist/services/distribution-service.js +1 -60
  38. package/dist/services/distribution-service.js.map +1 -1
  39. package/dist/services/github-service.d.ts +7 -4
  40. package/dist/services/github-service.d.ts.map +1 -1
  41. package/dist/services/github-service.js +16 -15
  42. package/dist/services/github-service.js.map +1 -1
  43. package/dist/services/linear-push-service.d.ts +3 -4
  44. package/dist/services/linear-push-service.d.ts.map +1 -1
  45. package/dist/services/linear-push-service.js +5 -6
  46. package/dist/services/linear-push-service.js.map +1 -1
  47. package/dist/services/runtime-change-summary.d.ts +4 -0
  48. package/dist/services/runtime-change-summary.d.ts.map +1 -1
  49. package/dist/services/runtime-change-summary.js +7 -0
  50. package/dist/services/runtime-change-summary.js.map +1 -1
  51. package/dist/services/runtime-manager/doctor.js +1 -1
  52. package/dist/services/runtime-manager/doctor.js.map +1 -1
  53. package/dist/services/runtime-manager/runtime-package.d.ts +15 -0
  54. package/dist/services/runtime-manager/runtime-package.d.ts.map +1 -1
  55. package/dist/services/runtime-manager/runtime-package.js +50 -10
  56. package/dist/services/runtime-manager/runtime-package.js.map +1 -1
  57. package/dist/services/runtime-manager-service.d.ts +11 -0
  58. package/dist/services/runtime-manager-service.d.ts.map +1 -1
  59. package/dist/services/runtime-manager-service.js +88 -16
  60. package/dist/services/runtime-manager-service.js.map +1 -1
  61. package/dist/services/spec-service.d.ts.map +1 -1
  62. package/dist/services/spec-service.js +34 -69
  63. package/dist/services/spec-service.js.map +1 -1
  64. package/dist/services/upgrade-report.d.ts +2 -2
  65. package/dist/services/upgrade-report.d.ts.map +1 -1
  66. package/dist/services/upgrade-report.js +11 -2
  67. package/dist/services/upgrade-report.js.map +1 -1
  68. package/dist/services/upgrade-service.d.ts +8 -2
  69. package/dist/services/upgrade-service.d.ts.map +1 -1
  70. package/dist/services/upgrade-service.js +44 -12
  71. package/dist/services/upgrade-service.js.map +1 -1
  72. package/dist/templates/rules/shared/capability-map.md.hbs +1 -1
  73. package/docs/CLI.md +14 -15
  74. package/docs/TROUBLESHOOTING.md +9 -6
  75. package/install.ps1 +3 -3
  76. package/install.sh +3 -3
  77. package/lib/host-packages/adapter-registry.json +5 -5
  78. package/lib/host-packages/capability-map.json +8 -8
  79. package/lib/host-packages/claude/.claude-plugin/marketplace.json +2 -2
  80. package/lib/host-packages/claude/openplanr/.claude-plugin/plugin.json +2 -2
  81. package/lib/host-packages/claude/openplanr/.openplanr-content.json +91 -75
  82. package/lib/host-packages/claude/openplanr/README.md +8 -12
  83. package/lib/host-packages/claude/openplanr/agents/planr-backend.md +49 -55
  84. package/lib/host-packages/claude/openplanr/agents/planr-database.md +56 -92
  85. package/lib/host-packages/claude/openplanr/agents/planr-designer.md +35 -51
  86. package/lib/host-packages/claude/openplanr/agents/planr-devops.md +37 -51
  87. package/lib/host-packages/claude/openplanr/agents/planr-documentation.md +29 -52
  88. package/lib/host-packages/claude/openplanr/agents/planr-entity-scaffold.md +24 -23
  89. package/lib/host-packages/claude/openplanr/agents/planr-frontend.md +46 -58
  90. package/lib/host-packages/claude/openplanr/agents/planr-qa.md +4 -2
  91. package/lib/host-packages/claude/openplanr/agents/planr-specification.md +54 -51
  92. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/default/backend.md +21 -65
  93. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/default/designer.md +5 -6
  94. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/default/devops.md +2 -3
  95. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/default/doc-gen.md +3 -4
  96. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/default/frontend.md +19 -64
  97. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/default/specification.md +10 -12
  98. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/spec-driven/backend.md +19 -62
  99. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/spec-driven/designer.md +3 -4
  100. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/spec-driven/devops.md +2 -3
  101. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/spec-driven/doc-gen.md +2 -3
  102. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/spec-driven/frontend.md +16 -59
  103. package/lib/host-packages/claude/openplanr/references/agents/shared/modes/spec-driven/specification.md +9 -11
  104. package/lib/host-packages/claude/openplanr/references/pipeline/stacks/database/mongodb.md +4 -4
  105. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-review.mjs +24 -9
  106. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-share.mjs +3 -3
  107. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +1 -1
  108. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/package.json +4 -4
  109. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +14 -4
  110. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +1 -1
  111. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +14 -5
  112. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-design-review-experience.js +28 -0
  113. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-studio-shell.js +105 -27
  114. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +6 -6
  115. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +2 -2
  116. package/lib/host-packages/claude/openplanr/runtime/plan/scripts/design.mjs +1 -1
  117. package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +2 -2
  118. package/lib/host-packages/claude/openplanr/skills/artifact/SKILL.md +2 -2
  119. package/lib/host-packages/claude/openplanr/skills/artifact/references/diagram-sharing.md +1 -1
  120. package/lib/host-packages/claude/openplanr/skills/ceo-review/SKILL.md +0 -1
  121. package/lib/host-packages/claude/openplanr/skills/ceo-review/references/operate-advisor-contract.md +25 -4
  122. package/lib/host-packages/claude/openplanr/skills/chair-review/SKILL.md +6 -2
  123. package/lib/host-packages/claude/openplanr/skills/challenger-review/SKILL.md +6 -2
  124. package/lib/host-packages/claude/openplanr/skills/cmo-review/SKILL.md +0 -1
  125. package/lib/host-packages/claude/openplanr/skills/cmo-review/references/operate-advisor-contract.md +25 -4
  126. package/lib/host-packages/claude/openplanr/skills/coo-review/SKILL.md +0 -1
  127. package/lib/host-packages/claude/openplanr/skills/coo-review/references/operate-advisor-contract.md +25 -4
  128. package/lib/host-packages/claude/openplanr/skills/cpo-review/SKILL.md +0 -1
  129. package/lib/host-packages/claude/openplanr/skills/cpo-review/references/operate-advisor-contract.md +25 -4
  130. package/lib/host-packages/claude/openplanr/skills/cto-review/SKILL.md +0 -1
  131. package/lib/host-packages/claude/openplanr/skills/cto-review/references/operate-advisor-contract.md +25 -4
  132. package/lib/host-packages/claude/openplanr/skills/delegate/SKILL.md +8 -1
  133. package/lib/host-packages/claude/openplanr/skills/delegate/references/capsule-contract.md +17 -2
  134. package/lib/host-packages/claude/openplanr/skills/delegate/references/operator-guide.md +7 -5
  135. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/adapters/claude.mjs +11 -4
  136. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/adapters/native.mjs +1 -1
  137. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/backend-diagnostics.mjs +14 -48
  138. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/context.mjs +84 -22
  139. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/credentials.mjs +167 -0
  140. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/presentation.mjs +3 -2
  141. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/run-contract.mjs +5 -7
  142. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/run-preparation.mjs +5 -3
  143. package/lib/host-packages/claude/openplanr/skills/delegate/scripts/run-record.mjs +2 -3
  144. package/lib/host-packages/claude/openplanr/skills/design/references/utilities.md +1 -1
  145. package/lib/host-packages/claude/openplanr/skills/design-loop/references/utilities.md +1 -1
  146. package/lib/host-packages/claude/openplanr/skills/design-review/references/utilities.md +1 -1
  147. package/lib/host-packages/claude/openplanr/skills/diagram/references/diagram-sharing.md +1 -1
  148. package/lib/host-packages/claude/openplanr/skills/doctor/SKILL.md +4 -1
  149. package/lib/host-packages/claude/openplanr/skills/doctor/references/upgrade.md +8 -0
  150. package/lib/host-packages/claude/openplanr/skills/operate/SKILL.md +5 -3
  151. package/lib/host-packages/claude/openplanr/skills/plan/SKILL.md +10 -4
  152. package/lib/host-packages/claude/openplanr/skills/sprint/SKILL.md +5 -4
  153. package/lib/host-packages/claude/openplanr/skills/sprint/references/tracker-connections.md +70 -0
  154. package/lib/host-packages/claude/openplanr/skills/status/SKILL.md +4 -3
  155. package/lib/host-packages/claude/openplanr/skills/status/references/tracker-connections.md +70 -0
  156. package/lib/host-packages/claude/openplanr/skills/sync/SKILL.md +11 -20
  157. package/lib/host-packages/claude/openplanr/skills/sync/references/tracker-connections.md +70 -0
  158. package/lib/host-packages/claude/openplanr/skills/sync/scripts/sync.mjs +2 -2
  159. package/lib/host-packages/cursor/openplanr/.openplanr-content.json +63 -47
  160. package/lib/host-packages/cursor/openplanr/manifest.json +1 -1
  161. package/lib/host-packages/cursor/openplanr/rules/planr-artifact/references/diagram-sharing.md +1 -1
  162. package/lib/host-packages/cursor/openplanr/rules/planr-artifact.mdc +2 -2
  163. package/lib/host-packages/cursor/openplanr/rules/planr-ceo-review/references/operate-advisor-contract.md +25 -4
  164. package/lib/host-packages/cursor/openplanr/rules/planr-chair-review.mdc +6 -1
  165. package/lib/host-packages/cursor/openplanr/rules/planr-challenger-review.mdc +6 -1
  166. package/lib/host-packages/cursor/openplanr/rules/planr-cmo-review/references/operate-advisor-contract.md +25 -4
  167. package/lib/host-packages/cursor/openplanr/rules/planr-coo-review/references/operate-advisor-contract.md +25 -4
  168. package/lib/host-packages/cursor/openplanr/rules/planr-cpo-review/references/operate-advisor-contract.md +25 -4
  169. package/lib/host-packages/cursor/openplanr/rules/planr-cto-review/references/operate-advisor-contract.md +25 -4
  170. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/references/capsule-contract.md +17 -2
  171. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/references/operator-guide.md +7 -5
  172. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/adapters/claude.mjs +11 -4
  173. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/adapters/native.mjs +1 -1
  174. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/backend-diagnostics.mjs +14 -48
  175. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/context.mjs +84 -22
  176. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/credentials.mjs +167 -0
  177. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/presentation.mjs +3 -2
  178. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/run-contract.mjs +5 -7
  179. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/run-preparation.mjs +5 -3
  180. package/lib/host-packages/cursor/openplanr/rules/planr-delegate/scripts/run-record.mjs +2 -3
  181. package/lib/host-packages/cursor/openplanr/rules/planr-delegate.mdc +8 -1
  182. package/lib/host-packages/cursor/openplanr/rules/planr-design/references/utilities.md +1 -1
  183. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/utilities.md +1 -1
  184. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/utilities.md +1 -1
  185. package/lib/host-packages/cursor/openplanr/rules/planr-diagram/references/diagram-sharing.md +1 -1
  186. package/lib/host-packages/cursor/openplanr/rules/planr-doctor/references/upgrade.md +8 -0
  187. package/lib/host-packages/cursor/openplanr/rules/planr-doctor.mdc +4 -1
  188. package/lib/host-packages/cursor/openplanr/rules/planr-operate.mdc +5 -3
  189. package/lib/host-packages/cursor/openplanr/rules/planr-plan.mdc +10 -4
  190. package/lib/host-packages/cursor/openplanr/rules/planr-sprint/references/tracker-connections.md +70 -0
  191. package/lib/host-packages/cursor/openplanr/rules/planr-sprint.mdc +5 -4
  192. package/lib/host-packages/cursor/openplanr/rules/planr-status/references/tracker-connections.md +70 -0
  193. package/lib/host-packages/cursor/openplanr/rules/planr-status.mdc +4 -3
  194. package/lib/host-packages/cursor/openplanr/rules/planr-sync/references/tracker-connections.md +70 -0
  195. package/lib/host-packages/cursor/openplanr/rules/planr-sync/scripts/sync.mjs +2 -2
  196. package/lib/host-packages/cursor/openplanr/rules/planr-sync.mdc +11 -20
  197. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-review.mjs +24 -9
  198. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-share.mjs +3 -3
  199. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +1 -1
  200. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/package.json +4 -4
  201. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +14 -4
  202. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +1 -1
  203. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +14 -5
  204. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-design-review-experience.js +28 -0
  205. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-studio-shell.js +105 -27
  206. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +6 -6
  207. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +2 -2
  208. package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/design.mjs +1 -1
  209. package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +2 -2
  210. package/lib/host-packages/openai/.claude-plugin/marketplace.json +2 -2
  211. package/lib/host-packages/openai/openplanr/.codex-plugin/plugin.json +1 -1
  212. package/lib/host-packages/openai/openplanr/.openplanr-content.json +72 -56
  213. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-review.mjs +24 -9
  214. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-share.mjs +3 -3
  215. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +1 -1
  216. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/package.json +4 -4
  217. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +14 -4
  218. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +1 -1
  219. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +14 -5
  220. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-design-review-experience.js +28 -0
  221. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-studio-shell.js +105 -27
  222. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +6 -6
  223. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +2 -2
  224. package/lib/host-packages/openai/openplanr/runtime/plan/scripts/design.mjs +1 -1
  225. package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +2 -2
  226. package/lib/host-packages/openai/openplanr/skills/artifact/SKILL.md +2 -2
  227. package/lib/host-packages/openai/openplanr/skills/artifact/references/diagram-sharing.md +1 -1
  228. package/lib/host-packages/openai/openplanr/skills/ceo-review/SKILL.md +0 -1
  229. package/lib/host-packages/openai/openplanr/skills/ceo-review/references/operate-advisor-contract.md +25 -4
  230. package/lib/host-packages/openai/openplanr/skills/chair-review/SKILL.md +6 -2
  231. package/lib/host-packages/openai/openplanr/skills/challenger-review/SKILL.md +6 -2
  232. package/lib/host-packages/openai/openplanr/skills/cmo-review/SKILL.md +0 -1
  233. package/lib/host-packages/openai/openplanr/skills/cmo-review/references/operate-advisor-contract.md +25 -4
  234. package/lib/host-packages/openai/openplanr/skills/coo-review/SKILL.md +0 -1
  235. package/lib/host-packages/openai/openplanr/skills/coo-review/references/operate-advisor-contract.md +25 -4
  236. package/lib/host-packages/openai/openplanr/skills/cpo-review/SKILL.md +0 -1
  237. package/lib/host-packages/openai/openplanr/skills/cpo-review/references/operate-advisor-contract.md +25 -4
  238. package/lib/host-packages/openai/openplanr/skills/cto-review/SKILL.md +0 -1
  239. package/lib/host-packages/openai/openplanr/skills/cto-review/references/operate-advisor-contract.md +25 -4
  240. package/lib/host-packages/openai/openplanr/skills/delegate/SKILL.md +8 -1
  241. package/lib/host-packages/openai/openplanr/skills/delegate/openplanr.skill.json +11 -0
  242. package/lib/host-packages/openai/openplanr/skills/delegate/references/capsule-contract.md +17 -2
  243. package/lib/host-packages/openai/openplanr/skills/delegate/references/operator-guide.md +7 -5
  244. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/adapters/claude.mjs +11 -4
  245. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/adapters/native.mjs +1 -1
  246. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/backend-diagnostics.mjs +14 -48
  247. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/context.mjs +84 -22
  248. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/credentials.mjs +167 -0
  249. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/presentation.mjs +3 -2
  250. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/run-contract.mjs +5 -7
  251. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/run-preparation.mjs +5 -3
  252. package/lib/host-packages/openai/openplanr/skills/delegate/scripts/run-record.mjs +2 -3
  253. package/lib/host-packages/openai/openplanr/skills/design/references/utilities.md +1 -1
  254. package/lib/host-packages/openai/openplanr/skills/design-loop/references/utilities.md +1 -1
  255. package/lib/host-packages/openai/openplanr/skills/design-review/references/utilities.md +1 -1
  256. package/lib/host-packages/openai/openplanr/skills/diagram/references/diagram-sharing.md +1 -1
  257. package/lib/host-packages/openai/openplanr/skills/doctor/SKILL.md +4 -1
  258. package/lib/host-packages/openai/openplanr/skills/doctor/references/upgrade.md +8 -0
  259. package/lib/host-packages/openai/openplanr/skills/operate/SKILL.md +5 -3
  260. package/lib/host-packages/openai/openplanr/skills/plan/SKILL.md +10 -4
  261. package/lib/host-packages/openai/openplanr/skills/sprint/SKILL.md +5 -4
  262. package/lib/host-packages/openai/openplanr/skills/sprint/openplanr.skill.json +11 -0
  263. package/lib/host-packages/openai/openplanr/skills/sprint/references/tracker-connections.md +70 -0
  264. package/lib/host-packages/openai/openplanr/skills/status/SKILL.md +4 -3
  265. package/lib/host-packages/openai/openplanr/skills/status/openplanr.skill.json +11 -0
  266. package/lib/host-packages/openai/openplanr/skills/status/references/tracker-connections.md +70 -0
  267. package/lib/host-packages/openai/openplanr/skills/sync/SKILL.md +11 -20
  268. package/lib/host-packages/openai/openplanr/skills/sync/openplanr.skill.json +11 -0
  269. package/lib/host-packages/openai/openplanr/skills/sync/references/tracker-connections.md +70 -0
  270. package/lib/host-packages/openai/openplanr/skills/sync/scripts/sync.mjs +2 -2
  271. package/lib/integrations.mjs +2 -2
  272. package/lib/node-runtime.d.mts +1 -1
  273. package/lib/node-runtime.mjs +2 -7
  274. package/package.json +4 -5
  275. package/bin/planr.js +0 -7
  276. package/dist/services/connectors/adapters/business-metrics.d.ts +0 -5
  277. package/dist/services/connectors/adapters/business-metrics.d.ts.map +0 -1
  278. package/dist/services/connectors/adapters/business-metrics.js +0 -41
  279. package/dist/services/connectors/adapters/business-metrics.js.map +0 -1
  280. package/dist/services/connectors/adapters/deployment-health.d.ts +0 -18
  281. package/dist/services/connectors/adapters/deployment-health.d.ts.map +0 -1
  282. package/dist/services/connectors/adapters/deployment-health.js +0 -67
  283. package/dist/services/connectors/adapters/deployment-health.js.map +0 -1
  284. package/dist/services/connectors/adapters/github.d.ts +0 -5
  285. package/dist/services/connectors/adapters/github.d.ts.map +0 -1
  286. package/dist/services/connectors/adapters/github.js +0 -31
  287. package/dist/services/connectors/adapters/github.js.map +0 -1
  288. package/dist/services/connectors/adapters/linear-jira.d.ts +0 -5
  289. package/dist/services/connectors/adapters/linear-jira.d.ts.map +0 -1
  290. package/dist/services/connectors/adapters/linear-jira.js +0 -26
  291. package/dist/services/connectors/adapters/linear-jira.js.map +0 -1
  292. package/dist/services/connectors/adapters/posthog.d.ts +0 -5
  293. package/dist/services/connectors/adapters/posthog.d.ts.map +0 -1
  294. package/dist/services/connectors/adapters/posthog.js +0 -31
  295. package/dist/services/connectors/adapters/posthog.js.map +0 -1
  296. package/dist/services/connectors/adapters/sentry.d.ts +0 -5
  297. package/dist/services/connectors/adapters/sentry.d.ts.map +0 -1
  298. package/dist/services/connectors/adapters/sentry.js +0 -31
  299. package/dist/services/connectors/adapters/sentry.js.map +0 -1
  300. package/dist/services/connectors/checkpoint-custody.d.ts +0 -45
  301. package/dist/services/connectors/checkpoint-custody.d.ts.map +0 -1
  302. package/dist/services/connectors/checkpoint-custody.js +0 -499
  303. package/dist/services/connectors/checkpoint-custody.js.map +0 -1
  304. package/dist/services/connectors/connector-registry.d.ts +0 -144
  305. package/dist/services/connectors/connector-registry.d.ts.map +0 -1
  306. package/dist/services/connectors/connector-registry.js +0 -423
  307. package/dist/services/connectors/connector-registry.js.map +0 -1
  308. package/dist/services/connectors/connector-runtime.d.ts +0 -208
  309. package/dist/services/connectors/connector-runtime.d.ts.map +0 -1
  310. package/dist/services/connectors/connector-runtime.js +0 -1305
  311. package/dist/services/connectors/connector-runtime.js.map +0 -1
  312. package/dist/services/connectors/credential-custody.d.ts +0 -96
  313. package/dist/services/connectors/credential-custody.d.ts.map +0 -1
  314. package/dist/services/connectors/credential-custody.js +0 -390
  315. package/dist/services/connectors/credential-custody.js.map +0 -1
@@ -1,1305 +0,0 @@
1
- /**
2
- * Live evidence connector runtime: checks a bounded request against the frozen provider registry
3
- * and consent, calls the provider through a credential-scoped transport, journals each step in
4
- * checkpoint custody and submits accepted results through the injected Operate bridge.
5
- * Entry points: `createConnectorRuntimeV2` (`providers`, `preview`, `status`, `collect`) and
6
- * `createBoundedFetchConnectorTransportV2`. Adapters, credentials and checkpoints belong to
7
- * `connector-registry.ts`, `credential-custody.ts` and `checkpoint-custody.ts`.
8
- */
9
- import { createHash } from 'node:crypto';
10
- import { lookup } from 'node:dns/promises';
11
- import { isIP } from 'node:net';
12
- import { sha256CanonicalJson } from '../canonical-json.js';
13
- import { ConnectorCheckpointCustodyError } from './checkpoint-custody.js';
14
- import { readConnectorAdapterResultBytesV2 } from './connector-registry.js';
15
- import { ConnectorCredentialCustodyError } from './credential-custody.js';
16
- const HASH = /^sha256:[a-f0-9]{64}$/u;
17
- const ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
18
- const TEXT_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
19
- const VERSION = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)$/u;
20
- const PRIVATE_VALUE = /(?:^|[\s"'`(])\/(?:Users|home|private|var|etc|tmp)\/|(?:^|[\s"'`(])[A-Za-z]:[\\/][^\s]+|-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:Bearer|Basic)\s+[A-Za-z0-9+/=._-]+|\b(?:api[-_ ]?key|access[-_ ]?token|client[-_ ]?secret|credential(?:Value)?|password|passwd|secret|token)\s*[:=]\s*[^\s]+/iu;
21
- const SENSITIVE_KEY = /(?:authorization|proxy-authorization|cookie|set-cookie|password|passwd|secret|token|api[-_]?key|client[-_]?secret|credentialValue|private[-_]?key)/iu;
22
- const RESULT_EFFECTS = Object.freeze([
23
- 'provider-call',
24
- 'operate-assignment-submit',
25
- 'evidence-materialization',
26
- ]);
27
- export class ConnectorRuntimeErrorV2 extends Error {
28
- code;
29
- constructor(code, message) {
30
- super(message);
31
- this.code = code;
32
- this.name = code;
33
- }
34
- }
35
- function fail(code, message) {
36
- throw new ConnectorRuntimeErrorV2(code, message);
37
- }
38
- function exactKeys(value, expected, label) {
39
- if (JSON.stringify(Object.keys(value).sort()) !== JSON.stringify([...expected].sort())) {
40
- fail('E_LIVE_EVIDENCE_REQUEST_INVALID', `${label} contains missing or unsupported fields.`);
41
- }
42
- }
43
- function record(value, label) {
44
- if (value === null || typeof value !== 'object' || Array.isArray(value)) {
45
- fail('E_LIVE_EVIDENCE_REQUEST_INVALID', `${label} must be one closed object.`);
46
- }
47
- return value;
48
- }
49
- function safePortable(value, key = '', seen = new Set()) {
50
- if (SENSITIVE_KEY.test(key)) {
51
- fail('E_LIVE_EVIDENCE_PRIVATE_DATA', 'Live-evidence input contains restricted content.');
52
- }
53
- if (typeof value === 'string') {
54
- if (value.length > 2_048 || PRIVATE_VALUE.test(value) || value.includes('\u0000')) {
55
- fail('E_LIVE_EVIDENCE_PRIVATE_DATA', 'Live-evidence input contains restricted content.');
56
- }
57
- return;
58
- }
59
- if (value === null || typeof value !== 'object')
60
- return;
61
- if (seen.has(value))
62
- fail('E_LIVE_EVIDENCE_REQUEST_INVALID', 'Live-evidence input contains a cycle.');
63
- seen.add(value);
64
- try {
65
- if (Array.isArray(value)) {
66
- for (const entry of value)
67
- safePortable(entry, key, seen);
68
- }
69
- else {
70
- for (const [nestedKey, nested] of Object.entries(value)) {
71
- safePortable(nested, nestedKey, seen);
72
- }
73
- }
74
- }
75
- finally {
76
- seen.delete(value);
77
- }
78
- }
79
- function sortedUnique(values, label) {
80
- if (values.length < 1 ||
81
- values.length > 32 ||
82
- values.some((value) => !TEXT_ID.test(value)) ||
83
- new Set(values).size !== values.length ||
84
- values.some((value, index) => index > 0 && value.localeCompare(values[index - 1]) <= 0)) {
85
- fail('E_LIVE_EVIDENCE_REQUEST_INVALID', `${label} must be unique and canonically ordered.`);
86
- }
87
- }
88
- export function assertConnectorEvidenceRequestV2(value) {
89
- const input = record(value, 'Live-evidence request');
90
- exactKeys(input, [
91
- 'accountIdentityHash',
92
- 'actor',
93
- 'adapterVersion',
94
- 'assignmentId',
95
- 'bounds',
96
- 'claim',
97
- 'classification',
98
- 'consentExpiresAt',
99
- 'consentRecordId',
100
- 'kind',
101
- 'logicalCredentialRef',
102
- 'operation',
103
- 'ownerActorId',
104
- 'provider',
105
- 'purpose',
106
- 'queryHash',
107
- 'schemaVersion',
108
- 'scope',
109
- 'scopes',
110
- 'selectors',
111
- 'sensitivity',
112
- 'sourceContract',
113
- 'submissionId',
114
- 'window',
115
- ], 'Live-evidence request');
116
- const actor = record(input.actor, 'Actor');
117
- const provider = record(input.provider, 'Provider');
118
- const sourceContract = record(input.sourceContract, 'Source contract');
119
- const scope = record(input.scope, 'Scope');
120
- const window = record(input.window, 'Window');
121
- const bounds = record(input.bounds, 'Bounds');
122
- const claim = record(input.claim, 'Claim');
123
- exactKeys(actor, ['actorId', 'kind', 'runtime'], 'Actor');
124
- exactKeys(provider, ['id', 'version'], 'Provider');
125
- exactKeys(sourceContract, ['id', 'version'], 'Source contract');
126
- exactKeys(scope, ['domainId', 'domainVersion', 'scopeId'], 'Scope');
127
- exactKeys(window, ['from', 'to'], 'Window');
128
- exactKeys(bounds, ['maxBytes', 'maxCalls', 'maxPages', 'maxRecords'], 'Bounds');
129
- exactKeys(claim, ['confidence', 'localClaimId', 'relation'], 'Claim');
130
- safePortable(input);
131
- if (input.kind !== 'openplanr-live-evidence-request' ||
132
- input.schemaVersion !== '1.0.0' ||
133
- typeof input.assignmentId !== 'string' ||
134
- !/^asg_[A-Za-z0-9][A-Za-z0-9._-]{7,127}$/u.test(input.assignmentId) ||
135
- typeof input.submissionId !== 'string' ||
136
- !/^sub_[A-Za-z0-9][A-Za-z0-9._-]{7,127}$/u.test(input.submissionId) ||
137
- typeof actor.actorId !== 'string' ||
138
- !TEXT_ID.test(actor.actorId) ||
139
- !['agent', 'human'].includes(String(actor.kind)) ||
140
- typeof actor.runtime !== 'string' ||
141
- !TEXT_ID.test(actor.runtime) ||
142
- typeof input.ownerActorId !== 'string' ||
143
- !TEXT_ID.test(input.ownerActorId) ||
144
- typeof provider.id !== 'string' ||
145
- !ID.test(provider.id) ||
146
- typeof provider.version !== 'string' ||
147
- !VERSION.test(provider.version) ||
148
- typeof input.adapterVersion !== 'string' ||
149
- !VERSION.test(input.adapterVersion) ||
150
- typeof input.operation !== 'string' ||
151
- !ID.test(input.operation) ||
152
- typeof sourceContract.id !== 'string' ||
153
- !ID.test(sourceContract.id) ||
154
- sourceContract.version !== '1.0.0' ||
155
- typeof scope.scopeId !== 'string' ||
156
- !TEXT_ID.test(scope.scopeId) ||
157
- typeof scope.domainId !== 'string' ||
158
- !ID.test(scope.domainId) ||
159
- typeof scope.domainVersion !== 'string' ||
160
- !VERSION.test(scope.domainVersion) ||
161
- typeof input.logicalCredentialRef !== 'string' ||
162
- !TEXT_ID.test(input.logicalCredentialRef) ||
163
- typeof input.consentRecordId !== 'string' ||
164
- !/^lcon_[A-Za-z0-9][A-Za-z0-9._-]{7,127}$/u.test(input.consentRecordId) ||
165
- typeof input.accountIdentityHash !== 'string' ||
166
- !HASH.test(input.accountIdentityHash) ||
167
- typeof input.queryHash !== 'string' ||
168
- !HASH.test(input.queryHash) ||
169
- typeof input.purpose !== 'string' ||
170
- input.purpose.length < 1 ||
171
- input.purpose.length > 512 ||
172
- !['public', 'internal', 'confidential', 'restricted'].includes(String(input.classification)) ||
173
- !['public', 'internal', 'confidential', 'restricted'].includes(String(input.sensitivity)) ||
174
- typeof window.from !== 'string' ||
175
- typeof window.to !== 'string' ||
176
- !Number.isFinite(Date.parse(window.from)) ||
177
- !Number.isFinite(Date.parse(window.to)) ||
178
- Date.parse(window.from) >= Date.parse(window.to) ||
179
- typeof input.consentExpiresAt !== 'string' ||
180
- !Number.isFinite(Date.parse(input.consentExpiresAt)) ||
181
- Date.parse(window.to) >= Date.parse(input.consentExpiresAt) ||
182
- typeof claim.localClaimId !== 'string' ||
183
- !TEXT_ID.test(claim.localClaimId) ||
184
- !['supportedBy', 'contradictedBy'].includes(String(claim.relation)) ||
185
- typeof claim.confidence !== 'number' ||
186
- !Number.isFinite(claim.confidence) ||
187
- claim.confidence < 0 ||
188
- claim.confidence > 1) {
189
- fail('E_LIVE_EVIDENCE_REQUEST_INVALID', 'Live-evidence request fields are invalid.');
190
- }
191
- const maxima = { maxCalls: 100, maxPages: 100, maxRecords: 10_000, maxBytes: 10_485_760 };
192
- for (const [key, maximum] of Object.entries(maxima)) {
193
- const bound = bounds[key];
194
- if (!Number.isSafeInteger(bound) || bound < 1 || bound > maximum) {
195
- fail('E_LIVE_EVIDENCE_REQUEST_INVALID', 'Live-evidence request bounds are invalid.');
196
- }
197
- }
198
- if (!Array.isArray(input.selectors) || !Array.isArray(input.scopes)) {
199
- fail('E_LIVE_EVIDENCE_REQUEST_INVALID', 'Live-evidence selectors and scopes are invalid.');
200
- }
201
- sortedUnique(input.selectors, 'selectors');
202
- sortedUnique(input.scopes, 'scopes');
203
- return Object.freeze(structuredClone(input));
204
- }
205
- function effects(executed = new Set()) {
206
- return Object.freeze(RESULT_EFFECTS.map((id) => Object.freeze({ id, executed: executed.has(id) })));
207
- }
208
- function ok(operation, data, executed, nextAction) {
209
- return Object.freeze({
210
- ok: true,
211
- operation,
212
- effects: effects(executed),
213
- authorityRequired: operation === 'evidence.preview',
214
- ownerActionRequired: false,
215
- nextAction,
216
- data: Object.freeze(structuredClone(data)),
217
- });
218
- }
219
- function failed(operation, code, problem, executed, options = {}) {
220
- return Object.freeze({
221
- ok: false,
222
- operation,
223
- effects: effects(executed),
224
- authorityRequired: code.includes('AUTHORITY') || code.includes('CONSENT') || code.includes('OWNER_CONFIRMATION'),
225
- ownerActionRequired: options.ownerActionRequired ?? false,
226
- nextAction: options.nextAction ??
227
- 'Inspect the bounded failure and retry only after correcting its cause.',
228
- error: Object.freeze({ code, problem }),
229
- });
230
- }
231
- function digestId(prefix, value) {
232
- return `${prefix}_${sha256CanonicalJson(value).slice('sha256:'.length, 38)}`;
233
- }
234
- function protocolDocket(request) {
235
- return Object.freeze({
236
- provider: Object.freeze(structuredClone(request.provider)),
237
- adapterVersion: request.adapterVersion,
238
- accountIdentityHash: request.accountIdentityHash,
239
- selectors: Object.freeze([...request.selectors]),
240
- effect: 'provider-call',
241
- scopes: Object.freeze([...request.scopes]),
242
- queryHash: request.queryHash,
243
- window: Object.freeze(structuredClone(request.window)),
244
- ceilings: Object.freeze(structuredClone(request.bounds)),
245
- classification: request.classification,
246
- sensitivity: request.sensitivity,
247
- purpose: request.purpose,
248
- expiresAt: request.consentExpiresAt,
249
- changedRecordDiffHash: null,
250
- consequences: Object.freeze(['The provider receives one bounded read-only evidence query.']),
251
- authorityBoundary: 'consent-is-not-access-authority',
252
- choices: Object.freeze(['approve', 'cancel']),
253
- defaultChoice: null,
254
- cancelEffect: 'none',
255
- });
256
- }
257
- function consentRecord(request, docket, issuedAt) {
258
- const body = {
259
- kind: 'operating-live-evidence-consent-record',
260
- schemaVersion: '1.0.0',
261
- protocolVersion: '2.0.0',
262
- consentRecordId: request.consentRecordId,
263
- authority: 'none',
264
- decision: 'approved',
265
- docket,
266
- docketHash: sha256CanonicalJson(docket),
267
- actor: { actorId: request.ownerActorId, kind: 'human' },
268
- provider: request.provider,
269
- adapterVersion: request.adapterVersion,
270
- accountIdentityHash: request.accountIdentityHash,
271
- selectors: request.selectors,
272
- scopes: request.scopes,
273
- queryHash: request.queryHash,
274
- window: request.window,
275
- ceilings: request.bounds,
276
- classification: request.classification,
277
- purpose: request.purpose,
278
- issuedAt,
279
- expiresAt: request.consentExpiresAt,
280
- };
281
- return Object.freeze({ ...body, consentHash: sha256CanonicalJson(body) });
282
- }
283
- function exactConsentForRequest(value, request, docket) {
284
- return (value.consentRecordId === request.consentRecordId &&
285
- value.authority === 'none' &&
286
- value.decision === 'approved' &&
287
- sha256CanonicalJson(value.docket) === sha256CanonicalJson(docket) &&
288
- sha256CanonicalJson(value.provider) === sha256CanonicalJson(request.provider) &&
289
- value.adapterVersion === request.adapterVersion &&
290
- value.accountIdentityHash === request.accountIdentityHash &&
291
- sha256CanonicalJson(value.selectors) === sha256CanonicalJson(request.selectors) &&
292
- sha256CanonicalJson(value.scopes) === sha256CanonicalJson(request.scopes) &&
293
- value.queryHash === request.queryHash &&
294
- sha256CanonicalJson(value.window) === sha256CanonicalJson(request.window) &&
295
- sha256CanonicalJson(value.ceilings) === sha256CanonicalJson(request.bounds) &&
296
- value.classification === request.classification &&
297
- value.purpose === request.purpose &&
298
- value.expiresAt === request.consentExpiresAt);
299
- }
300
- function selfHashed(body, field) {
301
- return Object.freeze({ ...body, [field]: sha256CanonicalJson(body) });
302
- }
303
- function checkpointEvent(checkpoint, type, at, patch) {
304
- const body = {
305
- eventId: digestId('lcev', {
306
- checkpointId: checkpoint.checkpointId,
307
- generation: checkpoint.generation,
308
- type,
309
- patch,
310
- }),
311
- type,
312
- expectedGeneration: checkpoint.generation,
313
- expectedCheckpointHash: checkpoint.checkpointHash,
314
- at,
315
- patch,
316
- };
317
- return Object.freeze({ ...body, eventHash: sha256CanonicalJson(body) });
318
- }
319
- function findBaseRecord(records, identity, fields, label) {
320
- const matches = records.filter((entry) => fields.every((field) => entry[field] === identity[field]));
321
- if (matches.length !== 1)
322
- fail('E_LIVE_EVIDENCE_PACKAGE_INVALID', `${label} is not uniquely available.`);
323
- return matches[0];
324
- }
325
- function asHash(value, label) {
326
- if (typeof value !== 'string' || !HASH.test(value)) {
327
- fail('E_LIVE_EVIDENCE_PACKAGE_INVALID', `${label} is invalid.`);
328
- }
329
- return value;
330
- }
331
- function sha256Bytes(bytes) {
332
- return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
333
- }
334
- export class ConnectorRuntimeV2 {
335
- #registry;
336
- #credentials;
337
- #checkpoints;
338
- #contracts;
339
- #evidenceRegistry;
340
- #operate;
341
- #transport;
342
- #now;
343
- constructor(input) {
344
- this.#registry = input.registry;
345
- this.#credentials = input.credentials;
346
- this.#checkpoints = input.checkpoints;
347
- this.#contracts = input.contracts;
348
- this.#evidenceRegistry = input.evidenceRegistry;
349
- this.#operate = input.operate;
350
- this.#transport = input.transport;
351
- this.#now = input.now ?? Date.now;
352
- }
353
- async providers() {
354
- const providers = this.#registry.list().map(({ registration, adapter }) => ({
355
- provider: adapter.definition.provider,
356
- adapterVersion: adapter.definition.adapterVersion,
357
- category: adapter.definition.category,
358
- operations: adapter.definition.operations.map(({ operation, sourceContracts }) => ({
359
- operation,
360
- sourceContracts,
361
- })),
362
- registrationHash: registration.registrationHash,
363
- }));
364
- return ok('evidence.providers', { providers }, new Set(), 'Preview one exact provider request before collecting evidence.');
365
- }
366
- async preview(value) {
367
- const request = assertConnectorEvidenceRequestV2(value);
368
- const selected = this.#registry.resolve({
369
- providerId: request.provider.id,
370
- providerVersion: request.provider.version,
371
- adapterVersion: request.adapterVersion,
372
- operation: request.operation,
373
- sourceContractId: request.sourceContract.id,
374
- domainId: request.scope.domainId,
375
- });
376
- const docket = protocolDocket(request);
377
- return Object.freeze({
378
- ok: true,
379
- operation: 'evidence.preview',
380
- effects: effects(),
381
- authorityRequired: true,
382
- ownerActionRequired: true,
383
- nextAction: 'Ask the named owner to choose approve or cancel on this exact neutral docket.',
384
- data: Object.freeze({
385
- provider: request.provider,
386
- adapterVersion: request.adapterVersion,
387
- category: selected.adapter.definition.category,
388
- logicalCredentialRef: request.logicalCredentialRef,
389
- docket,
390
- protocolDocketHash: sha256CanonicalJson(docket),
391
- }),
392
- });
393
- }
394
- async status(value) {
395
- if (typeof value === 'string') {
396
- const snapshot = await this.#checkpoints.readByIdentity(value);
397
- const checkpoint = snapshot.checkpoint;
398
- return ok('evidence.status', {
399
- checkpointId: checkpoint.checkpointId,
400
- requestId: checkpoint.requestId,
401
- state: checkpoint.state,
402
- generation: checkpoint.generation,
403
- requestHash: checkpoint.requestHash,
404
- artifactId: checkpoint.artifactId,
405
- artifactHash: checkpoint.artifactHash,
406
- updatedAt: checkpoint.updatedAt,
407
- }, new Set(), checkpoint.state === 'committed'
408
- ? 'Use the accepted Evidence through normal Operate reads.'
409
- : 'Resume only the exact bound collection request.');
410
- }
411
- const request = assertConnectorEvidenceRequestV2(value);
412
- const docket = protocolDocket(request);
413
- const protocolBinding = Object.freeze({
414
- actorId: request.ownerActorId,
415
- credentialRef: request.logicalCredentialRef,
416
- consentRecordId: request.consentRecordId,
417
- protocolDocketHash: sha256CanonicalJson(docket),
418
- provider: request.provider,
419
- adapterVersion: request.adapterVersion,
420
- accountIdentityHash: request.accountIdentityHash,
421
- });
422
- try {
423
- const consent = await this.#credentials.validatedConsent({
424
- ...protocolBinding,
425
- requestHash: 'sha256:0000000000000000000000000000000000000000000000000000000000000000',
426
- });
427
- if (!exactConsentForRequest(consent, request, docket)) {
428
- fail('E_LIVE_EVIDENCE_CONSENT_INVALID', 'Stored consent does not match this exact request.');
429
- }
430
- const protocolBindingHash = await this.#credentials.protocolBindingHash(protocolBinding);
431
- const selected = this.#registry.resolve({
432
- providerId: request.provider.id,
433
- providerVersion: request.provider.version,
434
- adapterVersion: request.adapterVersion,
435
- operation: request.operation,
436
- sourceContractId: request.sourceContract.id,
437
- domainId: request.scope.domainId,
438
- });
439
- const preparation = await this.#operate.prepareLiveEvidenceSubmission({
440
- assignmentId: request.assignmentId,
441
- submissionId: request.submissionId,
442
- actor: request.actor,
443
- });
444
- const baseEvidenceProvider = findBaseRecord(this.#evidenceRegistry.providers, record(selected.registration.baseEvidenceProvider, 'Provider base reference'), ['providerId', 'providerVersion'], 'Base evidence-provider registration');
445
- const baseResolver = findBaseRecord(this.#evidenceRegistry.resolvers, record(selected.registration.baseResolver, 'Resolver base reference'), ['resolverId', 'resolverVersion'], 'Base evidence-resolver registration');
446
- const checkpointId = digestId('lchk', { request, protocolBindingHash });
447
- const snapshot = await this.#checkpoints.readByIdentity(checkpointId);
448
- const context = this.#checkpointContext({
449
- request,
450
- preparation,
451
- registration: selected.registration,
452
- baseEvidenceProvider,
453
- baseResolver,
454
- consent,
455
- protocolBindingHash,
456
- health: record(snapshot.checkpoint.health, 'Stored provider health'),
457
- });
458
- const checkpoint = snapshot.checkpoint;
459
- this.#assertCheckpointContext(context, checkpoint);
460
- return ok('evidence.status', {
461
- checkpointId: context.checkpointId,
462
- state: checkpoint.state,
463
- generation: checkpoint.generation,
464
- requestHash: checkpoint.requestHash,
465
- artifactId: checkpoint.artifactId,
466
- artifactHash: checkpoint.artifactHash,
467
- updatedAt: checkpoint.updatedAt,
468
- }, new Set(), checkpoint.state === 'committed'
469
- ? 'Use the accepted Evidence through normal Operate reads.'
470
- : 'Resume the exact collection request; changed inputs will conflict.');
471
- }
472
- catch (error) {
473
- if (error instanceof ConnectorCheckpointCustodyError &&
474
- error.code === 'LIVE_EVIDENCE_CHECKPOINT_NOT_FOUND') {
475
- return failed('evidence.status', error.code, 'No checkpoint exists for this exact request.', new Set(), { nextAction: 'Preview the request before choosing whether to collect.' });
476
- }
477
- throw error;
478
- }
479
- }
480
- #checkpointContext(input) {
481
- const { request, preparation, registration, baseEvidenceProvider, baseResolver, consent, protocolBindingHash, health, } = input;
482
- const sourceIdentityHash = sha256CanonicalJson({
483
- provider: request.provider,
484
- adapterVersion: request.adapterVersion,
485
- accountIdentityHash: request.accountIdentityHash,
486
- selectors: request.selectors,
487
- sourceContract: request.sourceContract,
488
- });
489
- const checkpointId = digestId('lchk', { request, protocolBindingHash });
490
- const requestId = digestId('lreq', { request, protocolBindingHash });
491
- const createdAt = String(consent.issuedAt);
492
- const immutable = {
493
- kind: 'operating-connector-checkpoint',
494
- schemaVersion: '1.0.0',
495
- protocolVersion: '2.0.0',
496
- checkpointId,
497
- requestId,
498
- effect: 'provider-call',
499
- provider: request.provider,
500
- providerRegistrationHash: registration.registrationHash,
501
- adapterVersion: request.adapterVersion,
502
- sourceIdentityHash,
503
- credentialRefHash: sha256CanonicalJson({
504
- logicalCredentialRef: request.logicalCredentialRef,
505
- protocolDocket: protocolDocket(request),
506
- }),
507
- boundsHash: sha256CanonicalJson(request.bounds),
508
- queryHash: request.queryHash,
509
- windowHash: sha256CanonicalJson(request.window),
510
- health,
511
- redactionVersion: '1.0.0',
512
- createdAt,
513
- };
514
- const provisionalRequesting = selfHashed({
515
- ...immutable,
516
- generation: 1,
517
- lastEventId: digestId('lcev', { checkpointId, type: 'request' }),
518
- lastEventHash: sha256CanonicalJson({ checkpointId, type: 'request' }),
519
- state: 'requesting',
520
- requestHash: 'sha256:0000000000000000000000000000000000000000000000000000000000000000',
521
- consentRecordId: consent.consentRecordId,
522
- consentRecordHash: consent.consentHash,
523
- consentExpiresAt: consent.expiresAt,
524
- runtimeCapabilityHash: protocolBindingHash,
525
- predecessorDigest: 'sha256:0000000000000000000000000000000000000000000000000000000000000000',
526
- watermarkDigest: null,
527
- artifactId: null,
528
- artifactHash: null,
529
- absenceDigest: null,
530
- updatedAt: createdAt,
531
- }, 'checkpointHash');
532
- this.#contracts.assertOperatingConnectorCheckpointV2(provisionalRequesting);
533
- const requestHash = this.#contracts.deriveOperatingLiveEvidenceRequestHashV2({
534
- assignment: preparation.assignment,
535
- consentRecord: consent,
536
- connectorCheckpoint: provisionalRequesting,
537
- liveProviderRegistration: registration,
538
- baseEvidenceProvider,
539
- baseResolver,
540
- sourceContract: request.sourceContract,
541
- classification: request.classification,
542
- });
543
- const prepared = this.#contracts.assertOperatingConnectorCheckpointV2(selfHashed({
544
- ...immutable,
545
- generation: 0,
546
- lastEventId: null,
547
- lastEventHash: null,
548
- state: 'prepared',
549
- requestHash,
550
- consentRecordId: null,
551
- consentRecordHash: null,
552
- consentExpiresAt: null,
553
- runtimeCapabilityHash: null,
554
- predecessorDigest: null,
555
- watermarkDigest: null,
556
- artifactId: null,
557
- artifactHash: null,
558
- absenceDigest: null,
559
- updatedAt: createdAt,
560
- }, 'checkpointHash'));
561
- return Object.freeze({
562
- checkpointId,
563
- requestId,
564
- requestHash,
565
- sourceIdentityHash,
566
- health,
567
- prepared,
568
- });
569
- }
570
- #assertCheckpointContext(context, checkpoint) {
571
- const immutableFields = [
572
- 'kind',
573
- 'schemaVersion',
574
- 'protocolVersion',
575
- 'checkpointId',
576
- 'requestId',
577
- 'effect',
578
- 'provider',
579
- 'providerRegistrationHash',
580
- 'adapterVersion',
581
- 'sourceIdentityHash',
582
- 'credentialRefHash',
583
- 'boundsHash',
584
- 'queryHash',
585
- 'windowHash',
586
- 'health',
587
- 'redactionVersion',
588
- 'createdAt',
589
- 'requestHash',
590
- ];
591
- if (immutableFields.some((field) => sha256CanonicalJson(checkpoint[field]) !== sha256CanonicalJson(context.prepared[field]))) {
592
- fail('E_LIVE_EVIDENCE_CHECKPOINT_CONFLICT', 'Stored checkpoint custody does not match the exact request.');
593
- }
594
- }
595
- #assertPreflightHealth(health) {
596
- if (health.status !== 'available' ||
597
- !HASH.test(health.proofDigest) ||
598
- !Number.isFinite(Date.parse(health.checkedAt)) ||
599
- !Number.isFinite(Date.parse(health.freshUntil)) ||
600
- Date.parse(health.checkedAt) >= Date.parse(health.freshUntil) ||
601
- this.#now() >= Date.parse(health.freshUntil)) {
602
- fail('E_LIVE_EVIDENCE_PROVIDER_UNHEALTHY', 'The provider lacks one current trusted available-health proof.');
603
- }
604
- }
605
- async collect(value, options = {}) {
606
- const operation = 'evidence.collect';
607
- const executed = new Set();
608
- const request = assertConnectorEvidenceRequestV2(value);
609
- const selected = this.#registry.resolve({
610
- providerId: request.provider.id,
611
- providerVersion: request.provider.version,
612
- adapterVersion: request.adapterVersion,
613
- operation: request.operation,
614
- sourceContractId: request.sourceContract.id,
615
- domainId: request.scope.domainId,
616
- });
617
- const registration = selected.registration;
618
- const operationDefinition = selected.adapter.definition.operations.find((candidate) => candidate.operation === request.operation);
619
- if (!operationDefinition) {
620
- fail('E_LIVE_EVIDENCE_PROVIDER_INCOMPATIBLE', 'The exact provider operation is unavailable.');
621
- }
622
- const registrationBounds = record(registration.bounds, 'Registration bounds');
623
- for (const key of ['maxCalls', 'maxPages', 'maxRecords', 'maxBytes']) {
624
- if (request.bounds[key] > Number(registrationBounds[key])) {
625
- return failed(operation, 'E_LIVE_EVIDENCE_SCOPE_EXPANSION', 'The request exceeds the frozen provider bounds.', executed, {
626
- ownerActionRequired: true,
627
- nextAction: 'Ask the owner to approve a separately registered bounded provider scope.',
628
- });
629
- }
630
- }
631
- if (!selected.adapter.definition.scopes.every((scope) => request.scopes.includes(scope)) ||
632
- !request.scopes.every((scope) => selected.adapter.definition.scopes.includes(scope))) {
633
- return failed(operation, 'E_LIVE_EVIDENCE_SCOPE_EXPANSION', 'The request scopes differ from the frozen adapter scopes.', executed, {
634
- ownerActionRequired: true,
635
- nextAction: 'Ask the owner to approve a separately registered exact scope.',
636
- });
637
- }
638
- const docket = protocolDocket(request);
639
- const protocolDocketHash = sha256CanonicalJson(docket);
640
- const protocolBinding = Object.freeze({
641
- actorId: request.ownerActorId,
642
- credentialRef: request.logicalCredentialRef,
643
- consentRecordId: request.consentRecordId,
644
- protocolDocketHash,
645
- provider: request.provider,
646
- adapterVersion: request.adapterVersion,
647
- accountIdentityHash: request.accountIdentityHash,
648
- });
649
- const proposedConsent = this.#contracts.assertOperatingLiveEvidenceConsentRecordV2(consentRecord(request, docket, new Date(this.#now()).toISOString()));
650
- let currentConsent = null;
651
- try {
652
- currentConsent = await this.#credentials.validatedConsent({
653
- ...protocolBinding,
654
- requestHash: 'sha256:0000000000000000000000000000000000000000000000000000000000000000',
655
- });
656
- }
657
- catch (error) {
658
- if (!(error instanceof ConnectorCredentialCustodyError) ||
659
- !['LIVE_EVIDENCE_CONSENT_INVALID', 'LIVE_EVIDENCE_CONSENT_EXPIRED'].includes(error.code)) {
660
- throw error;
661
- }
662
- }
663
- if (currentConsent && !exactConsentForRequest(currentConsent, request, docket)) {
664
- currentConsent = null;
665
- }
666
- if (!currentConsent && !options.allowOwnerConfirmation) {
667
- return failed(operation, 'E_LIVE_EVIDENCE_OWNER_CONFIRMATION_REQUIRED', 'The exact current owner consent grant is unavailable or changed.', executed, {
668
- ownerActionRequired: true,
669
- nextAction: 'Ask the named owner to review the exact neutral consent docket.',
670
- });
671
- }
672
- const preparation = await this.#operate.prepareLiveEvidenceSubmission({
673
- assignmentId: request.assignmentId,
674
- submissionId: request.submissionId,
675
- actor: request.actor,
676
- });
677
- const baseEvidenceProvider = findBaseRecord(this.#evidenceRegistry.providers, record(registration.baseEvidenceProvider, 'Provider base reference'), ['providerId', 'providerVersion'], 'Base evidence-provider registration');
678
- const baseResolver = findBaseRecord(this.#evidenceRegistry.resolvers, record(registration.baseResolver, 'Resolver base reference'), ['resolverId', 'resolverVersion'], 'Base evidence-resolver registration');
679
- const consent = currentConsent ?? proposedConsent;
680
- const protocolBindingHash = currentConsent
681
- ? await this.#credentials.protocolBindingHash(protocolBinding)
682
- : sha256CanonicalJson({
683
- domain: 'openplanr-local-live-evidence-capability-binding-v2',
684
- authority: 'none',
685
- bindingHash: sha256CanonicalJson(protocolBinding),
686
- consentHash: proposedConsent.consentHash,
687
- });
688
- const checkpointId = digestId('lchk', { request, protocolBindingHash });
689
- let snapshot;
690
- let activeContext;
691
- try {
692
- snapshot = await this.#checkpoints.readByIdentity(checkpointId);
693
- activeContext = this.#checkpointContext({
694
- request,
695
- preparation,
696
- registration,
697
- baseEvidenceProvider,
698
- baseResolver,
699
- consent,
700
- protocolBindingHash,
701
- health: record(snapshot.checkpoint.health, 'Stored provider health'),
702
- });
703
- this.#assertCheckpointContext(activeContext, snapshot.checkpoint);
704
- }
705
- catch (error) {
706
- if (!(error instanceof ConnectorCheckpointCustodyError) ||
707
- error.code !== 'LIVE_EVIDENCE_CHECKPOINT_NOT_FOUND') {
708
- throw error;
709
- }
710
- const preflightHealth = this.#transport.health({ request, registration });
711
- this.#assertPreflightHealth(preflightHealth);
712
- activeContext = this.#checkpointContext({
713
- request,
714
- preparation,
715
- registration,
716
- baseEvidenceProvider,
717
- baseResolver,
718
- consent,
719
- protocolBindingHash,
720
- health: preflightHealth,
721
- });
722
- }
723
- const credentialBinding = Object.freeze({
724
- ...protocolBinding,
725
- requestHash: activeContext.requestHash,
726
- });
727
- let confirmedCapability;
728
- if (!currentConsent) {
729
- const confirmation = await this.#credentials.issueOwnerConfirmation({
730
- binding: credentialBinding,
731
- proposedConsentRecord: proposedConsent,
732
- });
733
- confirmedCapability = await this.#credentials.issueCapability({
734
- binding: credentialBinding,
735
- ownerConfirmation: confirmation,
736
- });
737
- currentConsent = await this.#credentials.validatedConsent(credentialBinding);
738
- if (!exactConsentForRequest(currentConsent, request, docket)) {
739
- fail('E_LIVE_EVIDENCE_CONSENT_INVALID', 'Confirmed consent does not match the exact request.');
740
- }
741
- const persistedBindingHash = await this.#credentials.protocolBindingHash(protocolBinding);
742
- if (persistedBindingHash !== protocolBindingHash) {
743
- fail('E_LIVE_EVIDENCE_CONSENT_INVALID', 'Confirmed consent changed its protocol binding.');
744
- }
745
- }
746
- if (!snapshot) {
747
- snapshot = await this.#checkpoints.initialize(activeContext.prepared);
748
- }
749
- const { sourceIdentityHash } = activeContext;
750
- let checkpoint = snapshot.checkpoint;
751
- if (checkpoint.state === 'committed') {
752
- return ok(operation, this.#acceptedSummary(checkpoint, snapshot.evidence?.bytes), executed, 'Use the existing accepted Evidence; no provider or Store effect was repeated.');
753
- }
754
- if (checkpoint.state === 'materialized') {
755
- return this.#completeMaterialized(request, preparation, registration, baseEvidenceProvider, baseResolver, consent, checkpoint, snapshot.evidence?.bytes, executed, snapshot.checkpointHistory);
756
- }
757
- this.#assertPreflightHealth(checkpoint.health);
758
- const capability = confirmedCapability ??
759
- (await this.#credentials.issueCapability({ binding: credentialBinding }));
760
- if (checkpoint.state === 'prepared') {
761
- const requestedAt = new Date(this.#now()).toISOString();
762
- snapshot = await this.#checkpoints.transition({
763
- checkpointId,
764
- event: checkpointEvent(checkpoint, 'request', requestedAt, {
765
- consentRecordId: consent.consentRecordId,
766
- consentRecordHash: consent.consentHash,
767
- consentExpiresAt: consent.expiresAt,
768
- runtimeCapabilityHash: protocolBindingHash,
769
- health: checkpoint.health,
770
- }),
771
- });
772
- checkpoint = snapshot.checkpoint;
773
- }
774
- if (checkpoint.state !== 'requesting') {
775
- return failed(operation, 'E_LIVE_EVIDENCE_CHECKPOINT_TERMINAL', 'The exact request already reached a non-materialized terminal checkpoint.', executed, { nextAction: 'Inspect the typed absence or uncertainty before creating a new request.' });
776
- }
777
- let providerResult;
778
- try {
779
- providerResult = await this.#credentials.withResolvedCredential(capability, credentialBinding, async (credential) => {
780
- executed.add('provider-call');
781
- return this.#transport.execute({
782
- request,
783
- registration,
784
- collection: operationDefinition.collection,
785
- credential,
786
- expectedHealth: checkpoint.health,
787
- });
788
- });
789
- }
790
- catch (error) {
791
- if (error instanceof ConnectorCredentialCustodyError)
792
- throw error;
793
- const observedAt = new Date(this.#now()).toISOString();
794
- providerResult = {
795
- status: 'uncertain',
796
- requestedAt: String(checkpoint.updatedAt),
797
- capturedAt: observedAt,
798
- freshUntil: new Date(this.#now() + 60_000).toISOString(),
799
- health: {
800
- status: 'unavailable',
801
- checkedAt: observedAt,
802
- freshUntil: new Date(this.#now() + 60_000).toISOString(),
803
- proofDigest: sha256CanonicalJson({
804
- domain: 'openplanr-live-evidence-provider-unavailable-v2',
805
- provider: request.provider,
806
- requestHash: checkpoint.requestHash,
807
- observedAt,
808
- }),
809
- },
810
- payload: {},
811
- absence: {
812
- kind: 'provider-unavailable',
813
- reasonCode: 'PROVIDER_UNAVAILABLE',
814
- required: true,
815
- observedAt,
816
- details: { retryable: false },
817
- },
818
- };
819
- }
820
- if (providerResult.status === 'materialized' &&
821
- (providerResult.health.status !== 'available' ||
822
- sha256CanonicalJson(providerResult.health) !== sha256CanonicalJson(checkpoint.health))) {
823
- const observedAt = providerResult.capturedAt;
824
- providerResult = {
825
- ...providerResult,
826
- status: 'uncertain',
827
- payload: {},
828
- absence: {
829
- kind: 'unhealthy',
830
- reasonCode: 'PROVIDER_HEALTH_DIVERGED',
831
- required: true,
832
- observedAt,
833
- details: { expectedProofDigest: checkpoint.health.proofDigest },
834
- },
835
- };
836
- }
837
- const adapterResult = selected.adapter.normalize({
838
- operation: request.operation,
839
- sourceContract: request.sourceContract,
840
- requestHash: asHash(checkpoint.requestHash, 'Checkpoint request hash'),
841
- sourceIdentityHash,
842
- classification: request.classification,
843
- status: providerResult.status,
844
- requestedAt: providerResult.requestedAt,
845
- capturedAt: providerResult.capturedAt,
846
- freshUntil: providerResult.freshUntil,
847
- health: providerResult.health,
848
- bounds: request.bounds,
849
- payload: providerResult.payload,
850
- ...(providerResult.absence ? { absence: providerResult.absence } : {}),
851
- });
852
- if (adapterResult.status !== 'materialized') {
853
- const absence = adapterResult.absences[0];
854
- const terminalType = adapterResult.status === 'uncertain' ? 'uncertain' : 'absent';
855
- await this.#checkpoints.transition({
856
- checkpointId,
857
- event: checkpointEvent(checkpoint, terminalType, adapterResult.sourceTiming.capturedAt, {
858
- absenceDigest: absence.detailsHash,
859
- health: adapterResult.health,
860
- }),
861
- ...(providerResult.providerCursor ? { providerCursor: providerResult.providerCursor } : {}),
862
- });
863
- return failed(operation, absence.reasonCode, 'The exact provider returned a typed absence or uncertain result.', executed, { nextAction: 'Resolve the declared provider condition or create a new bounded request.' });
864
- }
865
- const redactedBytes = readConnectorAdapterResultBytesV2(adapterResult);
866
- if (sha256Bytes(redactedBytes) !== adapterResult.redactedBytesDigest) {
867
- fail('E_LIVE_EVIDENCE_RESPONSE_INVALID', 'Redacted adapter bytes changed after validation.');
868
- }
869
- const ingestion = this.#buildIngestion({
870
- request,
871
- preparation,
872
- registration,
873
- baseResolver,
874
- consent,
875
- checkpoint,
876
- adapterResult,
877
- });
878
- const ingestionBytes = Buffer.from(JSON.stringify(ingestion), 'utf8');
879
- snapshot = await this.#checkpoints.transition({
880
- checkpointId,
881
- event: checkpointEvent(checkpoint, 'materialize', adapterResult.sourceTiming.capturedAt, {
882
- artifactId: preparation.artifactId,
883
- artifactHash: sha256CanonicalJson(ingestion),
884
- watermarkDigest: providerResult.providerCursor
885
- ? sha256Bytes(providerResult.providerCursor)
886
- : null,
887
- health: adapterResult.health,
888
- }),
889
- evidenceBytes: ingestionBytes,
890
- evidenceFreshness: {
891
- capturedAt: adapterResult.sourceTiming.capturedAt,
892
- freshUntil: adapterResult.sourceTiming.freshUntil,
893
- },
894
- ...(providerResult.providerCursor ? { providerCursor: providerResult.providerCursor } : {}),
895
- });
896
- return this.#completeMaterialized(request, preparation, registration, baseEvidenceProvider, baseResolver, consent, snapshot.checkpoint, snapshot.evidence?.bytes, executed, snapshot.checkpointHistory);
897
- }
898
- #buildIngestion(input) {
899
- const { request, preparation, registration, consent, checkpoint, adapterResult } = input;
900
- const assignment = preparation.assignment;
901
- const candidate = {
902
- kind: 'operating-evidence-candidate',
903
- schemaVersion: '1.0.0',
904
- protocolVersion: '2.0.0',
905
- candidateId: preparation.candidateId,
906
- scopeId: request.scope.scopeId,
907
- domainId: request.scope.domainId,
908
- domainVersion: request.scope.domainVersion,
909
- sourceArtifactId: preparation.artifactId,
910
- evidenceKind: 'operate-artifact',
911
- locator: {
912
- artifactId: preparation.artifactId,
913
- expectedArtifactType: 'live-evidence-ingestion',
914
- expectedSchemaId: 'operating-live-evidence-ingestion',
915
- expectedSchemaVersion: '2.0.0',
916
- },
917
- provider: {
918
- id: registration.baseEvidenceProvider.providerId,
919
- version: registration.baseEvidenceProvider.providerVersion,
920
- },
921
- resolver: {
922
- id: registration.baseResolver.resolverId,
923
- version: registration.baseResolver.resolverVersion,
924
- },
925
- };
926
- const body = {
927
- kind: 'operating-live-evidence-ingestion',
928
- schemaVersion: '1.0.0',
929
- protocolVersion: '2.0.0',
930
- ingestionId: preparation.ingestionId,
931
- requestId: checkpoint.requestId,
932
- requestHash: checkpoint.requestHash,
933
- assignment: {
934
- assignmentId: assignment.assignmentId,
935
- assignmentRevision: 1,
936
- assignmentHash: sha256CanonicalJson(assignment),
937
- requestHash: checkpoint.requestHash,
938
- assignmentKind: 'verification',
939
- outputContract: {
940
- schemaId: 'operating-live-evidence-ingestion',
941
- schemaVersion: '2.0.0',
942
- },
943
- },
944
- submission: {
945
- operation: 'operate.assignment.submit',
946
- submissionId: preparation.submissionId,
947
- submittedEventId: preparation.acceptanceEventIds.submitted,
948
- artifactCreatedEventId: preparation.acceptanceEventIds.artifactCreated,
949
- validatedEventId: preparation.acceptanceEventIds.validated,
950
- artifactId: preparation.artifactId,
951
- evidenceRefId: preparation.evidenceRefId,
952
- },
953
- provider: request.provider,
954
- providerRegistration: registration.baseEvidenceProvider,
955
- providerRegistrationHash: registration.registrationHash,
956
- resolverRegistration: registration.baseResolver,
957
- resolverRegistrationHash: registration.baseResolver.recordDigest,
958
- adapterVersion: request.adapterVersion,
959
- status: 'materialized',
960
- consentRecordHash: consent.consentHash,
961
- runtimeCapabilityHash: checkpoint.runtimeCapabilityHash,
962
- checkpoint: {
963
- checkpointId: checkpoint.checkpointId,
964
- predecessorDigest: checkpoint.predecessorDigest,
965
- checkpointHash: checkpoint.checkpointHash,
966
- },
967
- health: adapterResult.health,
968
- boundsHash: sha256CanonicalJson(request.bounds),
969
- classification: request.classification,
970
- sourceContract: request.sourceContract,
971
- sourceTiming: adapterResult.sourceTiming,
972
- records: adapterResult.records,
973
- absences: [],
974
- evidenceCandidates: [candidate],
975
- evidenceClaimLinks: [
976
- {
977
- candidateId: preparation.candidateId,
978
- sourceArtifactId: preparation.artifactId,
979
- localClaimId: request.claim.localClaimId,
980
- relation: request.claim.relation,
981
- confidence: request.claim.confidence,
982
- },
983
- ],
984
- materialization: {
985
- artifactId: preparation.artifactId,
986
- artifactCreatedEventId: preparation.acceptanceEventIds.artifactCreated,
987
- evidenceRefId: preparation.evidenceRefId,
988
- contentDigest: 'sha256:0000000000000000000000000000000000000000000000000000000000000000',
989
- redactionVersion: '1.0.0',
990
- },
991
- };
992
- body.materialization.contentDigest =
993
- this.#contracts.deriveOperatingLiveEvidenceContentDigestV2(body);
994
- body.ingestionHash = sha256CanonicalJson(body);
995
- return Object.freeze(body);
996
- }
997
- #acceptedSummary(checkpoint, bytes) {
998
- if (!bytes)
999
- fail('E_LIVE_EVIDENCE_CHECKPOINT_INVALID', 'Accepted evidence custody is unavailable.');
1000
- let ingestion;
1001
- try {
1002
- ingestion = record(JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes)), 'Accepted ingestion');
1003
- }
1004
- catch {
1005
- fail('E_LIVE_EVIDENCE_CHECKPOINT_INVALID', 'Accepted evidence custody is invalid.');
1006
- }
1007
- const submission = record(ingestion.submission, 'Accepted submission');
1008
- return Object.freeze({
1009
- checkpointId: checkpoint.checkpointId,
1010
- state: checkpoint.state,
1011
- requestHash: checkpoint.requestHash,
1012
- artifactId: checkpoint.artifactId,
1013
- artifactHash: checkpoint.artifactHash,
1014
- evidenceRefId: submission.evidenceRefId,
1015
- ingestionId: ingestion.ingestionId,
1016
- replayed: true,
1017
- });
1018
- }
1019
- async #completeMaterialized(request, preparation, registration, baseEvidenceProvider, baseResolver, consent, checkpoint, bytes, executed, checkpointHistory = []) {
1020
- if (!bytes)
1021
- fail('E_LIVE_EVIDENCE_CHECKPOINT_INVALID', 'Materialized evidence bytes are unavailable.');
1022
- const ingestion = record(JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes)), 'Materialized ingestion');
1023
- const requestingCheckpoints = checkpointHistory.filter((candidate) => candidate.state === 'requesting');
1024
- if (requestingCheckpoints.length !== 1) {
1025
- fail('E_LIVE_EVIDENCE_CHECKPOINT_INVALID', 'Materialized evidence does not retain one exact requesting checkpoint.');
1026
- }
1027
- const requestingCheckpoint = requestingCheckpoints[0];
1028
- this.#contracts.assertOperatingLiveEvidenceIngestionV2(ingestion, {
1029
- liveProviderRegistration: registration,
1030
- baseEvidenceProvider,
1031
- baseResolver,
1032
- assignment: preparation.assignment,
1033
- consentRecord: consent,
1034
- connectorCheckpoint: requestingCheckpoint,
1035
- });
1036
- const result = await this.#operate.acceptLiveEvidenceSubmission({
1037
- assignmentId: request.assignmentId,
1038
- submissionId: request.submissionId,
1039
- actor: request.actor,
1040
- preparationHash: preparation.preparationHash,
1041
- contentBase64: Buffer.from(bytes).toString('base64'),
1042
- custody: {
1043
- issuedAssignment: preparation.assignment,
1044
- liveProviderRegistration: registration,
1045
- baseEvidenceProvider,
1046
- baseResolver,
1047
- consentRecord: consent,
1048
- connectorCheckpoint: requestingCheckpoint,
1049
- resultCheckpoint: checkpoint,
1050
- checkpointHistory: [],
1051
- },
1052
- });
1053
- executed.add('operate-assignment-submit');
1054
- executed.add('evidence-materialization');
1055
- const committedAt = new Date(this.#now()).toISOString();
1056
- const committed = await this.#checkpoints.transition({
1057
- checkpointId: String(checkpoint.checkpointId),
1058
- event: checkpointEvent(checkpoint, 'commit', committedAt, {
1059
- watermarkDigest: checkpoint.watermarkDigest,
1060
- }),
1061
- });
1062
- return ok('evidence.collect', {
1063
- checkpointId: committed.checkpoint.checkpointId,
1064
- state: committed.checkpoint.state,
1065
- requestHash: committed.checkpoint.requestHash,
1066
- artifactId: result.artifactId,
1067
- artifactHash: result.artifactHash,
1068
- evidenceRefId: result.evidenceRefId,
1069
- evidenceArtifactId: result.evidenceArtifactId,
1070
- resolutionId: result.resolutionId,
1071
- replayed: result.replayed,
1072
- }, executed, 'Use the accepted Evidence through normal Operate reads.');
1073
- }
1074
- }
1075
- export function createConnectorRuntimeV2(input) {
1076
- return new ConnectorRuntimeV2(input);
1077
- }
1078
- function privateAddress(address) {
1079
- if (address.includes(':')) {
1080
- const value = address.toLowerCase();
1081
- const dotted = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/u.exec(value)?.[1];
1082
- const words = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/u.exec(value);
1083
- if (dotted)
1084
- return privateAddress(dotted);
1085
- if (words) {
1086
- const high = Number.parseInt(words[1], 16);
1087
- const low = Number.parseInt(words[2], 16);
1088
- return privateAddress(`${high >>> 8}.${high & 0xff}.${low >>> 8}.${low & 0xff}`);
1089
- }
1090
- return (value === '::' ||
1091
- value === '::1' ||
1092
- value.startsWith('fc') ||
1093
- value.startsWith('fd') ||
1094
- /^fe[89ab]/u.test(value) ||
1095
- value.startsWith('ff'));
1096
- }
1097
- const parts = address.split('.').map(Number);
1098
- return (parts[0] === 0 ||
1099
- parts[0] === 10 ||
1100
- parts[0] === 127 ||
1101
- (parts[0] === 169 && parts[1] === 254) ||
1102
- (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
1103
- (parts[0] === 192 && parts[1] === 168) ||
1104
- (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127) ||
1105
- parts[0] >= 224);
1106
- }
1107
- async function assertTransportTarget(url, allowLoopback, resolveHost) {
1108
- const exactLoopback = url.hostname === '127.0.0.1' || url.hostname === '[::1]';
1109
- if (url.username ||
1110
- url.password ||
1111
- url.search ||
1112
- url.hash ||
1113
- (url.protocol !== 'https:' && !(allowLoopback && exactLoopback && url.protocol === 'http:'))) {
1114
- fail('E_LIVE_EVIDENCE_TRANSPORT_FORBIDDEN', 'Provider transport target is not allowed.');
1115
- }
1116
- const addresses = isIP(url.hostname.replace(/^\[|\]$/gu, ''))
1117
- ? [{ address: url.hostname.replace(/^\[|\]$/gu, '') }]
1118
- : await resolveHost(url.hostname);
1119
- if (addresses.length === 0 ||
1120
- addresses.some(({ address }) => privateAddress(address) && !(allowLoopback && exactLoopback))) {
1121
- fail('E_LIVE_EVIDENCE_TRANSPORT_FORBIDDEN', 'Provider address is not allowed.');
1122
- }
1123
- return Object.freeze(addresses.map(({ address }) => address.toLowerCase()).sort());
1124
- }
1125
- async function boundedResponseBytes(response, maxBytes) {
1126
- const declared = response.headers.get('content-length');
1127
- if (declared && Number(declared) > maxBytes) {
1128
- fail('E_LIVE_EVIDENCE_RESPONSE_TOO_LARGE', 'Provider response exceeds the exact byte ceiling.');
1129
- }
1130
- if (!response.body)
1131
- fail('E_LIVE_EVIDENCE_RESPONSE_INVALID', 'Provider response body is unavailable.');
1132
- const reader = response.body.getReader();
1133
- const chunks = [];
1134
- let total = 0;
1135
- try {
1136
- while (true) {
1137
- const { done, value } = await reader.read();
1138
- if (done)
1139
- break;
1140
- total += value.byteLength;
1141
- if (total > maxBytes) {
1142
- await reader.cancel();
1143
- fail('E_LIVE_EVIDENCE_RESPONSE_TOO_LARGE', 'Provider response exceeds the exact byte ceiling.');
1144
- }
1145
- chunks.push(value);
1146
- }
1147
- }
1148
- finally {
1149
- reader.releaseLock();
1150
- }
1151
- const output = new Uint8Array(total);
1152
- let offset = 0;
1153
- for (const chunk of chunks) {
1154
- output.set(chunk, offset);
1155
- offset += chunk.byteLength;
1156
- }
1157
- return output;
1158
- }
1159
- function csvPayload(text, collection, observedAt) {
1160
- const rows = text.trim().split(/\r?\n/u);
1161
- if (rows.length < 2)
1162
- fail('E_LIVE_EVIDENCE_RESPONSE_INVALID', 'CSV response is empty.');
1163
- const headings = rows[0].split(',').map((value) => value.trim());
1164
- if (headings.some((value) => !TEXT_ID.test(value))) {
1165
- fail('E_LIVE_EVIDENCE_RESPONSE_INVALID', 'CSV headings are invalid.');
1166
- }
1167
- return {
1168
- [collection]: rows.slice(1).map((row, index) => {
1169
- const values = row.split(',').map((value) => value.trim());
1170
- if (values.length !== headings.length) {
1171
- fail('E_LIVE_EVIDENCE_RESPONSE_INVALID', 'CSV row shape changed.');
1172
- }
1173
- return {
1174
- id: `row-${index + 1}`,
1175
- observedAt,
1176
- data: Object.fromEntries(headings.map((heading, position) => [heading, values[position]])),
1177
- };
1178
- }),
1179
- };
1180
- }
1181
- export function createBoundedFetchConnectorTransportV2(input) {
1182
- const policies = new Map(input.policies.map((policy) => [policy.providerId, policy]));
1183
- if (policies.size !== input.policies.length) {
1184
- fail('E_LIVE_EVIDENCE_TRANSPORT_CONFIG_INVALID', 'Provider transport policy is duplicated.');
1185
- }
1186
- const fetchImpl = input.fetchImpl ?? fetch;
1187
- const now = input.now ?? Date.now;
1188
- const resolveHost = input.resolveHost ??
1189
- (async (hostname) => lookup(hostname, { all: true, verbatim: true }));
1190
- return Object.freeze({
1191
- health({ request }) {
1192
- const policy = policies.get(request.provider.id);
1193
- if (!policy)
1194
- fail('E_LIVE_EVIDENCE_TRANSPORT_CONFIG_INVALID', 'Provider policy is unavailable.');
1195
- const checkedAt = new Date(now()).toISOString();
1196
- const freshUntil = new Date(now() + Math.min(policy.timeoutMs * 2, 300_000)).toISOString();
1197
- return Object.freeze({
1198
- status: 'available',
1199
- checkedAt,
1200
- freshUntil,
1201
- proofDigest: sha256CanonicalJson({
1202
- domain: 'openplanr-live-evidence-transport-health-v2',
1203
- provider: request.provider,
1204
- origin: policy.origin,
1205
- checkedAt,
1206
- freshUntil,
1207
- }),
1208
- });
1209
- },
1210
- async execute({ request, collection, credential, expectedHealth }) {
1211
- const policy = policies.get(request.provider.id);
1212
- if (!policy)
1213
- fail('E_LIVE_EVIDENCE_TRANSPORT_CONFIG_INVALID', 'Provider policy is unavailable.');
1214
- const pathname = policy.pathnameByOperation[request.operation];
1215
- if (typeof pathname !== 'string' || !pathname.startsWith('/') || pathname.includes('?')) {
1216
- fail('E_LIVE_EVIDENCE_TRANSPORT_CONFIG_INVALID', 'Provider operation path is unavailable.');
1217
- }
1218
- const origin = new URL(policy.origin);
1219
- const beforeAddresses = await assertTransportTarget(origin, policy.allowLoopback, resolveHost);
1220
- const target = new URL(pathname, origin);
1221
- if (target.origin !== origin.origin) {
1222
- fail('E_LIVE_EVIDENCE_TRANSPORT_FORBIDDEN', 'Provider operation escaped its allowed origin.');
1223
- }
1224
- const requestedAt = new Date(now()).toISOString();
1225
- const controller = new AbortController();
1226
- const timeout = setTimeout(() => controller.abort(), policy.timeoutMs);
1227
- let response;
1228
- try {
1229
- response = await fetchImpl(target, {
1230
- method: 'GET',
1231
- redirect: 'manual',
1232
- headers: {
1233
- accept: 'application/json, text/csv',
1234
- authorization: `${policy.authorizationScheme} ${Buffer.from(credential).toString('utf8')}`,
1235
- },
1236
- signal: controller.signal,
1237
- });
1238
- }
1239
- finally {
1240
- clearTimeout(timeout);
1241
- }
1242
- const afterAddresses = await assertTransportTarget(origin, policy.allowLoopback, resolveHost);
1243
- if (JSON.stringify(afterAddresses) !== JSON.stringify(beforeAddresses)) {
1244
- fail('E_LIVE_EVIDENCE_DNS_REBINDING', 'Provider address resolution changed during the bounded request.');
1245
- }
1246
- if (response.status >= 300 && response.status < 400) {
1247
- fail('E_LIVE_EVIDENCE_REDIRECT_FORBIDDEN', 'Provider redirects are not followed.');
1248
- }
1249
- const capturedAt = new Date(now()).toISOString();
1250
- const health = Object.freeze({
1251
- status: response.ok ? 'available' : 'unavailable',
1252
- checkedAt: capturedAt,
1253
- freshUntil: new Date(now() + 60_000).toISOString(),
1254
- proofDigest: sha256CanonicalJson({
1255
- domain: 'openplanr-live-evidence-http-health-v2',
1256
- provider: request.provider,
1257
- status: response.status,
1258
- capturedAt,
1259
- }),
1260
- });
1261
- if (!response.ok) {
1262
- return {
1263
- status: 'absent',
1264
- requestedAt,
1265
- capturedAt,
1266
- freshUntil: health.freshUntil,
1267
- health,
1268
- payload: {},
1269
- absence: {
1270
- kind: response.status === 429 ? 'rate-limited' : 'provider-unavailable',
1271
- reasonCode: response.status === 429 ? 'RATE_LIMITED' : 'PROVIDER_UNAVAILABLE',
1272
- required: true,
1273
- observedAt: capturedAt,
1274
- details: { statusClass: `${Math.floor(response.status / 100)}xx` },
1275
- },
1276
- };
1277
- }
1278
- const mediaType = response.headers.get('content-type')?.split(';', 1)[0]?.trim();
1279
- if (mediaType !== 'application/json' && mediaType !== 'text/csv') {
1280
- fail('E_LIVE_EVIDENCE_RESPONSE_INVALID', 'Provider response media type is unsupported.');
1281
- }
1282
- const bytes = await boundedResponseBytes(response, request.bounds.maxBytes);
1283
- const text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
1284
- let payload;
1285
- try {
1286
- payload =
1287
- mediaType === 'text/csv' ? csvPayload(text, collection, capturedAt) : JSON.parse(text);
1288
- }
1289
- catch (cause) {
1290
- if (cause instanceof ConnectorRuntimeErrorV2)
1291
- throw cause;
1292
- fail('E_LIVE_EVIDENCE_RESPONSE_INVALID', 'Provider response is invalid UTF-8 data.');
1293
- }
1294
- return {
1295
- status: 'materialized',
1296
- requestedAt,
1297
- capturedAt,
1298
- freshUntil: new Date(now() + 300_000).toISOString(),
1299
- health: expectedHealth,
1300
- payload,
1301
- };
1302
- },
1303
- });
1304
- }
1305
- //# sourceMappingURL=connector-runtime.js.map