@hraness/slopcamera 3.2.4

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 (441) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +60 -0
  3. package/PRIVACY.md +46 -0
  4. package/README.md +311 -0
  5. package/SECURITY.md +21 -0
  6. package/apps/desktop/README.md +966 -0
  7. package/apps/desktop/analysis/protocol.ts +223 -0
  8. package/apps/desktop/application/capability-binding.ts +594 -0
  9. package/apps/desktop/application/context.ts +97 -0
  10. package/apps/desktop/application/creative-iteration.ts +1385 -0
  11. package/apps/desktop/application/default-registry.ts +146 -0
  12. package/apps/desktop/application/directing-contract.ts +218 -0
  13. package/apps/desktop/application/directing-plan.ts +178 -0
  14. package/apps/desktop/application/errors.ts +18 -0
  15. package/apps/desktop/application/gateway-port.ts +867 -0
  16. package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
  17. package/apps/desktop/application/html-overlay-integrity.ts +337 -0
  18. package/apps/desktop/application/html-overlay-renderer.ts +45 -0
  19. package/apps/desktop/application/index.ts +13 -0
  20. package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
  21. package/apps/desktop/application/operation-effects.ts +94 -0
  22. package/apps/desktop/application/operation.ts +224 -0
  23. package/apps/desktop/application/operations/analysis/faces.ts +244 -0
  24. package/apps/desktop/application/operations/analysis/music.ts +248 -0
  25. package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
  26. package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
  27. package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
  28. package/apps/desktop/application/operations/analysis/shared.ts +202 -0
  29. package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
  30. package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
  31. package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
  32. package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
  33. package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
  34. package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
  35. package/apps/desktop/application/operations/gateway/image.ts +41 -0
  36. package/apps/desktop/application/operations/gateway/index.ts +17 -0
  37. package/apps/desktop/application/operations/gateway/shared.ts +122 -0
  38. package/apps/desktop/application/operations/gateway/speech.ts +41 -0
  39. package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
  40. package/apps/desktop/application/operations/gateway/video.ts +41 -0
  41. package/apps/desktop/application/operations/index.ts +31 -0
  42. package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
  43. package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
  44. package/apps/desktop/application/operations/iteration/select.ts +155 -0
  45. package/apps/desktop/application/operations/iteration/shared.ts +250 -0
  46. package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
  47. package/apps/desktop/application/operations/media/binding.ts +79 -0
  48. package/apps/desktop/application/operations/media/capabilities.ts +112 -0
  49. package/apps/desktop/application/operations/media/color-grade.ts +343 -0
  50. package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
  51. package/apps/desktop/application/operations/media/index.ts +8 -0
  52. package/apps/desktop/application/operations/media/ingest.ts +303 -0
  53. package/apps/desktop/application/operations/media/overlay.ts +729 -0
  54. package/apps/desktop/application/operations/media/shared.ts +845 -0
  55. package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
  56. package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
  57. package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
  58. package/apps/desktop/application/operations/project/snapshot.ts +95 -0
  59. package/apps/desktop/application/operations/recording/index.ts +5 -0
  60. package/apps/desktop/application/operations/recording/pause.ts +34 -0
  61. package/apps/desktop/application/operations/recording/resume.ts +34 -0
  62. package/apps/desktop/application/operations/recording/shared.ts +199 -0
  63. package/apps/desktop/application/operations/recording/start.ts +169 -0
  64. package/apps/desktop/application/operations/recording/stop.ts +34 -0
  65. package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
  66. package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
  67. package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
  68. package/apps/desktop/application/operations/render/project-plan.ts +939 -0
  69. package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
  70. package/apps/desktop/application/operations/render/project.ts +1922 -0
  71. package/apps/desktop/application/operations/shared.ts +67 -0
  72. package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
  73. package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
  74. package/apps/desktop/application/operations/spatial-project.ts +209 -0
  75. package/apps/desktop/application/operations/spatial-render.ts +415 -0
  76. package/apps/desktop/application/operations/spatial-scene.ts +97 -0
  77. package/apps/desktop/application/operations/studio.ts +143 -0
  78. package/apps/desktop/application/output-publication-lease.ts +163 -0
  79. package/apps/desktop/application/project-publication-lease.ts +233 -0
  80. package/apps/desktop/application/project-store.ts +196 -0
  81. package/apps/desktop/application/receipts.ts +533 -0
  82. package/apps/desktop/application/registry.ts +182 -0
  83. package/apps/desktop/application/spatial-assets.ts +675 -0
  84. package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
  85. package/apps/desktop/application/spatial-project-authority.ts +35 -0
  86. package/apps/desktop/application/spatial-project-lease.ts +126 -0
  87. package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
  88. package/apps/desktop/application/spatial-project-store.ts +286 -0
  89. package/apps/desktop/application/spatial-render-projection.ts +201 -0
  90. package/apps/desktop/application/spatial-render.ts +677 -0
  91. package/apps/desktop/application/spatial-spz.ts +44 -0
  92. package/apps/desktop/application/spatial-world-import.ts +135 -0
  93. package/apps/desktop/application/spatial-world-provenance.ts +46 -0
  94. package/apps/desktop/application/studio-port.ts +58 -0
  95. package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
  96. package/apps/desktop/capture/protocol.ts +689 -0
  97. package/apps/desktop/cli/analyzer.ts +406 -0
  98. package/apps/desktop/cli/args.ts +3181 -0
  99. package/apps/desktop/cli/asset-ingest.ts +747 -0
  100. package/apps/desktop/cli/atomic-render-effects.ts +210 -0
  101. package/apps/desktop/cli/atomic-render-platform.ts +205 -0
  102. package/apps/desktop/cli/atomic-render.ts +50 -0
  103. package/apps/desktop/cli/audio-analysis.ts +252 -0
  104. package/apps/desktop/cli/bounded-map.ts +39 -0
  105. package/apps/desktop/cli/bundle-service.ts +256 -0
  106. package/apps/desktop/cli/capabilities.ts +212 -0
  107. package/apps/desktop/cli/capture-bundle.ts +1117 -0
  108. package/apps/desktop/cli/command-host-resources.ts +251 -0
  109. package/apps/desktop/cli/commands.ts +6836 -0
  110. package/apps/desktop/cli/directing-blob.ts +291 -0
  111. package/apps/desktop/cli/directing-media.ts +517 -0
  112. package/apps/desktop/cli/directing-quote.ts +89 -0
  113. package/apps/desktop/cli/directing-service.ts +288 -0
  114. package/apps/desktop/cli/emoji-assets.ts +538 -0
  115. package/apps/desktop/cli/errors.ts +29 -0
  116. package/apps/desktop/cli/face-analysis-service.ts +940 -0
  117. package/apps/desktop/cli/filter-script.ts +76 -0
  118. package/apps/desktop/cli/gateway-application-port.ts +1142 -0
  119. package/apps/desktop/cli/gateway-credential.ts +81 -0
  120. package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
  121. package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
  122. package/apps/desktop/cli/gateway-media-service.ts +3106 -0
  123. package/apps/desktop/cli/gateway-media-signature.ts +122 -0
  124. package/apps/desktop/cli/gateway-provider-options.ts +562 -0
  125. package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
  126. package/apps/desktop/cli/help.ts +501 -0
  127. package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
  128. package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
  129. package/apps/desktop/cli/io.ts +290 -0
  130. package/apps/desktop/cli/main.ts +123 -0
  131. package/apps/desktop/cli/media-effects-service.ts +885 -0
  132. package/apps/desktop/cli/media-ingest-model.ts +364 -0
  133. package/apps/desktop/cli/media-ingest-platform.ts +227 -0
  134. package/apps/desktop/cli/media-ingest-program.ts +200 -0
  135. package/apps/desktop/cli/media-ingest.ts +30 -0
  136. package/apps/desktop/cli/music-analysis-service.ts +434 -0
  137. package/apps/desktop/cli/mutation-lock.ts +530 -0
  138. package/apps/desktop/cli/native-assets.d.ts +12 -0
  139. package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
  140. package/apps/desktop/cli/overlay-playback.ts +177 -0
  141. package/apps/desktop/cli/paths.ts +265 -0
  142. package/apps/desktop/cli/portable-surface.ts +242 -0
  143. package/apps/desktop/cli/project-camera-receipt.ts +112 -0
  144. package/apps/desktop/cli/project-face-camera.ts +394 -0
  145. package/apps/desktop/cli/project-inactivity-service.ts +552 -0
  146. package/apps/desktop/cli/project-media-integrity.ts +121 -0
  147. package/apps/desktop/cli/project-renderer.ts +1526 -0
  148. package/apps/desktop/cli/project-service.ts +519 -0
  149. package/apps/desktop/cli/project-state-transaction.ts +520 -0
  150. package/apps/desktop/cli/query-limits.ts +1 -0
  151. package/apps/desktop/cli/recording-controller.ts +1567 -0
  152. package/apps/desktop/cli/recording-daemon.ts +931 -0
  153. package/apps/desktop/cli/recording-ref.ts +56 -0
  154. package/apps/desktop/cli/renamed-environment.ts +6 -0
  155. package/apps/desktop/cli/renderer.ts +1439 -0
  156. package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
  157. package/apps/desktop/cli/scene-analysis-service.ts +879 -0
  158. package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
  159. package/apps/desktop/cli/spatial-project-service.ts +90 -0
  160. package/apps/desktop/cli/spatial-scene-service.ts +105 -0
  161. package/apps/desktop/cli/spatial-world-service.ts +39 -0
  162. package/apps/desktop/cli/speech-analysis-service.ts +682 -0
  163. package/apps/desktop/cli/studio-assemble.ts +58 -0
  164. package/apps/desktop/cli/studio-command.ts +60 -0
  165. package/apps/desktop/cli/studio-custody.ts +60 -0
  166. package/apps/desktop/cli/studio-encode.ts +216 -0
  167. package/apps/desktop/cli/studio-exr.ts +52 -0
  168. package/apps/desktop/cli/studio-files.ts +179 -0
  169. package/apps/desktop/cli/studio-output-validation.ts +162 -0
  170. package/apps/desktop/cli/studio-process.ts +145 -0
  171. package/apps/desktop/cli/studio-runtime.ts +77 -0
  172. package/apps/desktop/cli/studio-scaffold.ts +70 -0
  173. package/apps/desktop/cli/studio-service.ts +301 -0
  174. package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
  175. package/apps/desktop/cli/studio-template-names.ts +3 -0
  176. package/apps/desktop/cli/studio-workflow.ts +19 -0
  177. package/apps/desktop/cli/time.ts +25 -0
  178. package/apps/desktop/cli/workflow-code.ts +584 -0
  179. package/apps/desktop/cli/workflow-runs.ts +576 -0
  180. package/apps/desktop/code/advanced.ts +139 -0
  181. package/apps/desktop/code/application-node-planner.ts +1288 -0
  182. package/apps/desktop/code/compiler.ts +329 -0
  183. package/apps/desktop/code/contracts.ts +13 -0
  184. package/apps/desktop/code/creative-recipes.ts +94 -0
  185. package/apps/desktop/code/define-workflow.ts +115 -0
  186. package/apps/desktop/code/editing.ts +284 -0
  187. package/apps/desktop/code/file-candidate-provenance.ts +420 -0
  188. package/apps/desktop/code/file-candidate.ts +69 -0
  189. package/apps/desktop/code/graph-builder.ts +10 -0
  190. package/apps/desktop/code/host-resource-policy.ts +55 -0
  191. package/apps/desktop/code/plan-contracts.ts +159 -0
  192. package/apps/desktop/code/planning.ts +361 -0
  193. package/apps/desktop/code/public.ts +134 -0
  194. package/apps/desktop/code/reference-projection.ts +62 -0
  195. package/apps/desktop/code/run-contracts.ts +466 -0
  196. package/apps/desktop/code/run-store.ts +2478 -0
  197. package/apps/desktop/code/runtime-identity.ts +328 -0
  198. package/apps/desktop/code/scheduler.ts +3067 -0
  199. package/apps/desktop/code/semantic-builder.ts +1436 -0
  200. package/apps/desktop/code/source-bundle.ts +1641 -0
  201. package/apps/desktop/code/source-typecheck.ts +211 -0
  202. package/apps/desktop/code/testing.ts +79 -0
  203. package/apps/desktop/code/worker-client.ts +1675 -0
  204. package/apps/desktop/code/worker-effects.ts +141 -0
  205. package/apps/desktop/code/worker-entry.ts +435 -0
  206. package/apps/desktop/code/worker-lease-guardian.ts +179 -0
  207. package/apps/desktop/code/worker-process-identity.ts +484 -0
  208. package/apps/desktop/code/worker-protocol.ts +211 -0
  209. package/apps/desktop/code/workflow-effects.ts +132 -0
  210. package/apps/desktop/contracts/analysis.ts +949 -0
  211. package/apps/desktop/contracts/edit.ts +576 -0
  212. package/apps/desktop/contracts/index.ts +9 -0
  213. package/apps/desktop/contracts/media-effects.ts +139 -0
  214. package/apps/desktop/contracts/project-render.ts +644 -0
  215. package/apps/desktop/contracts/project.ts +740 -0
  216. package/apps/desktop/contracts/recording.ts +1434 -0
  217. package/apps/desktop/contracts/render.ts +334 -0
  218. package/apps/desktop/contracts/runtime.ts +155 -0
  219. package/apps/desktop/contracts/spatial-compositor.ts +58 -0
  220. package/apps/desktop/contracts/spatial-project.ts +159 -0
  221. package/apps/desktop/contracts/spatial-world.ts +48 -0
  222. package/apps/desktop/contracts/video-effects.ts +120 -0
  223. package/apps/desktop/core/alignment-apply.ts +400 -0
  224. package/apps/desktop/core/audio-alignment.ts +466 -0
  225. package/apps/desktop/core/auto-zoom.ts +209 -0
  226. package/apps/desktop/core/canonical-json.ts +14 -0
  227. package/apps/desktop/core/emoji.ts +210 -0
  228. package/apps/desktop/core/events.ts +409 -0
  229. package/apps/desktop/core/face-framing.ts +533 -0
  230. package/apps/desktop/core/face-tracks.ts +326 -0
  231. package/apps/desktop/core/inactivity.ts +137 -0
  232. package/apps/desktop/core/index.ts +30 -0
  233. package/apps/desktop/core/intervals.ts +123 -0
  234. package/apps/desktop/core/montage.ts +236 -0
  235. package/apps/desktop/core/music-analysis.ts +518 -0
  236. package/apps/desktop/core/plan.ts +244 -0
  237. package/apps/desktop/core/project-auto-zoom.ts +75 -0
  238. package/apps/desktop/core/project-camera-spatial.ts +676 -0
  239. package/apps/desktop/core/project-camera.ts +403 -0
  240. package/apps/desktop/core/project-captions.ts +888 -0
  241. package/apps/desktop/core/project-inactivity.ts +125 -0
  242. package/apps/desktop/core/project-metadata.ts +927 -0
  243. package/apps/desktop/core/project-plan.ts +375 -0
  244. package/apps/desktop/core/project-render-plan.ts +303 -0
  245. package/apps/desktop/core/project-time.ts +469 -0
  246. package/apps/desktop/core/render-plan.ts +442 -0
  247. package/apps/desktop/core/scene-cache.ts +174 -0
  248. package/apps/desktop/core/scene-sampling.ts +448 -0
  249. package/apps/desktop/core/spatial-compositor.ts +22 -0
  250. package/apps/desktop/core/spatial-durability.ts +99 -0
  251. package/apps/desktop/core/spatial-project.ts +169 -0
  252. package/apps/desktop/core/spatial-shot-clock.ts +32 -0
  253. package/apps/desktop/core/speech-analysis.ts +333 -0
  254. package/apps/desktop/core/storage.ts +970 -0
  255. package/apps/desktop/core/time-map.ts +91 -0
  256. package/apps/desktop/core/time.ts +61 -0
  257. package/apps/desktop/core/video-effects.ts +660 -0
  258. package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
  259. package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  260. package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  261. package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
  262. package/apps/desktop/dist/cli/main.js +5086 -0
  263. package/apps/desktop/html-overlay/catalog.ts +171 -0
  264. package/apps/desktop/html-overlay/contracts.ts +372 -0
  265. package/apps/desktop/html-overlay/execution-profile.ts +77 -0
  266. package/apps/desktop/html-overlay/index.ts +98 -0
  267. package/apps/desktop/html-overlay/libraries.ts +263 -0
  268. package/apps/desktop/html-overlay/random.ts +49 -0
  269. package/apps/desktop/html-overlay/runtime.ts +813 -0
  270. package/apps/desktop/html-overlay/scaffolds.ts +792 -0
  271. package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
  272. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
  273. package/apps/desktop/html-overlay/spatial.ts +720 -0
  274. package/apps/desktop/studio/assets/README.md +39 -0
  275. package/apps/desktop/studio/assets/contracts.ts +129 -0
  276. package/apps/desktop/studio/assets/network.ts +64 -0
  277. package/apps/desktop/studio/assets/poly-haven.ts +148 -0
  278. package/apps/desktop/studio/drivers/blender_driver.py +466 -0
  279. package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
  280. package/apps/desktop/studio/education/driver.py +308 -0
  281. package/apps/desktop/studio/education/lesson.py +180 -0
  282. package/apps/desktop/studio/education/toolkit.py +249 -0
  283. package/apps/desktop/workflows/chaptered-demo.ts +48 -0
  284. package/apps/desktop/workflows/creative-iteration.ts +107 -0
  285. package/apps/desktop/workflows/creative-selection.ts +92 -0
  286. package/apps/desktop/workflows/fragments.ts +486 -0
  287. package/apps/desktop/workflows/index.ts +93 -0
  288. package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
  289. package/apps/desktop/workflows/scene-direction.ts +12 -0
  290. package/apps/desktop/workflows/social-variants.ts +75 -0
  291. package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
  292. package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
  293. package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  294. package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  295. package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
  296. package/dist/cli.js +479 -0
  297. package/dist/code/advanced.js +270 -0
  298. package/dist/code/index.js +2240 -0
  299. package/dist/generate.js +24 -0
  300. package/dist/host-resources.js +26 -0
  301. package/dist/index-42zsesc1.js +2257 -0
  302. package/dist/index-7308egqr.js +48 -0
  303. package/dist/index-8txs6fkn.js +712 -0
  304. package/dist/index-mcy8z0br.js +192 -0
  305. package/dist/index-p63wavx0.js +2029 -0
  306. package/dist/index-r7gdhmsp.js +424 -0
  307. package/dist/index-sh6xbav6.js +1219 -0
  308. package/dist/index-z1w83f81.js +4 -0
  309. package/dist/index-z7239b4h.js +2363 -0
  310. package/dist/index-zfeg9hmw.js +2013 -0
  311. package/dist/index.js +241 -0
  312. package/dist/operations.js +29 -0
  313. package/dist/skill-install-dh0ntqba.js +10 -0
  314. package/dist/vectorize/worker.js +142 -0
  315. package/dist/workflow.js +16 -0
  316. package/docs/README.md +38 -0
  317. package/docs/studio.md +215 -0
  318. package/examples/studio/blender/README.md +26 -0
  319. package/examples/studio/blender/character.py +152 -0
  320. package/examples/studio/blender/cloth.py +101 -0
  321. package/examples/studio/blender/color_chart.py +29 -0
  322. package/examples/studio/blender/fluid.py +78 -0
  323. package/examples/studio/blender/import_model.py +74 -0
  324. package/examples/studio/blender/product.py +53 -0
  325. package/examples/studio/blender/shaded_street.py +481 -0
  326. package/examples/studio/blender/studio_scene.py +147 -0
  327. package/examples/studio/cadquery/README.md +13 -0
  328. package/examples/studio/cadquery/bracket.py +36 -0
  329. package/examples/studio/cadquery/import_step.py +12 -0
  330. package/examples/studio/education/README.md +46 -0
  331. package/examples/studio/education/lesson.json +99 -0
  332. package/examples/studio/education/scene.py +7 -0
  333. package/examples/studio/hybrid-scene.ts +96 -0
  334. package/examples/studio/native-workflow.ts +14 -0
  335. package/examples/studio/vgpu/README.md +64 -0
  336. package/examples/studio/vgpu/heat-field.mjs +290 -0
  337. package/package.json +304 -0
  338. package/packages/scene/src/index.ts +1 -0
  339. package/packages/scene/src/scene.ts +215 -0
  340. package/schema/diagram.schema.json +334 -0
  341. package/skills/slopcamera/SKILL.md +45 -0
  342. package/skills/slopcamera/agents/openai.yaml +4 -0
  343. package/skills/slopcamera/references/customization.md +77 -0
  344. package/skills/slopcamera/references/diagrams.md +153 -0
  345. package/skills/slopcamera/references/directed-scenes.md +42 -0
  346. package/skills/slopcamera/references/directing-video.md +18 -0
  347. package/skills/slopcamera/references/educational-video.md +26 -0
  348. package/skills/slopcamera/references/gateway-media.md +134 -0
  349. package/skills/slopcamera/references/install.md +62 -0
  350. package/skills/slopcamera/references/native-studio.md +31 -0
  351. package/skills/slopcamera/references/reference-led-3d.md +140 -0
  352. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
  353. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
  354. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
  355. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
  356. package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
  357. package/skills/slopcamera/references/vectorization.md +29 -0
  358. package/skills/slopcamera/references/video-projects.md +187 -0
  359. package/skills/slopcamera/references/visual-communication.md +120 -0
  360. package/skills/slopcamera/references/workflows-sdk.md +45 -0
  361. package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
  362. package/src/artifacts.ts +106 -0
  363. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  364. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  365. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  366. package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  367. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  368. package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
  369. package/src/assets.d.ts +9 -0
  370. package/src/cli.ts +601 -0
  371. package/src/cloud-errors.ts +26 -0
  372. package/src/code/advanced.ts +10 -0
  373. package/src/code/boundary.ts +21 -0
  374. package/src/code/canonical-json.ts +129 -0
  375. package/src/code/compiler.ts +953 -0
  376. package/src/code/contracts.ts +576 -0
  377. package/src/code/define-workflow.ts +334 -0
  378. package/src/code/errors.ts +41 -0
  379. package/src/code/graph-builder.ts +945 -0
  380. package/src/code/index.ts +121 -0
  381. package/src/code/json-snapshot.ts +899 -0
  382. package/src/code/json-utf8.ts +88 -0
  383. package/src/code/portable-builder.ts +136 -0
  384. package/src/code/projection.ts +378 -0
  385. package/src/code/public-operations.ts +450 -0
  386. package/src/code/runtime.ts +580 -0
  387. package/src/code/sha256.ts +36 -0
  388. package/src/config.ts +180 -0
  389. package/src/desktop.ts +253 -0
  390. package/src/fs.ts +10 -0
  391. package/src/generate.ts +704 -0
  392. package/src/host-resource-posix.ts +161 -0
  393. package/src/host-resources.ts +1749 -0
  394. package/src/icons.ts +58 -0
  395. package/src/index.ts +200 -0
  396. package/src/label-layout.ts +130 -0
  397. package/src/layout.ts +413 -0
  398. package/src/lint.ts +127 -0
  399. package/src/mcp/boundary.ts +354 -0
  400. package/src/mcp/index.ts +33 -0
  401. package/src/mcp/server.ts +272 -0
  402. package/src/mcp/tools.ts +963 -0
  403. package/src/mcp/types.ts +71 -0
  404. package/src/operations.ts +775 -0
  405. package/src/parse.ts +861 -0
  406. package/src/process-environment.ts +17 -0
  407. package/src/render.ts +437 -0
  408. package/src/skill-install.ts +49 -0
  409. package/src/spatial-scene/authoring.ts +27 -0
  410. package/src/spatial-scene/camera-track.ts +83 -0
  411. package/src/spatial-scene/contracts.ts +277 -0
  412. package/src/spatial-scene/evaluate.ts +145 -0
  413. package/src/spatial-scene/gltf.ts +558 -0
  414. package/src/spatial-scene/identity.ts +260 -0
  415. package/src/spatial-scene/index.ts +10 -0
  416. package/src/spatial-scene/inspect.ts +68 -0
  417. package/src/spatial-scene/math.ts +280 -0
  418. package/src/spatial-scene/patch.ts +131 -0
  419. package/src/spatial-scene/time.ts +53 -0
  420. package/src/studio/contracts.ts +129 -0
  421. package/src/studio/index.ts +2 -0
  422. package/src/studio/plan.ts +156 -0
  423. package/src/studio/shared.ts +41 -0
  424. package/src/theme.ts +68 -0
  425. package/src/tldr.ts +474 -0
  426. package/src/types.ts +212 -0
  427. package/src/vectorize/archive.ts +101 -0
  428. package/src/vectorize/command.ts +797 -0
  429. package/src/vectorize/index.ts +27 -0
  430. package/src/vectorize/limits.ts +61 -0
  431. package/src/vectorize/metrics.ts +351 -0
  432. package/src/vectorize/pixels.ts +323 -0
  433. package/src/vectorize/supervisor.ts +339 -0
  434. package/src/vectorize/svg.ts +188 -0
  435. package/src/vectorize/tool.ts +542 -0
  436. package/src/vectorize/types.ts +113 -0
  437. package/src/vectorize/vectorize.ts +634 -0
  438. package/src/vectorize/worker-protocol.ts +40 -0
  439. package/src/vectorize/worker.ts +209 -0
  440. package/src/version.ts +1 -0
  441. package/src/workflow.ts +409 -0
@@ -0,0 +1,677 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { lstat, mkdir, mkdtemp, open, readdir, realpath, rm } from "node:fs/promises";
4
+ import { dirname, join, relative } from "node:path";
5
+
6
+ import sharp from "sharp";
7
+ import { z } from "zod";
8
+
9
+ import { createBoundedJsonSnapshot, deepFreezeJson } from "../../../src/code/json-snapshot";
10
+ import {
11
+ SpatialAssetIdSchema, SpatialCameraIdSchema, SpatialDigestSchema, SpatialFrameRateSchema, SpatialOverrideSchema, SpatialPoseSchema,
12
+ SpatialTimeUsSchema, SpatialShotV1Schema, type SpatialSceneV1,
13
+ } from "../../../src/spatial-scene/contracts";
14
+ import { evaluateSpatialScene } from "../../../src/spatial-scene/evaluate";
15
+ import { parseSpatialScene, parseSpatialValue, spatialAssetClosureDigests, spatialSceneSha256 } from "../../../src/spatial-scene/identity";
16
+ import { reduceSpatialFrameRate, spatialFrameCount, spatialFrameSample, spatialOutputDuration, type SpatialRational } from "../../../src/spatial-scene/time";
17
+ import { canonicalJson, canonicalJsonSha256 } from "../core/canonical-json";
18
+ import { createNodeSpatialDurability, type SpatialDurabilityPort } from "../core/spatial-durability";
19
+ import { SpatialShotRenderClockSchema, spatialShotSceneTime } from "../core/spatial-shot-clock";
20
+ import { createSpatialOverlayBatch, SpatialRenderModeSchema, SPATIAL_OVERLAY_LIMITS, type SpatialOverlayBatchInput, type PreparedSpatialAsset } from "../html-overlay/spatial";
21
+ import { HTML_OVERLAY_MAX_HTML_BYTES } from "../html-overlay/contracts";
22
+ import { assertHtmlOverlayGpuEvidenceProfile, HtmlOverlayExecutionProfileSchema, HtmlOverlayGpuEvidenceSchema, type HtmlOverlayExecutionProfile, type HtmlOverlayGpuEvidence } from "../html-overlay/execution-profile";
23
+ import { exactCapabilityByName } from "./capability-binding";
24
+ import { ApplicationError } from "./errors";
25
+ import { bindHtmlOverlayBrowserRuntime, HtmlOverlayBrowserRuntimeBindingSchema, type HtmlOverlayBrowserRuntimeBinding } from "./html-overlay-browser-runtime";
26
+ import { createHtmlOverlayExecutionBundle, HtmlOverlayExecutionIntegritySchema } from "./html-overlay-integrity";
27
+ import type { OperationExecutionContext } from "./operation";
28
+ import { assertMediaCapabilities, bindExpectedMediaCapabilities, MediaCapabilityBindingsSchema, mediaCapabilityCommand, mediaCapabilityRunner, type MediaCapabilityName } from "./operations/media/capabilities";
29
+ import { AbortBoundApplicationRunner, createMediaOperationWorkspace, MediaArtifactReferenceSchema, publishContentAddressedMedia, publishContentAddressedReceipt, type MediaArtifactReference } from "./operations/media/shared";
30
+ import { throwIfAborted } from "./operations/shared";
31
+ import { withPreparedSpatialAssets } from "./spatial-assets";
32
+
33
+ /** Conservative admission limits, including an uncompressed worst-case staging estimate. */
34
+ export const SPATIAL_RENDER_LIMITS = Object.freeze({
35
+ frames: 1_800, contactSheetFrames: 64, batchFrames: 32, dimension: 8_192,
36
+ framePixels: 33_554_432, totalPixels: 1_100_000_000, entitySamples: 262_144,
37
+ sourceBytes: 256 * 1024 * 1024, pngBytes: 6 * 1024 * 1024 * 1024,
38
+ outputBytes: 256 * 1024 * 1024, metadataBytes: 32 * 1024 * 1024,
39
+ stagingBytes: 8 * 1024 * 1024 * 1024, probeBytes: 2 * 1024 * 1024,
40
+ nativeTimeoutMs: 120_000,
41
+ });
42
+
43
+ export function spatialBatchSerializationLimit(error: unknown): boolean {
44
+ if (error instanceof z.ZodError) return error.issues.length > 0 && error.issues.every(issue => issue.path.length === 1 && issue.path[0] === "html"
45
+ && (issue.code === "too_big" && issue.maximum === HTML_OVERLAY_MAX_HTML_BYTES
46
+ || issue.code === "custom" && issue.message === `HTML overlay documents may not exceed ${HTML_OVERLAY_MAX_HTML_BYTES} UTF-8 bytes.`));
47
+ return error instanceof Error && ["Spatial overlay request", "Spatial overlay metadata"].some(name => error.message === `${name} contains more than ${SPATIAL_OVERLAY_LIMITS.requestBytes} bytes.`);
48
+ }
49
+
50
+ /** Prepare once; partition only pure serialization admission, never renderer failures. */
51
+ export function partitionSpatialRenderWindow(input: SpatialOverlayBatchInput) {
52
+ type Partition = { readonly offset: number; readonly length: number; readonly preparedAssets: readonly PreparedSpatialAsset[]; readonly batch: ReturnType<typeof createSpatialOverlayBatch> };
53
+ const partitions: Partition[] = [];
54
+ const visit = (offset: number, length: number): void => {
55
+ const snapshots = input.snapshots.slice(offset, offset + length);
56
+ const times = new Set(snapshots.map(snapshot => snapshot.timeUs));
57
+ const preparedAssets = input.executionProfile === undefined ? input.preparedAssets ?? []
58
+ : (input.preparedAssets ?? []).filter(asset => asset.kind === "splat" || asset.timeUs === null || times.has(asset.timeUs));
59
+ try {
60
+ const batch = createSpatialOverlayBatch({ ...input, snapshots, preparedAssets });
61
+ partitions.push({ offset, length, preparedAssets, batch });
62
+ } catch (error) {
63
+ if (input.executionProfile === undefined || length <= 1 || !spatialBatchSerializationLimit(error)) throw error;
64
+ const left = Math.floor(length / 2);
65
+ visit(offset, left);
66
+ visit(offset + left, length - left);
67
+ }
68
+ };
69
+ visit(0, input.snapshots.length);
70
+ return partitions;
71
+ }
72
+
73
+ export const SpatialRenderRequestSchema = z.strictObject({
74
+ executionProfile: HtmlOverlayExecutionProfileSchema.optional(),
75
+ cameraId: SpatialCameraIdSchema,
76
+ overrides: z.array(SpatialOverrideSchema).max(4_096).optional(),
77
+ cameraPoseOverride: SpatialPoseSchema.optional(),
78
+ selection: z.discriminatedUnion("kind", [
79
+ z.strictObject({ kind: z.literal("frame"), timeUs: SpatialTimeUsSchema }),
80
+ z.strictObject({ kind: z.literal("video"),
81
+ range: z.strictObject({ startUs: SpatialTimeUsSchema, endUs: SpatialTimeUsSchema }),
82
+ frameRate: SpatialFrameRateSchema.overwrite(reduceSpatialFrameRate),
83
+ clock: SpatialShotRenderClockSchema.optional(),
84
+ }),
85
+ z.strictObject({ kind: z.literal("contact-sheet"),
86
+ timesUs: z.array(SpatialTimeUsSchema).min(1).max(SPATIAL_RENDER_LIMITS.contactSheetFrames),
87
+ columns: z.number().int().min(1).max(16),
88
+ cellWidth: z.number().int().min(1).max(2_048),
89
+ cellHeight: z.number().int().min(1).max(2_048),
90
+ /** Resampling is a presentation operation after calibrated scene rendering. */
91
+ fit: z.literal("contain").default("contain"),
92
+ }),
93
+ ]),
94
+ mode: SpatialRenderModeSchema,
95
+ });
96
+ export type SpatialRenderRequest = z.infer<typeof SpatialRenderRequestSchema>;
97
+
98
+ /** One canonical request identity shared by V2 projection production and receipt verification. */
99
+ export function spatialShotRenderRequest(shotInput: unknown, frameRateInput: unknown, executionProfile?: HtmlOverlayExecutionProfile): SpatialRenderRequest {
100
+ const shot = parseSpatialValue(SpatialShotV1Schema, shotInput, "spatial shot");
101
+ const frameRate = reduceSpatialFrameRate(frameRateInput);
102
+ return deepFreezeJson(SpatialRenderRequestSchema.parse({ cameraId: shot.cameraId, overrides: shot.overrides,
103
+ ...(executionProfile === undefined ? {} : { executionProfile }),
104
+ ...(shot.cameraPoseOverride === undefined ? {} : { cameraPoseOverride: shot.cameraPoseOverride }),
105
+ mode: { kind: "beauty" }, selection: { kind: "video", frameRate,
106
+ range: { startUs: 0, endUs: shot.range.endUs - shot.range.startUs },
107
+ clock: { kind: "shot", sceneStartUs: shot.sceneStartUs, playback: shot.playback },
108
+ },
109
+ }));
110
+ }
111
+ export interface SpatialRenderSample {
112
+ readonly index: number;
113
+ readonly timeUs: number;
114
+ readonly exactTimeUs: SpatialRational;
115
+ }
116
+ export interface SpatialRenderPlan {
117
+ readonly scene: SpatialSceneV1;
118
+ readonly sceneSha256: string;
119
+ readonly request: SpatialRenderRequest;
120
+ readonly requestSha256: string;
121
+ readonly samples: readonly SpatialRenderSample[];
122
+ readonly width: number;
123
+ readonly height: number;
124
+ readonly outputWidth: number;
125
+ readonly outputHeight: number;
126
+ readonly outputDurationUs?: SpatialRational;
127
+ readonly costs: { readonly sourceBytes: number; readonly renderPixels: number; readonly pngBytesBound: number; readonly outputBytesBound: number; readonly stagingBytesBound: number };
128
+ }
129
+
130
+ function rational(numerator: bigint, denominator: bigint): SpatialRational {
131
+ let a = numerator, b = denominator;
132
+ while (b !== 0n) { const rest = a % b; a = b; b = rest; }
133
+ return { numerator: String(numerator / a), denominator: String(denominator / a) };
134
+ }
135
+ function checked(condition: boolean, message: string): asserts condition {
136
+ if (!condition) throw new ApplicationError("invalid-data", message);
137
+ }
138
+ const sha256 = (bytes: Uint8Array): string => createHash("sha256").update(bytes).digest("hex");
139
+
140
+ /** Source-clock samples are computed independently in BigInt and quantized exactly once. */
141
+ export function planSpatialRender(sceneInput: unknown, requestInput: unknown): SpatialRenderPlan {
142
+ const scene = parseSpatialScene(sceneInput);
143
+ const request = parseSpatialValue(SpatialRenderRequestSchema, requestInput, "spatial render request");
144
+ checked(!scene.entities.some(entity => entity.kind === "splat") || request.executionProfile === "three-spark-webgl2-hardware-v1", "Splat rendering requires the explicit Three/Spark hardware profile.");
145
+ checked(request.executionProfile !== "three-spark-webgl2-hardware-v1" || request.mode.kind === "beauty", "The Three/Spark hardware profile supports beauty rendering only; splat selection and depth semantics are not qualified.");
146
+ const camera = scene.cameras.find(item => item.cameraId === request.cameraId);
147
+ checked(camera !== undefined, "Spatial render camera is absent from the scene.");
148
+ const { width, height } = camera.projection;
149
+ checked(width <= SPATIAL_RENDER_LIMITS.dimension && height <= SPATIAL_RENDER_LIMITS.dimension, "Spatial render dimensions exceed the renderer profile.");
150
+ const selection = request.selection;
151
+ let samples: SpatialRenderSample[], outputDurationUs: SpatialRational | undefined;
152
+ if (selection.kind === "video") {
153
+ checked(selection.range.endUs > selection.range.startUs, "Video range must be nonempty.");
154
+ if (selection.clock === undefined) checked(selection.range.endUs <= scene.durationUs, "Video range must be contained in the scene clock.");
155
+ else {
156
+ checked(selection.range.startUs === 0, "Shot video range must begin at relative time zero.");
157
+ checked(selection.clock.sceneStartUs < scene.durationUs, "Shot scene start must precede scene duration.");
158
+ if (selection.clock.playback === "once") checked(selection.clock.sceneStartUs + selection.range.endUs <= scene.durationUs, "Once shot duration exceeds the scene clock.");
159
+ }
160
+ const duration = selection.range.endUs - selection.range.startUs;
161
+ const count = spatialFrameCount(duration, selection.frameRate);
162
+ checked(count <= SPATIAL_RENDER_LIMITS.frames, "Spatial video exceeds the frame-count limit.");
163
+ samples = Array.from({ length: count }, (_, index) => {
164
+ const relativeSample = spatialFrameSample(index, duration, selection.frameRate);
165
+ if (selection.clock !== undefined) return { index, ...spatialShotSceneTime({ clock: selection.clock, relativeTimeUs: relativeSample.exactTimeUs, sceneDurationUs: scene.durationUs }) };
166
+ const denominator = BigInt(relativeSample.exactTimeUs.denominator);
167
+ const numerator = BigInt(selection.range.startUs) * denominator + BigInt(relativeSample.exactTimeUs.numerator);
168
+ return { index, exactTimeUs: rational(numerator, denominator), timeUs: Number((2n * numerator + denominator) / (2n * denominator)) };
169
+ });
170
+ outputDurationUs = spatialOutputDuration(duration, selection.frameRate);
171
+ } else {
172
+ const times = selection.kind === "frame" ? [selection.timeUs] : selection.timesUs;
173
+ checked(times.every(time => time <= scene.durationUs), "A selected frame is outside the scene clock.");
174
+ samples = times.map((timeUs, index) => ({ index, timeUs, exactTimeUs: { numerator: String(timeUs), denominator: "1" } }));
175
+ }
176
+ const outputWidth = selection.kind === "contact-sheet" ? selection.columns * selection.cellWidth : width;
177
+ const outputHeight = selection.kind === "contact-sheet" ? Math.ceil(samples.length / selection.columns) * selection.cellHeight : height;
178
+ checked(outputWidth <= SPATIAL_RENDER_LIMITS.dimension && outputHeight <= SPATIAL_RENDER_LIMITS.dimension
179
+ && outputWidth * outputHeight <= SPATIAL_RENDER_LIMITS.framePixels, "Spatial output exceeds its dimension or pixel budget.");
180
+ checked(scene.entities.length * samples.length <= SPATIAL_RENDER_LIMITS.entitySamples, "Spatial render exceeds its evaluated entity-sample budget.");
181
+ const sourceBytes = scene.assets.reduce((sum, asset) => sum + asset.payload.bytes, 0);
182
+ const renderPixels = width * height * samples.length;
183
+ // PNG deflate overhead and qtrle literal overhead are covered before native execution.
184
+ const pngBytesBound = (width * height * 5 + height * 8 + 65_536) * samples.length;
185
+ // Video compression is content-dependent. This is an enforced output ceiling,
186
+ // not a promise that every admitted scene compresses below it. A truncated
187
+ // encode cannot publish: complete encoded count/timestamps are verified.
188
+ const outputBytesBound = selection.kind === "video" ? Math.min(SPATIAL_RENDER_LIMITS.outputBytes, pngBytesBound + 1024 * 1024) : outputWidth * outputHeight * 5 + outputHeight * 8 + 65_536;
189
+ const stagingBytesBound = sourceBytes + pngBytesBound + outputBytesBound + SPATIAL_RENDER_LIMITS.metadataBytes;
190
+ checked(sourceBytes <= SPATIAL_RENDER_LIMITS.sourceBytes, "Spatial source closure exceeds its byte budget.");
191
+ checked(renderPixels <= SPATIAL_RENDER_LIMITS.totalPixels && pngBytesBound <= SPATIAL_RENDER_LIMITS.pngBytes, "Spatial frame staging exceeds its pixel or byte budget; reduce resolution or range.");
192
+ checked(outputBytesBound <= SPATIAL_RENDER_LIMITS.outputBytes && stagingBytesBound <= SPATIAL_RENDER_LIMITS.stagingBytes, "Spatial output exceeds its conservative byte budget.");
193
+ // Semantic override validation is pure and precedes capability or resource admission.
194
+ evaluateSpatialScene(scene, { cameraId: request.cameraId, timeUs: samples[0]!.timeUs,
195
+ ...(request.overrides === undefined ? {} : { overrides: request.overrides }),
196
+ ...(request.cameraPoseOverride === undefined ? {} : { cameraPoseOverride: request.cameraPoseOverride }),
197
+ });
198
+ return deepFreezeJson({ scene, sceneSha256: spatialSceneSha256(scene), request, requestSha256: canonicalJsonSha256(request), samples,
199
+ width, height, outputWidth, outputHeight, ...(outputDurationUs === undefined ? {} : { outputDurationUs }),
200
+ costs: { sourceBytes, renderPixels, pngBytesBound, outputBytesBound, stagingBytesBound },
201
+ });
202
+ }
203
+
204
+ /** The single capability selection rule shared by exact planning and execution. */
205
+ export function spatialRenderCapabilityNames(plan: SpatialRenderPlan): readonly MediaCapabilityName[] {
206
+ return Object.freeze(plan.request.selection.kind === "video" || plan.scene.assets.some(asset => asset.interpretation.kind === "video")
207
+ ? ["ffmpeg", "ffprobe", "html-browser"] as const : ["html-browser"] as const);
208
+ }
209
+
210
+ export interface SpatialEncodedVideoEvidence {
211
+ readonly codec: "qtrle";
212
+ readonly container: "mov";
213
+ readonly pixelFormat: "argb";
214
+ readonly alpha: "straight";
215
+ readonly colorSpace: "srgb" | "rgba8-data";
216
+ readonly streamIndex: number;
217
+ readonly frameRate: { readonly numerator: number; readonly denominator: number };
218
+ readonly frameCount: number;
219
+ readonly timeBase: SpatialRational;
220
+ readonly firstPts: string;
221
+ readonly lastPts: string;
222
+ readonly endPts: string;
223
+ readonly outputDurationUs: SpatialRational;
224
+ }
225
+ export interface SpatialRetainedAsset {
226
+ readonly assetId: string;
227
+ readonly manifestSha256: string;
228
+ readonly originalPath: string;
229
+ readonly artifact: MediaArtifactReference;
230
+ }
231
+ export interface SpatialRenderResult {
232
+ readonly artifact: MediaArtifactReference;
233
+ readonly receipt: MediaArtifactReference;
234
+ readonly sceneSource: MediaArtifactReference;
235
+ readonly retainedAssets: readonly SpatialRetainedAsset[];
236
+ readonly render: {
237
+ readonly kind: SpatialRenderRequest["selection"]["kind"];
238
+ readonly frameCount: number;
239
+ readonly width: number;
240
+ readonly height: number;
241
+ readonly frameRate?: { readonly numerator: number; readonly denominator: number } | undefined;
242
+ readonly encodedEvidence?: SpatialEncodedVideoEvidence | undefined;
243
+ };
244
+ }
245
+ const rationalSchema = z.strictObject({ numerator: z.string().regex(/^(?:0|[1-9]\d{0,23})$/u), denominator: z.string().regex(/^[1-9]\d{0,12}$/u) });
246
+ const countSchema = z.number().int().min(1).max(SPATIAL_RENDER_LIMITS.frames);
247
+ const pixelDimensionSchema = z.number().int().min(1).max(SPATIAL_RENDER_LIMITS.dimension);
248
+ const byteCountSchema = z.number().int().safe().min(0).max(SPATIAL_RENDER_LIMITS.stagingBytes);
249
+ const encodedEvidenceSchema = z.strictObject({
250
+ codec: z.literal("qtrle"), container: z.literal("mov"), pixelFormat: z.literal("argb"), alpha: z.literal("straight"),
251
+ colorSpace: z.enum(["srgb", "rgba8-data"]), streamIndex: z.number().int().min(0),
252
+ frameRate: SpatialFrameRateSchema, frameCount: countSchema, timeBase: rationalSchema,
253
+ firstPts: z.string().regex(/^0$/u), lastPts: z.string().regex(/^\d{1,24}$/u), endPts: z.string().regex(/^\d{1,24}$/u), outputDurationUs: rationalSchema,
254
+ });
255
+ const retainedAssetSchema = z.strictObject({ assetId: SpatialAssetIdSchema, manifestSha256: SpatialDigestSchema,
256
+ originalPath: z.string().min(1).max(1_024), artifact: MediaArtifactReferenceSchema });
257
+ const renderSummarySchema = z.strictObject({ kind: z.enum(["frame", "video", "contact-sheet"]), frameCount: countSchema,
258
+ width: pixelDimensionSchema, height: pixelDimensionSchema, frameRate: SpatialFrameRateSchema.optional(), encodedEvidence: encodedEvidenceSchema.optional(),
259
+ }).superRefine((value, context) => {
260
+ if (value.kind === "video") {
261
+ if (value.frameRate === undefined || value.encodedEvidence === undefined || value.encodedEvidence.frameCount !== value.frameCount
262
+ || canonicalJson(value.frameRate) !== canonicalJson(value.encodedEvidence.frameRate)) context.addIssue({ code: "custom", message: "Video summary requires matching encoded rate and frame-count evidence." });
263
+ } else if (value.frameRate !== undefined || value.encodedEvidence !== undefined || value.kind === "frame" && value.frameCount !== 1) {
264
+ context.addIssue({ code: "custom", message: "Raster summary must not claim encoded-video evidence." });
265
+ }
266
+ });
267
+ export const SpatialRenderOutputSchema = z.strictObject({ artifact: MediaArtifactReferenceSchema, receipt: MediaArtifactReferenceSchema,
268
+ sceneSource: MediaArtifactReferenceSchema, retainedAssets: z.array(retainedAssetSchema).max(128), render: renderSummarySchema });
269
+ export type SpatialRenderOutput = z.infer<typeof SpatialRenderOutputSchema>;
270
+ export const SpatialRenderReceiptSchema = z.strictObject({
271
+ kind: z.literal("slopcamera.spatial-render-receipt"), schemaVersion: z.literal(1), attemptId: z.string().uuid(), sceneSha256: SpatialDigestSchema,
272
+ source: z.strictObject({ canonicalScene: MediaArtifactReferenceSchema, canonicalization: z.literal("parsed-spatial-scene-v1"),
273
+ originalSceneArtifact: MediaArtifactReferenceSchema.optional(), retainedAssets: z.array(retainedAssetSchema).max(128), sourceManifests: z.record(SpatialAssetIdSchema, SpatialDigestSchema) }),
274
+ request: SpatialRenderRequestSchema, requestSha256: SpatialDigestSchema,
275
+ samples: z.array(z.strictObject({ sample: z.strictObject({ index: z.number().int().min(0).max(SPATIAL_RENDER_LIMITS.frames - 1), timeUs: SpatialTimeUsSchema, exactTimeUs: rationalSchema }),
276
+ stateSha256: SpatialDigestSchema, viewSha256: SpatialDigestSchema, pngSha256: SpatialDigestSchema, pngBytes: byteCountSchema })).min(1).max(SPATIAL_RENDER_LIMITS.frames),
277
+ render: renderSummarySchema, output: MediaArtifactReferenceSchema,
278
+ batches: z.array(MediaArtifactReferenceSchema).min(1).max(SPATIAL_RENDER_LIMITS.frames),
279
+ frameArtifacts: z.array(MediaArtifactReferenceSchema).max(SPATIAL_RENDER_LIMITS.contactSheetFrames),
280
+ runtime: z.strictObject({ artifact: MediaArtifactReferenceSchema, rootSha256: SpatialDigestSchema, capabilities: MediaCapabilityBindingsSchema,
281
+ gpuEvidence: HtmlOverlayGpuEvidenceSchema.optional(),
282
+ renderer: z.literal("three-webgl2-snapshot-v1"), threeVersion: z.literal("0.185.1") }),
283
+ color: z.strictObject({ output: z.enum(["srgb", "rgba8-data"]), alpha: z.enum(["straight", "binary-validity"]), toneMapping: z.literal("none") }),
284
+ timing: z.strictObject({ sourceClock: z.literal("absolute-rational-microseconds"), quantization: z.literal("independent-nearest-microsecond-ties-positive"),
285
+ encodedClock: z.literal("zero-based-uniform-frame-intervals"), outputDurationUs: rationalSchema.optional() }),
286
+ calibratedSourceDimensions: z.strictObject({ width: pixelDimensionSchema, height: pixelDimensionSchema }),
287
+ contactSheet: z.strictObject({ fit: z.literal("contain"), resampling: z.enum(["lanczos3", "nearest-data"]), calibratedProjectionResized: z.literal(false) }).optional(),
288
+ encodedProbe: MediaArtifactReferenceSchema.optional(),
289
+ workflow: z.strictObject({ nodeKey: z.string().min(1).max(255), nodePlanSha256: SpatialDigestSchema, runId: z.string().min(1).max(128) }).optional(),
290
+ costs: z.strictObject({ sourceBytes: byteCountSchema, renderPixels: z.number().int().min(1).max(SPATIAL_RENDER_LIMITS.totalPixels), pngBytesBound: byteCountSchema,
291
+ outputBytesBound: byteCountSchema, stagingBytesBound: byteCountSchema, actualPngBytes: byteCountSchema, actualBatchMetadataBytes: byteCountSchema }),
292
+ }).superRefine((receipt, context) => {
293
+ if (receipt.batches.length > (receipt.request.executionProfile === undefined ? Math.ceil(SPATIAL_RENDER_LIMITS.frames / SPATIAL_RENDER_LIMITS.batchFrames) : receipt.samples.length)) {
294
+ context.addIssue({ code: "custom", path: ["batches"], message: "Spatial batch count exceeds its selected profile's bounded sample partitions." });
295
+ }
296
+ try { assertHtmlOverlayGpuEvidenceProfile(receipt.request.executionProfile, receipt.runtime.gpuEvidence); }
297
+ catch { context.addIssue({ code: "custom", path: ["runtime", "gpuEvidence"], message: "Spatial receipt must bind hardware evidence exactly when its execution profile requires it." }); }
298
+ });
299
+ export type SpatialRenderReceipt = z.infer<typeof SpatialRenderReceiptSchema>;
300
+ export interface SpatialRenderInput {
301
+ readonly scene: unknown;
302
+ /** Adapter-owned root, not a path selected by the serialized render request. */
303
+ readonly assetRoot: string;
304
+ readonly request: unknown;
305
+ readonly capabilityBindings?: z.infer<typeof MediaCapabilityBindingsSchema>;
306
+ readonly browserRuntime?: HtmlOverlayBrowserRuntimeBinding;
307
+ /** Exact original bytes retained by the source-loading adapter, distinct from canonical scene JSON. */
308
+ readonly originalSceneArtifact?: MediaArtifactReference;
309
+ }
310
+ /** Trusted injection seam for deterministic host tests; never serialized or registered as an operation. */
311
+ export interface SpatialRenderDependencies {
312
+ readonly bindBrowserRuntime?: typeof bindHtmlOverlayBrowserRuntime;
313
+ readonly publishMedia?: typeof publishContentAddressedMedia;
314
+ readonly publishReceipt?: typeof publishContentAddressedReceipt;
315
+ readonly durability?: SpatialDurabilityPort;
316
+ }
317
+ export interface SpatialRenderFailureEvidence {
318
+ readonly attemptId: string;
319
+ readonly stage: "preparation" | "render" | "encode" | "publication" | "cleanup";
320
+ readonly published: readonly MediaArtifactReference[];
321
+ /** Destination may have been linked before an I/O failure. Reconcile; never retry blindly. */
322
+ readonly uncertainPublication?: MediaArtifactReference;
323
+ readonly completion?: SpatialRenderResult;
324
+ }
325
+ export class SpatialRenderFailure extends ApplicationError {
326
+ readonly evidence: SpatialRenderFailureEvidence;
327
+ constructor(error: unknown, evidence: SpatialRenderFailureEvidence, cleanupErrors: readonly unknown[] = []) {
328
+ super(error instanceof ApplicationError ? error.code : "internal", error instanceof Error ? error.message : "Spatial rendering failed.", {
329
+ spatialRender: evidence,
330
+ cleanupErrors: cleanupErrors.map(item => item instanceof Error ? item.message : String(item)),
331
+ });
332
+ this.name = "SpatialRenderFailure";
333
+ this.evidence = deepFreezeJson(evidence);
334
+ this.cause = error;
335
+ }
336
+ }
337
+
338
+ /** Descriptor-bound, bounded read. Parent paths and the named leaf must retain physical identity. */
339
+ async function readPhysical(path: string, maximumBytes: number, signal: AbortSignal): Promise<Uint8Array> {
340
+ checked(await realpath(path) === path, "Spatial render files must have physical paths.");
341
+ const parents: { path: string; dev: number; ino: number }[] = [];
342
+ for (let parent = dirname(path);; parent = dirname(parent)) {
343
+ const stat = await lstat(parent);
344
+ checked(stat.isDirectory() && !stat.isSymbolicLink(), "Spatial render file parent is unsafe.");
345
+ parents.push({ path: parent, dev: stat.dev, ino: stat.ino });
346
+ if (dirname(parent) === parent) break;
347
+ }
348
+ const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
349
+ try {
350
+ throwIfAborted(signal);
351
+ const before = await handle.stat();
352
+ checked(before.isFile() && before.nlink === 1 && Number.isSafeInteger(before.size) && before.size > 0 && before.size <= maximumBytes, "Spatial render file is empty, unsafe, or exceeds its byte budget.");
353
+ const bytes = new Uint8Array(before.size);
354
+ let offset = 0;
355
+ while (offset < bytes.length) {
356
+ throwIfAborted(signal);
357
+ const { bytesRead } = await handle.read(bytes, offset, Math.min(1_048_576, bytes.length - offset), offset);
358
+ checked(bytesRead > 0, "Spatial render file changed during capture.");
359
+ offset += bytesRead;
360
+ }
361
+ const after = await handle.stat(), named = await lstat(path);
362
+ checked(before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeMs === after.mtimeMs && before.ctimeMs === after.ctimeMs
363
+ && named.dev === before.dev && named.ino === before.ino && named.nlink === 1 && !named.isSymbolicLink(), "Spatial render file identity changed during capture.");
364
+ for (const parent of parents) {
365
+ const current = await lstat(parent.path);
366
+ checked(current.isDirectory() && !current.isSymbolicLink() && current.dev === parent.dev && current.ino === parent.ino, "Spatial render parent identity changed during capture.");
367
+ }
368
+ return bytes;
369
+ } finally { await handle.close(); }
370
+ }
371
+ async function stageBytes(path: string, bytes: Uint8Array): Promise<void> {
372
+ const handle = await open(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
373
+ try { await handle.writeFile(bytes); await handle.sync(); } finally { await handle.close(); }
374
+ }
375
+ async function stageJson(path: string, input: unknown, maximumBytes: number): Promise<Uint8Array> {
376
+ const captured = createBoundedJsonSnapshot(input, maximumBytes - 1, "Spatial render evidence", { captureCanonicalText: true });
377
+ const bytes = new TextEncoder().encode(`${captured.canonicalText!}\n`);
378
+ await stageBytes(path, bytes);
379
+ return bytes;
380
+ }
381
+ async function verifyPng(bytes: Uint8Array, width: number, height: number): Promise<void> {
382
+ const decoder = sharp(bytes, { failOn: "warning", limitInputPixels: SPATIAL_RENDER_LIMITS.framePixels });
383
+ const metadata = await decoder.metadata();
384
+ checked(metadata.format === "png" && metadata.width === width && metadata.height === height && metadata.depth === "uchar"
385
+ && (metadata.pages === undefined || metadata.pages === 1) && metadata.icc === undefined
386
+ && metadata.orientation === undefined, "Renderer PNG must be a single calibrated 8-bit frame without an unqualified color profile.");
387
+ // Fully decode before accepting renderer bytes. This preserves data-pass channel values.
388
+ const decoded = await decoder.ensureAlpha().raw().toBuffer({ resolveWithObject: true });
389
+ checked(decoded.info.channels === 4 && decoded.data.length === width * height * 4, "Renderer PNG did not decode to its declared RGBA raster.");
390
+ }
391
+
392
+ const integerText = z.union([z.number().int().safe(), z.string().regex(/^-?(?:0|[1-9]\d{0,23})$/u)]).transform(String);
393
+ const probeSchema = z.strictObject({
394
+ programs: z.array(z.never()).max(0).optional(),
395
+ stream_groups: z.array(z.never()).max(0).optional(),
396
+ streams: z.array(z.object({ index: z.number().int().min(0), codec_type: z.string(), codec_name: z.string(),
397
+ width: z.number().int().positive(), height: z.number().int().positive(), pix_fmt: z.string(),
398
+ r_frame_rate: z.string(), avg_frame_rate: z.string(), time_base: z.string(), start_pts: integerText,
399
+ duration_ts: integerText, nb_frames: integerText,
400
+ })).min(1).max(2),
401
+ frames: z.array(z.object({ stream_index: z.number().int().min(0), pts: integerText,
402
+ best_effort_timestamp: integerText.optional(), pkt_duration: integerText.optional(), duration: integerText.optional(),
403
+ })).min(1).max(SPATIAL_RENDER_LIMITS.frames),
404
+ });
405
+ function ratio(text: string): { numerator: bigint; denominator: bigint } {
406
+ checked(/^[1-9]\d{0,12}\/[1-9]\d{0,12}$/u.test(text), "Encoded stream has an invalid exact rational.");
407
+ const [numerator, denominator] = text.split("/");
408
+ return { numerator: BigInt(numerator!), denominator: BigInt(denominator!) };
409
+ }
410
+ /** Qualifies actual ffprobe packet timestamps rather than inferring delivery from requested fps. */
411
+ export function verifySpatialEncodedVideo(plan: SpatialRenderPlan, probeInput: unknown): SpatialEncodedVideoEvidence {
412
+ checked(plan.request.selection.kind === "video", "Encoded evidence requires a video plan.");
413
+ const captured = createBoundedJsonSnapshot(probeInput, SPATIAL_RENDER_LIMITS.probeBytes, "Spatial video probe");
414
+ const probe = probeSchema.parse(captured.value), stream = probe.streams[0]!;
415
+ const rate = plan.request.selection.frameRate, count = plan.samples.length;
416
+ checked(probe.streams.length === 1 && stream.codec_type === "video" && stream.codec_name === "qtrle" && stream.pix_fmt === "argb"
417
+ && stream.width === plan.width && stream.height === plan.height, "Encoded spatial video differs from the qualified qtrle/ARGB profile.");
418
+ for (const value of [stream.r_frame_rate, stream.avg_frame_rate]) {
419
+ const actual = ratio(value);
420
+ checked(actual.numerator * BigInt(rate.denominator) === actual.denominator * BigInt(rate.numerator), "Encoded frame rate differs from the exact requested rate.");
421
+ }
422
+ const timeBase = ratio(stream.time_base);
423
+ checked(probe.frames.length === count && BigInt(stream.nb_frames) === BigInt(count) && BigInt(stream.start_pts) === 0n, "Encoded stream frame count or start timestamp is incorrect.");
424
+ const durationNumerator = BigInt(rate.denominator) * timeBase.denominator;
425
+ const durationDenominator = BigInt(rate.numerator) * timeBase.numerator;
426
+ checked(durationNumerator % durationDenominator === 0n, "Encoded stream time base cannot represent every exact frame timestamp.");
427
+ const ticks = durationNumerator / durationDenominator;
428
+ for (const [index, frame] of probe.frames.entries()) {
429
+ const expectedPts = BigInt(index) * ticks;
430
+ checked(frame.stream_index === stream.index && BigInt(frame.pts) === expectedPts
431
+ && (frame.best_effort_timestamp === undefined || BigInt(frame.best_effort_timestamp) === expectedPts), "Encoded frame timestamps drift, reorder, or skip a sample.");
432
+ for (const duration of [frame.duration, frame.pkt_duration]) {
433
+ checked(duration === undefined || BigInt(duration) === ticks, "Encoded frame duration differs from the exact cadence.");
434
+ }
435
+ }
436
+ checked(BigInt(stream.duration_ts) === BigInt(count) * ticks, "Encoded stream endpoint differs from its complete exact frame interval.");
437
+ return deepFreezeJson({ codec: "qtrle", container: "mov", pixelFormat: "argb", alpha: "straight", colorSpace: plan.request.mode.kind === "beauty" ? "srgb" : "rgba8-data",
438
+ streamIndex: stream.index, frameRate: rate, frameCount: count, timeBase: rational(timeBase.numerator, timeBase.denominator),
439
+ firstPts: probe.frames[0]!.pts, lastPts: probe.frames.at(-1)!.pts, endPts: stream.duration_ts, outputDurationUs: plan.outputDurationUs!,
440
+ });
441
+ }
442
+
443
+ export async function renderSpatialScene(context: OperationExecutionContext, input: SpatialRenderInput, dependencies: SpatialRenderDependencies = {}): Promise<SpatialRenderResult> {
444
+ const plan = planSpatialRender(input.scene, input.request);
445
+ const assetRoot = input.assetRoot;
446
+ const expectedCapabilities = input.capabilityBindings === undefined ? undefined : parseSpatialValue(MediaCapabilityBindingsSchema, input.capabilityBindings, "render capabilities");
447
+ const expectedRuntime = input.browserRuntime === undefined ? undefined : HtmlOverlayBrowserRuntimeBindingSchema.parse(
448
+ createBoundedJsonSnapshot(input.browserRuntime, SPATIAL_RENDER_LIMITS.metadataBytes, "Expected browser runtime").value,
449
+ );
450
+ const sourceManifests = spatialAssetClosureDigests(plan.scene.assets);
451
+ const originalSceneArtifact = input.originalSceneArtifact === undefined ? undefined : parseSpatialValue(MediaArtifactReferenceSchema, input.originalSceneArtifact, "original scene artifact");
452
+ const attemptId = randomUUID(), published: MediaArtifactReference[] = [];
453
+ const durability = dependencies.durability ?? createNodeSpatialDurability(context.application.paths.repositoryRoot);
454
+ let stage: SpatialRenderFailureEvidence["stage"] = "preparation", uncertainPublication: MediaArtifactReference | undefined;
455
+ let completed: SpatialRenderResult | undefined, failure: unknown, failed = false;
456
+ const cleanupErrors: unknown[] = [];
457
+ const workspace = await createMediaOperationWorkspace(context);
458
+ let directory: string | undefined;
459
+ const assertCustody = async () => { await context.application.hostResourceLease?.assertOwned(); throwIfAborted(context.abortSignal); };
460
+ try {
461
+ await assertCustody();
462
+ if (originalSceneArtifact !== undefined) {
463
+ const bytes = await readPhysical(join(context.application.paths.repositoryRoot, originalSceneArtifact.path), 2_097_152, context.abortSignal);
464
+ checked(bytes.length === originalSceneArtifact.bytes && sha256(bytes) === originalSceneArtifact.sha256
465
+ && spatialSceneSha256(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes))) === plan.sceneSha256,
466
+ "Original scene artifact does not preserve the exact source or canonical scene authority.");
467
+ }
468
+ directory = await mkdtemp(join(workspace.path, "spatial-render-"));
469
+ checked(await realpath(directory) === directory, "Spatial render workspace must be physical.");
470
+ const frameDirectory = join(directory, "source-frames");
471
+ await mkdir(frameDirectory, { mode: 0o700 });
472
+ const names = spatialRenderCapabilityNames(plan);
473
+ await assertMediaCapabilities(context, context.application, expectedCapabilities, names);
474
+ const capabilities = await bindExpectedMediaCapabilities(context.application, names, expectedCapabilities);
475
+ const runtime = await (dependencies.bindBrowserRuntime ?? bindHtmlOverlayBrowserRuntime)(exactCapabilityByName(capabilities, "html-browser"), context.abortSignal);
476
+ if (expectedRuntime !== undefined) {
477
+ checked(canonicalJson(runtime) === canonicalJson(expectedRuntime), "Browser runtime changed after render planning.");
478
+ } else checked(context.workflow === undefined, "Workflow rendering requires a bound complete browser runtime.");
479
+ const renderer = context.application.htmlOverlayRenderer;
480
+ checked(renderer !== undefined, "The application does not provide the qualified spatial browser renderer.");
481
+ const runner = new AbortBoundApplicationRunner(mediaCapabilityRunner(context.application, capabilities), context.abortSignal);
482
+ const ports = { runner, ...(names.includes("ffmpeg") ? { ffmpegCommand: mediaCapabilityCommand(capabilities, "ffmpeg"), ffprobeCommand: mediaCapabilityCommand(capabilities, "ffprobe") } : {}) };
483
+ const retainedSources: { assetId: string; manifestSha256: string; originalPath: string; stagedPath: string; bytes: number }[] = [];
484
+ const batchFiles: string[] = [], sampleEvidence: { sample: SpatialRenderSample; stateSha256: string; viewSha256: string; pngSha256: string; pngBytes: number }[] = [];
485
+ let pngBytes = 0, metadataBytes = 0;
486
+ let gpuEvidence: HtmlOverlayGpuEvidence | undefined;
487
+ for (let offset = 0; offset < plan.samples.length; offset += SPATIAL_RENDER_LIMITS.batchFrames) {
488
+ await assertCustody();
489
+ const samples = plan.samples.slice(offset, offset + SPATIAL_RENDER_LIMITS.batchFrames);
490
+ const snapshots = samples.map(sample => evaluateSpatialScene(plan.scene, { cameraId: plan.request.cameraId, timeUs: sample.timeUs,
491
+ ...(plan.request.overrides === undefined ? {} : { overrides: plan.request.overrides }),
492
+ ...(plan.request.cameraPoseOverride === undefined ? {} : { cameraPoseOverride: plan.request.cameraPoseOverride }),
493
+ }));
494
+ stage = "preparation";
495
+ await withPreparedSpatialAssets({ snapshots, exactSceneTimesUs: samples.map(sample => sample.exactTimeUs), assetRoot, workspaceParent: directory }, ports, context.abortSignal, async prepared => {
496
+ checked(canonicalJson(prepared.receipt.sourceManifests) === canonicalJson(sourceManifests), "Asset preparation did not bind the complete source closure.");
497
+ if (offset === 0) {
498
+ for (const source of prepared.sources) {
499
+ const bytes = await readPhysical(source.absolutePath, source.manifest.payload.bytes, context.abortSignal);
500
+ checked(bytes.length === source.manifest.payload.bytes && sha256(bytes) === source.manifest.payload.sha256
501
+ && source.manifestSha256 === sourceManifests[source.manifest.assetId], "Retained asset differs from its prepared source manifest.");
502
+ const stagedPath = join(directory!, `retained-${source.manifest.assetId}.bin`);
503
+ await stageBytes(stagedPath, bytes);
504
+ retainedSources.push({ assetId: source.manifest.assetId, manifestSha256: source.manifestSha256, originalPath: source.manifest.payload.path, stagedPath, bytes: bytes.length });
505
+ }
506
+ checked(retainedSources.length === plan.scene.assets.length && new Set(retainedSources.map(source => source.assetId)).size === retainedSources.length, "Retained source closure is incomplete or duplicated.");
507
+ }
508
+ const partitions = partitionSpatialRenderWindow({ snapshots, mode: plan.request.mode,
509
+ ...(plan.request.executionProfile === undefined ? {} : { executionProfile: plan.request.executionProfile }),
510
+ frameRate: plan.request.selection.kind === "video" ? plan.request.selection.frameRate : { numerator: 1, denominator: 1 }, preparedAssets: prepared.preparedAssets });
511
+ for (const partition of partitions) {
512
+ const batchOffset = offset + partition.offset;
513
+ const batchSamples = samples.slice(partition.offset, partition.offset + partition.length);
514
+ const batchSnapshots = snapshots.slice(partition.offset, partition.offset + partition.length);
515
+ const batch = partition.batch;
516
+ const resourceNames = new Set(batch.authoring.resources.map(resource => resource.name));
517
+ const batchResources = plan.request.executionProfile === undefined ? prepared.resources : prepared.resources.filter(resource => resourceNames.has(resource.name));
518
+ const batchPreparation = plan.request.executionProfile === undefined ? prepared.receipt : { ...prepared.receipt,
519
+ preparedSha256: canonicalJsonSha256(partition.preparedAssets), outputBytes: batchResources.reduce((sum, resource) => sum + resource.bytes, 0) };
520
+ const batchDirectory = join(directory!, `batch-${batchOffset}`);
521
+ await mkdir(batchDirectory, { mode: 0o700 });
522
+ const bundle = createHtmlOverlayExecutionBundle(batch.authoring, runtime, plan.request.executionProfile);
523
+ const expectedLibraries = plan.request.executionProfile === "three-spark-webgl2-hardware-v1" ? ["@sparkjsdev/spark", "three", "three/addons/postprocessing/Pass.js"] : ["three"];
524
+ checked(canonicalJson(bundle.libraryLocks.map(lock => lock.specifier).sort()) === canonicalJson(expectedLibraries)
525
+ && bundle.libraryLocks.find(lock => lock.specifier === "three")?.version === "0.185.1", "Spatial renderer libraries differ from its exact qualified profile.");
526
+ stage = "render";
527
+ const rendered = await renderer.renderFrames({ authoring: batch.authoring, browserRuntime: runtime, outputDirectory: batchDirectory, resources: batchResources,
528
+ ...(plan.request.executionProfile === undefined ? {} : { executionProfile: plan.request.executionProfile }) }, context.abortSignal);
529
+ await assertCustody();
530
+ const integrity = parseSpatialValue(HtmlOverlayExecutionIntegritySchema, rendered.executionIntegrity, "Renderer execution integrity");
531
+ checked(canonicalJson(integrity) === canonicalJson(bundle.integrity) && canonicalJson(rendered.libraryLocks) === canonicalJson(bundle.libraryLocks), "Renderer returned execution or library integrity different from the bound input.");
532
+ const observedGpu = assertHtmlOverlayGpuEvidenceProfile(plan.request.executionProfile, rendered.gpuEvidence);
533
+ if (batchOffset !== 0) checked(observedGpu === undefined || gpuEvidence === undefined
534
+ ? observedGpu === gpuEvidence : canonicalJson(observedGpu) === canonicalJson(gpuEvidence), "Spatial render changed its observed hardware between batches.");
535
+ gpuEvidence = observedGpu;
536
+ const expectedPattern = join(batchDirectory, "frames", "frame-%08d.png");
537
+ checked(rendered.frameCount === batchSamples.length && rendered.framePattern === expectedPattern, "Renderer frame count or output location differs from the planned batch.");
538
+ const entries = await readdir(join(batchDirectory, "frames"));
539
+ checked(entries.length === batchSamples.length, "Renderer produced an unexpected frame directory entry.");
540
+ for (const [index, sample] of batchSamples.entries()) {
541
+ const bytes = await readPhysical(join(batchDirectory, "frames", frameName(index)), plan.costs.pngBytesBound, context.abortSignal);
542
+ pngBytes += bytes.length;
543
+ checked(pngBytes <= plan.costs.pngBytesBound, "Rendered PNG bytes exceeded the admitted staging estimate.");
544
+ await verifyPng(bytes, plan.width, plan.height);
545
+ await stageBytes(join(frameDirectory, frameName(sample.index)), bytes);
546
+ sampleEvidence.push({ sample, stateSha256: batchSnapshots[index]!.stateSha256, viewSha256: batchSnapshots[index]!.viewSha256, pngSha256: sha256(bytes), pngBytes: bytes.length });
547
+ }
548
+ const metadataPath = join(directory!, `batch-${batchOffset}.json`);
549
+ const bytes = await stageJson(metadataPath, { preparedAssets: partition.preparedAssets, metadata: batch.metadata, metadataSha256: batch.metadataSha256, executionIntegrity: integrity,
550
+ ...(observedGpu === undefined ? {} : { gpuEvidence: observedGpu }),
551
+ libraryLocks: bundle.libraryLocks, preparation: batchPreparation, samples: sampleEvidence.slice(batchOffset) }, SPATIAL_RENDER_LIMITS.metadataBytes);
552
+ metadataBytes += bytes.length;
553
+ checked(metadataBytes <= SPATIAL_RENDER_LIMITS.metadataBytes, "Spatial batch evidence exceeded its total byte budget.");
554
+ batchFiles.push(metadataPath);
555
+ await rm(batchDirectory, { recursive: true });
556
+ }
557
+ });
558
+ }
559
+ stage = "encode";
560
+ const outputPath = join(directory, plan.request.selection.kind === "video" ? "output.mov" : "output.png");
561
+ let encodedEvidence: SpatialEncodedVideoEvidence | undefined, probePath: string | undefined;
562
+ if (plan.request.selection.kind === "video") {
563
+ const rate = plan.request.selection.frameRate;
564
+ const result = await runner.run([mediaCapabilityCommand(capabilities, "ffmpeg"), "-hide_banner", "-loglevel", "error", "-nostdin", "-n",
565
+ "-framerate", `${rate.numerator}/${rate.denominator}`, "-start_number", "0", "-i", join(frameDirectory, "frame-%08d.png"),
566
+ "-frames:v", String(plan.samples.length), "-an", "-c:v", "qtrle", "-pix_fmt", "argb", "-fps_mode", "passthrough",
567
+ "-enc_time_base", `${rate.denominator}/${rate.numerator}`, "-video_track_timescale", String(rate.numerator),
568
+ "-movflags", "+faststart", "-fs", String(plan.costs.outputBytesBound), outputPath],
569
+ { cwd: directory, stdin: "ignore", timeoutMs: SPATIAL_RENDER_LIMITS.nativeTimeoutMs, maxOutputBytes: 64 * 1024 });
570
+ checked(result.exitCode === 0, `Spatial qtrle encoding failed: ${result.stderr.slice(0, 2_048)}`);
571
+ await readPhysical(outputPath, plan.costs.outputBytesBound, context.abortSignal);
572
+ const probe = await runner.run([mediaCapabilityCommand(capabilities, "ffprobe"), "-v", "error", "-show_streams", "-show_frames", "-show_entries",
573
+ "stream=index,codec_type,codec_name,width,height,pix_fmt,r_frame_rate,avg_frame_rate,time_base,start_pts,duration_ts,nb_frames,color_space,color_transfer,color_primaries,color_range:frame=stream_index,pts,best_effort_timestamp,pkt_duration,duration", "-of", "json", outputPath],
574
+ { cwd: directory, stdin: "ignore", timeoutMs: SPATIAL_RENDER_LIMITS.nativeTimeoutMs, maxOutputBytes: SPATIAL_RENDER_LIMITS.probeBytes });
575
+ checked(probe.exitCode === 0 && Buffer.byteLength(probe.stdout) <= SPATIAL_RENDER_LIMITS.probeBytes, "Spatial encoded-video probing failed or exceeded its byte budget.");
576
+ const value: unknown = JSON.parse(probe.stdout);
577
+ encodedEvidence = verifySpatialEncodedVideo(plan, value);
578
+ probePath = join(directory, "encoded-probe.json");
579
+ await stageJson(probePath, value, SPATIAL_RENDER_LIMITS.probeBytes);
580
+ } else if (plan.request.selection.kind === "contact-sheet") {
581
+ const selection = plan.request.selection;
582
+ const tiles: { input: Buffer; top: number; left: number }[] = [];
583
+ for (const sample of plan.samples) {
584
+ const bytes = await readPhysical(join(frameDirectory, frameName(sample.index)), plan.costs.pngBytesBound, context.abortSignal);
585
+ const tile = await sharp(bytes).resize(selection.cellWidth, selection.cellHeight, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 }, kernel: plan.request.mode.kind === "beauty" ? "lanczos3" : "nearest" }).png().toBuffer();
586
+ tiles.push({ input: tile, left: sample.index % selection.columns * selection.cellWidth, top: Math.floor(sample.index / selection.columns) * selection.cellHeight });
587
+ }
588
+ const bytes = await sharp({ create: { width: plan.outputWidth, height: plan.outputHeight, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } } }).composite(tiles).png().toBuffer();
589
+ checked(bytes.length <= plan.costs.outputBytesBound, "Contact sheet exceeded its admitted output-byte estimate.");
590
+ await stageBytes(outputPath, bytes);
591
+ } else {
592
+ await stageBytes(outputPath, await readPhysical(join(frameDirectory, frameName(0)), plan.costs.outputBytesBound, context.abortSignal));
593
+ }
594
+ await assertCustody();
595
+ const scenePath = join(directory, "scene.json"), runtimePath = join(directory, "runtime.json");
596
+ const sceneBytes = await stageJson(scenePath, plan.scene, 2_097_153);
597
+ const runtimeBytes = await stageJson(runtimePath, runtime, SPATIAL_RENDER_LIMITS.metadataBytes);
598
+ checked(metadataBytes + sceneBytes.length + runtimeBytes.length <= SPATIAL_RENDER_LIMITS.metadataBytes, "Combined spatial source/runtime/batch evidence exceeds the admitted metadata budget.");
599
+ stage = "publication";
600
+ const publish = async (stagedPath: string, extension: string, maximumBytes: number): Promise<MediaArtifactReference> => {
601
+ const bytes = await readPhysical(stagedPath, maximumBytes, context.abortSignal);
602
+ uncertainPublication = prospective(context, "outputs", extension, bytes);
603
+ const result = await (dependencies.publishMedia ?? publishContentAddressedMedia)({ context, stagedPath, extension, maximumBytes, beforePublication: assertCustody });
604
+ checked(canonicalJson(result.artifact) === canonicalJson(uncertainPublication), "Published spatial artifact differs from its staged content address.");
605
+ published.push(result.artifact);
606
+ await assertCustody();
607
+ await durability.syncExactFile(result.artifact.path, result.artifact);
608
+ await assertCustody();
609
+ uncertainPublication = undefined;
610
+ return result.artifact;
611
+ };
612
+ if (originalSceneArtifact !== undefined) {
613
+ await assertCustody();
614
+ await durability.syncExactFile(originalSceneArtifact.path, originalSceneArtifact);
615
+ }
616
+ const sceneSource = await publish(scenePath, ".json", 2_097_153);
617
+ const runtimeArtifact = await publish(runtimePath, ".json", SPATIAL_RENDER_LIMITS.metadataBytes);
618
+ const retainedAssets: SpatialRetainedAsset[] = [];
619
+ for (const source of retainedSources) retainedAssets.push({ assetId: source.assetId, manifestSha256: source.manifestSha256, originalPath: source.originalPath,
620
+ artifact: await publish(source.stagedPath, ".bin", source.bytes) });
621
+ const batches: MediaArtifactReference[] = [];
622
+ for (const path of batchFiles) batches.push(await publish(path, ".json", SPATIAL_RENDER_LIMITS.metadataBytes));
623
+ const encodedProbe = probePath === undefined ? undefined : await publish(probePath, ".json", SPATIAL_RENDER_LIMITS.probeBytes);
624
+ const artifact = await publish(outputPath, plan.request.selection.kind === "video" ? ".mov" : ".png", plan.costs.outputBytesBound);
625
+ const frameArtifacts: MediaArtifactReference[] = [];
626
+ if (plan.request.selection.kind === "frame") frameArtifacts.push(artifact);
627
+ else if (plan.request.selection.kind === "contact-sheet") {
628
+ for (const sample of plan.samples) frameArtifacts.push(await publish(join(frameDirectory, frameName(sample.index)), ".png", plan.costs.pngBytesBound));
629
+ }
630
+ const render: SpatialRenderResult["render"] = { kind: plan.request.selection.kind, frameCount: plan.samples.length, width: plan.outputWidth, height: plan.outputHeight,
631
+ ...(encodedEvidence === undefined ? {} : { frameRate: encodedEvidence.frameRate, encodedEvidence }) };
632
+ const receiptValue = SpatialRenderReceiptSchema.parse({
633
+ kind: "slopcamera.spatial-render-receipt", schemaVersion: 1, attemptId, sceneSha256: plan.sceneSha256,
634
+ source: { canonicalScene: sceneSource, canonicalization: "parsed-spatial-scene-v1", ...(originalSceneArtifact === undefined ? {} : { originalSceneArtifact }), retainedAssets, sourceManifests },
635
+ request: plan.request, requestSha256: plan.requestSha256, samples: sampleEvidence, render, output: artifact, batches, frameArtifacts,
636
+ runtime: { artifact: runtimeArtifact, rootSha256: runtime.manifest.rootSha256, capabilities, renderer: "three-webgl2-snapshot-v1", threeVersion: "0.185.1",
637
+ ...(gpuEvidence === undefined ? {} : { gpuEvidence }) },
638
+ color: { output: plan.request.mode.kind === "beauty" ? "srgb" : "rgba8-data", alpha: plan.request.mode.kind === "beauty" ? "straight" : "binary-validity", toneMapping: "none" },
639
+ timing: { sourceClock: "absolute-rational-microseconds", quantization: "independent-nearest-microsecond-ties-positive", encodedClock: "zero-based-uniform-frame-intervals",
640
+ ...(plan.outputDurationUs === undefined ? {} : { outputDurationUs: plan.outputDurationUs }) },
641
+ calibratedSourceDimensions: { width: plan.width, height: plan.height },
642
+ ...(plan.request.selection.kind === "contact-sheet" ? { contactSheet: { fit: "contain", resampling: plan.request.mode.kind === "beauty" ? "lanczos3" : "nearest-data", calibratedProjectionResized: false } } : {}),
643
+ ...(encodedProbe === undefined ? {} : { encodedProbe }),
644
+ ...(context.workflow === undefined ? {} : { workflow: { nodeKey: context.workflow.nodeKey, nodePlanSha256: context.workflow.nodePlanSha256, runId: context.workflow.runId } }),
645
+ costs: { ...plan.costs, actualPngBytes: pngBytes, actualBatchMetadataBytes: metadataBytes },
646
+ });
647
+ const receiptBytes = new TextEncoder().encode(`${canonicalJson(receiptValue)}\n`);
648
+ checked(receiptBytes.length <= 1024 * 1024, "Spatial completion receipt exceeds its byte budget.");
649
+ uncertainPublication = prospective(context, "receipts", ".json", receiptBytes);
650
+ const receipt = await (dependencies.publishReceipt ?? publishContentAddressedReceipt)({ context, receipt: receiptValue, workspace: { path: directory, dispose: () => Promise.resolve() }, beforePublication: assertCustody });
651
+ checked(canonicalJson(receipt) === canonicalJson(uncertainPublication), "Published spatial receipt differs from its staged content address.");
652
+ published.push(receipt);
653
+ await assertCustody();
654
+ await durability.syncExactFile(receipt.path, receipt);
655
+ uncertainPublication = undefined;
656
+ completed = deepFreezeJson(SpatialRenderOutputSchema.parse({ artifact, receipt, sceneSource, retainedAssets, render }));
657
+ } catch (error) { failure = error; failed = true; }
658
+ finally {
659
+ // Every render/preparation/native promise has settled before staging is removed.
660
+ if (directory !== undefined) try { await rm(directory, { recursive: true, force: true }); } catch (error) { cleanupErrors.push(error); }
661
+ try { await workspace.dispose(); } catch (error) { cleanupErrors.push(error); }
662
+ }
663
+ if (failed || cleanupErrors.length > 0) throw new SpatialRenderFailure(failed ? failure : cleanupErrors[0], {
664
+ attemptId, stage: failed ? stage : "cleanup", published,
665
+ ...(uncertainPublication === undefined ? {} : { uncertainPublication }), ...(completed === undefined ? {} : { completion: completed }),
666
+ }, cleanupErrors);
667
+ checked(completed !== undefined, "Spatial render did not produce completion evidence.");
668
+ return completed;
669
+ }
670
+
671
+ function frameName(index: number): string { return `frame-${String(index).padStart(8, "0")}.png`; }
672
+ function prospective(context: OperationExecutionContext, category: "outputs" | "receipts", extension: string, bytes: Uint8Array): MediaArtifactReference {
673
+ const digest = sha256(bytes);
674
+ return MediaArtifactReferenceSchema.parse({ sha256: digest, bytes: bytes.length,
675
+ path: relative(context.application.paths.repositoryRoot, join(dirname(context.application.paths.artifactRoot), `generated/media-operations/${category}`, `${digest}${extension}`)),
676
+ });
677
+ }