@nodetool-ai/agents 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/dist/.nodetool-build-stamp +1 -1
  2. package/dist/analysis/audio-dsp.d.ts +3 -2
  3. package/dist/analysis/audio-dsp.d.ts.map +1 -1
  4. package/dist/analysis/audio-dsp.js +11 -8
  5. package/dist/analysis/audio-dsp.js.map +1 -1
  6. package/dist/analysis/media-decode.d.ts +13 -0
  7. package/dist/analysis/media-decode.d.ts.map +1 -1
  8. package/dist/analysis/media-decode.js +33 -6
  9. package/dist/analysis/media-decode.js.map +1 -1
  10. package/dist/author-graph.d.ts +2 -3
  11. package/dist/author-graph.d.ts.map +1 -1
  12. package/dist/author-graph.js +3 -5
  13. package/dist/author-graph.js.map +1 -1
  14. package/dist/browser-gate.d.ts +2 -2
  15. package/dist/browser-gate.js +2 -2
  16. package/dist/capabilities/GamePublishRequest.d.ts +14 -0
  17. package/dist/capabilities/GamePublishRequest.d.ts.map +1 -0
  18. package/dist/capabilities/GamePublishRequest.js +2 -0
  19. package/dist/capabilities/GamePublishRequest.js.map +1 -0
  20. package/dist/capabilities/adapters.d.ts +4 -13
  21. package/dist/capabilities/adapters.d.ts.map +1 -1
  22. package/dist/capabilities/adapters.js +9 -14
  23. package/dist/capabilities/adapters.js.map +1 -1
  24. package/dist/capabilities/agent-activity.d.ts +27 -0
  25. package/dist/capabilities/agent-activity.d.ts.map +1 -0
  26. package/dist/capabilities/agent-activity.js +103 -0
  27. package/dist/capabilities/agent-activity.js.map +1 -0
  28. package/dist/capabilities/agentic-storyboard-finish.d.ts +67 -0
  29. package/dist/capabilities/agentic-storyboard-finish.d.ts.map +1 -0
  30. package/dist/capabilities/agentic-storyboard-finish.js +1321 -0
  31. package/dist/capabilities/agentic-storyboard-finish.js.map +1 -0
  32. package/dist/capabilities/agents.d.ts +5 -2
  33. package/dist/capabilities/agents.d.ts.map +1 -1
  34. package/dist/capabilities/agents.js +14 -4
  35. package/dist/capabilities/agents.js.map +1 -1
  36. package/dist/capabilities/agents.specs.d.ts +3 -0
  37. package/dist/capabilities/agents.specs.d.ts.map +1 -1
  38. package/dist/capabilities/agents.specs.js +71 -1
  39. package/dist/capabilities/agents.specs.js.map +1 -1
  40. package/dist/capabilities/analysis.d.ts.map +1 -1
  41. package/dist/capabilities/analysis.js +33 -22
  42. package/dist/capabilities/analysis.js.map +1 -1
  43. package/dist/capabilities/analysis.specs.js +2 -2
  44. package/dist/capabilities/analysis.specs.js.map +1 -1
  45. package/dist/capabilities/apps.d.ts.map +1 -1
  46. package/dist/capabilities/apps.js +5 -3
  47. package/dist/capabilities/apps.js.map +1 -1
  48. package/dist/capabilities/args.d.ts +2 -5
  49. package/dist/capabilities/args.d.ts.map +1 -1
  50. package/dist/capabilities/args.js +2 -5
  51. package/dist/capabilities/args.js.map +1 -1
  52. package/dist/capabilities/browser.d.ts +1 -1
  53. package/dist/capabilities/browser.js +1 -1
  54. package/dist/capabilities/browser.specs.d.ts +1 -1
  55. package/dist/capabilities/browser.specs.js +1 -1
  56. package/dist/capabilities/code.d.ts +5 -0
  57. package/dist/capabilities/code.d.ts.map +1 -1
  58. package/dist/capabilities/code.js +128 -89
  59. package/dist/capabilities/code.js.map +1 -1
  60. package/dist/capabilities/errors.d.ts +19 -0
  61. package/dist/capabilities/errors.d.ts.map +1 -0
  62. package/dist/capabilities/errors.js +109 -0
  63. package/dist/capabilities/errors.js.map +1 -0
  64. package/dist/capabilities/errors.specs.d.ts +11 -0
  65. package/dist/capabilities/errors.specs.d.ts.map +1 -0
  66. package/dist/capabilities/errors.specs.js +100 -0
  67. package/dist/capabilities/errors.specs.js.map +1 -0
  68. package/dist/capabilities/example-timelines.d.ts.map +1 -1
  69. package/dist/capabilities/example-timelines.js +52 -10
  70. package/dist/capabilities/example-timelines.js.map +1 -1
  71. package/dist/capabilities/example-timelines.specs.d.ts +1 -0
  72. package/dist/capabilities/example-timelines.specs.d.ts.map +1 -1
  73. package/dist/capabilities/example-timelines.specs.js +21 -3
  74. package/dist/capabilities/example-timelines.specs.js.map +1 -1
  75. package/dist/capabilities/finish-storyboard.d.ts +5 -0
  76. package/dist/capabilities/finish-storyboard.d.ts.map +1 -0
  77. package/dist/capabilities/finish-storyboard.js +278 -0
  78. package/dist/capabilities/finish-storyboard.js.map +1 -0
  79. package/dist/capabilities/game-asset-source.js +1 -1
  80. package/dist/capabilities/game-asset-source.js.map +1 -1
  81. package/dist/capabilities/game.d.ts +14 -2
  82. package/dist/capabilities/game.d.ts.map +1 -1
  83. package/dist/capabilities/game.js +209 -39
  84. package/dist/capabilities/game.js.map +1 -1
  85. package/dist/capabilities/game.specs.d.ts +2 -0
  86. package/dist/capabilities/game.specs.d.ts.map +1 -1
  87. package/dist/capabilities/game.specs.js +25 -11
  88. package/dist/capabilities/game.specs.js.map +1 -1
  89. package/dist/capabilities/game3d.d.ts +17 -0
  90. package/dist/capabilities/game3d.d.ts.map +1 -0
  91. package/dist/capabilities/game3d.js +433 -0
  92. package/dist/capabilities/game3d.js.map +1 -0
  93. package/dist/capabilities/gate-from-context.d.ts +1 -1
  94. package/dist/capabilities/gate-from-context.js +1 -1
  95. package/dist/capabilities/generations.d.ts +2 -0
  96. package/dist/capabilities/generations.d.ts.map +1 -1
  97. package/dist/capabilities/generations.js +7 -2
  98. package/dist/capabilities/generations.js.map +1 -1
  99. package/dist/capabilities/index.d.ts +2 -2
  100. package/dist/capabilities/index.d.ts.map +1 -1
  101. package/dist/capabilities/index.js +2 -2
  102. package/dist/capabilities/index.js.map +1 -1
  103. package/dist/capabilities/invoke.d.ts +14 -25
  104. package/dist/capabilities/invoke.d.ts.map +1 -1
  105. package/dist/capabilities/invoke.js +72 -35
  106. package/dist/capabilities/invoke.js.map +1 -1
  107. package/dist/capabilities/jobs.d.ts.map +1 -1
  108. package/dist/capabilities/jobs.js +47 -3
  109. package/dist/capabilities/jobs.js.map +1 -1
  110. package/dist/capabilities/jobs.specs.d.ts.map +1 -1
  111. package/dist/capabilities/jobs.specs.js +3 -1
  112. package/dist/capabilities/jobs.specs.js.map +1 -1
  113. package/dist/capabilities/js-scripts.d.ts +19 -1
  114. package/dist/capabilities/js-scripts.d.ts.map +1 -1
  115. package/dist/capabilities/js-scripts.js +109 -2
  116. package/dist/capabilities/js-scripts.js.map +1 -1
  117. package/dist/capabilities/js-scripts.specs.d.ts +2 -0
  118. package/dist/capabilities/js-scripts.specs.d.ts.map +1 -1
  119. package/dist/capabilities/js-scripts.specs.js +50 -0
  120. package/dist/capabilities/js-scripts.specs.js.map +1 -1
  121. package/dist/capabilities/lazy-tool.d.ts +23 -24
  122. package/dist/capabilities/lazy-tool.d.ts.map +1 -1
  123. package/dist/capabilities/lazy-tool.js +29 -35
  124. package/dist/capabilities/lazy-tool.js.map +1 -1
  125. package/dist/capabilities/legacy-tools.d.ts +15 -0
  126. package/dist/capabilities/legacy-tools.d.ts.map +1 -0
  127. package/dist/capabilities/legacy-tools.js +98 -0
  128. package/dist/capabilities/legacy-tools.js.map +1 -0
  129. package/dist/capabilities/media.d.ts +0 -9
  130. package/dist/capabilities/media.d.ts.map +1 -1
  131. package/dist/capabilities/media.js +301 -260
  132. package/dist/capabilities/media.js.map +1 -1
  133. package/dist/capabilities/models.d.ts +2 -2
  134. package/dist/capabilities/models.d.ts.map +1 -1
  135. package/dist/capabilities/models.js +5 -3
  136. package/dist/capabilities/models.js.map +1 -1
  137. package/dist/capabilities/registry.d.ts +2 -0
  138. package/dist/capabilities/registry.d.ts.map +1 -1
  139. package/dist/capabilities/registry.js +28 -0
  140. package/dist/capabilities/registry.js.map +1 -1
  141. package/dist/capabilities/run-agent.d.ts +19 -0
  142. package/dist/capabilities/run-agent.d.ts.map +1 -0
  143. package/dist/capabilities/run-agent.js +183 -0
  144. package/dist/capabilities/run-agent.js.map +1 -0
  145. package/dist/capabilities/runs.d.ts +9 -0
  146. package/dist/capabilities/runs.d.ts.map +1 -0
  147. package/dist/capabilities/runs.js +46 -0
  148. package/dist/capabilities/runs.js.map +1 -0
  149. package/dist/capabilities/runs.specs.d.ts +39 -0
  150. package/dist/capabilities/runs.specs.d.ts.map +1 -0
  151. package/dist/capabilities/runs.specs.js +52 -0
  152. package/dist/capabilities/runs.specs.js.map +1 -0
  153. package/dist/capabilities/scripts.d.ts.map +1 -1
  154. package/dist/capabilities/scripts.js +11 -5
  155. package/dist/capabilities/scripts.js.map +1 -1
  156. package/dist/capabilities/sketches.d.ts.map +1 -1
  157. package/dist/capabilities/sketches.js +4 -2
  158. package/dist/capabilities/sketches.js.map +1 -1
  159. package/dist/capabilities/skills.d.ts.map +1 -1
  160. package/dist/capabilities/skills.js +22 -2
  161. package/dist/capabilities/skills.js.map +1 -1
  162. package/dist/capabilities/storyboard-layout-check.d.ts +33 -0
  163. package/dist/capabilities/storyboard-layout-check.d.ts.map +1 -0
  164. package/dist/capabilities/storyboard-layout-check.js +229 -0
  165. package/dist/capabilities/storyboard-layout-check.js.map +1 -0
  166. package/dist/capabilities/storyboard-measured-sources.d.ts +10 -0
  167. package/dist/capabilities/storyboard-measured-sources.d.ts.map +1 -0
  168. package/dist/capabilities/storyboard-measured-sources.js +67 -0
  169. package/dist/capabilities/storyboard-measured-sources.js.map +1 -0
  170. package/dist/capabilities/storyboard-review-times.d.ts +4 -0
  171. package/dist/capabilities/storyboard-review-times.d.ts.map +1 -0
  172. package/dist/capabilities/storyboard-review-times.js +39 -0
  173. package/dist/capabilities/storyboard-review-times.js.map +1 -0
  174. package/dist/capabilities/storyboards.d.ts +2 -18
  175. package/dist/capabilities/storyboards.d.ts.map +1 -1
  176. package/dist/capabilities/storyboards.js +315 -94
  177. package/dist/capabilities/storyboards.js.map +1 -1
  178. package/dist/capabilities/storyboards.specs.d.ts +3 -0
  179. package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
  180. package/dist/capabilities/storyboards.specs.js +42 -3
  181. package/dist/capabilities/storyboards.specs.js.map +1 -1
  182. package/dist/capabilities/timeline-bridge.d.ts +359 -0
  183. package/dist/capabilities/timeline-bridge.d.ts.map +1 -0
  184. package/dist/capabilities/timeline-bridge.js +1016 -0
  185. package/dist/capabilities/timeline-bridge.js.map +1 -0
  186. package/dist/capabilities/timeline-clip-frames.d.ts +17 -0
  187. package/dist/capabilities/timeline-clip-frames.d.ts.map +1 -0
  188. package/dist/capabilities/timeline-clip-frames.js +84 -0
  189. package/dist/capabilities/timeline-clip-frames.js.map +1 -0
  190. package/dist/capabilities/timeline-media-edit.d.ts +8 -0
  191. package/dist/capabilities/timeline-media-edit.d.ts.map +1 -0
  192. package/dist/capabilities/timeline-media-edit.js +94 -0
  193. package/dist/capabilities/timeline-media-edit.js.map +1 -0
  194. package/dist/capabilities/timeline-native-media.d.ts.map +1 -1
  195. package/dist/capabilities/timeline-native-media.js +40 -0
  196. package/dist/capabilities/timeline-native-media.js.map +1 -1
  197. package/dist/capabilities/timelines.d.ts +64 -4
  198. package/dist/capabilities/timelines.d.ts.map +1 -1
  199. package/dist/capabilities/timelines.js +566 -67
  200. package/dist/capabilities/timelines.js.map +1 -1
  201. package/dist/capabilities/timelines.specs.d.ts +8 -0
  202. package/dist/capabilities/timelines.specs.d.ts.map +1 -1
  203. package/dist/capabilities/timelines.specs.js +173 -8
  204. package/dist/capabilities/timelines.specs.js.map +1 -1
  205. package/dist/capabilities/types.d.ts +3 -1
  206. package/dist/capabilities/types.d.ts.map +1 -1
  207. package/dist/capabilities/types.js.map +1 -1
  208. package/dist/capabilities/ui.d.ts.map +1 -1
  209. package/dist/capabilities/ui.js +24 -2
  210. package/dist/capabilities/ui.js.map +1 -1
  211. package/dist/capabilities/workflows.d.ts.map +1 -1
  212. package/dist/capabilities/workflows.js +53 -26
  213. package/dist/capabilities/workflows.js.map +1 -1
  214. package/dist/capabilities/workflows.specs.d.ts.map +1 -1
  215. package/dist/capabilities/workflows.specs.js +7 -1
  216. package/dist/capabilities/workflows.specs.js.map +1 -1
  217. package/dist/codeact/action-diagnostics.d.ts.map +1 -1
  218. package/dist/codeact/action-diagnostics.js +51 -1
  219. package/dist/codeact/action-diagnostics.js.map +1 -1
  220. package/dist/codeact/codeact-executor.d.ts.map +1 -1
  221. package/dist/codeact/codeact-executor.js +16 -8
  222. package/dist/codeact/codeact-executor.js.map +1 -1
  223. package/dist/codeact/compact-ids.d.ts.map +1 -1
  224. package/dist/codeact/compact-ids.js +5 -1
  225. package/dist/codeact/compact-ids.js.map +1 -1
  226. package/dist/codeact/graph-model.d.ts +1 -1
  227. package/dist/codeact/graph-model.d.ts.map +1 -1
  228. package/dist/codeact/graph-model.js +2 -3
  229. package/dist/codeact/graph-model.js.map +1 -1
  230. package/dist/codeact/nodetool-api.d.ts +1 -1
  231. package/dist/codeact/nodetool-api.d.ts.map +1 -1
  232. package/dist/codeact/nodetool-api.js +202 -284
  233. package/dist/codeact/nodetool-api.js.map +1 -1
  234. package/dist/codeact/tool-api.d.ts +6 -3
  235. package/dist/codeact/tool-api.d.ts.map +1 -1
  236. package/dist/codeact/tool-api.js +22 -15
  237. package/dist/codeact/tool-api.js.map +1 -1
  238. package/dist/evals/codeact-api-cases.d.ts.map +1 -1
  239. package/dist/evals/codeact-api-cases.js +2 -0
  240. package/dist/evals/codeact-api-cases.js.map +1 -1
  241. package/dist/evals/codeact-api-runs.d.ts +3 -0
  242. package/dist/evals/codeact-api-runs.d.ts.map +1 -0
  243. package/dist/evals/codeact-api-runs.js +33 -0
  244. package/dist/evals/codeact-api-runs.js.map +1 -0
  245. package/dist/evals/surfaces/sketch.d.ts +2 -0
  246. package/dist/evals/surfaces/sketch.d.ts.map +1 -1
  247. package/dist/evals/surfaces/sketch.js +11 -3
  248. package/dist/evals/surfaces/sketch.js.map +1 -1
  249. package/dist/evals/surfaces/timeline.d.ts +4 -357
  250. package/dist/evals/surfaces/timeline.d.ts.map +1 -1
  251. package/dist/evals/surfaces/timeline.js +9 -2621
  252. package/dist/evals/surfaces/timeline.js.map +1 -1
  253. package/dist/evals/tool-loop-bridge.d.ts +4 -0
  254. package/dist/evals/tool-loop-bridge.d.ts.map +1 -1
  255. package/dist/evals/tool-loop-bridge.js.map +1 -1
  256. package/dist/evals/tool-loop-eval.d.ts +2 -0
  257. package/dist/evals/tool-loop-eval.d.ts.map +1 -1
  258. package/dist/evals/tool-loop-eval.js +6 -1
  259. package/dist/evals/tool-loop-eval.js.map +1 -1
  260. package/dist/evals/tool-loop-permission.d.ts +1 -15
  261. package/dist/evals/tool-loop-permission.d.ts.map +1 -1
  262. package/dist/evals/tool-loop-permission.js +23 -41
  263. package/dist/evals/tool-loop-permission.js.map +1 -1
  264. package/dist/game-authoring.d.ts +4737 -0
  265. package/dist/game-authoring.d.ts.map +1 -0
  266. package/dist/game-authoring.js +146 -0
  267. package/dist/game-authoring.js.map +1 -0
  268. package/dist/game-code-bake.d.ts +26 -0
  269. package/dist/game-code-bake.d.ts.map +1 -0
  270. package/dist/game-code-bake.js +98 -0
  271. package/dist/game-code-bake.js.map +1 -0
  272. package/dist/game-examples.d.ts +872 -16
  273. package/dist/game-examples.d.ts.map +1 -1
  274. package/dist/game-examples.js +5 -4
  275. package/dist/game-examples.js.map +1 -1
  276. package/dist/index.d.ts +15 -4
  277. package/dist/index.d.ts.map +1 -1
  278. package/dist/index.js +13 -3
  279. package/dist/index.js.map +1 -1
  280. package/dist/js-sandbox-worker/engine.d.ts +4 -0
  281. package/dist/js-sandbox-worker/engine.d.ts.map +1 -0
  282. package/dist/js-sandbox-worker/engine.js +45 -0
  283. package/dist/js-sandbox-worker/engine.js.map +1 -0
  284. package/dist/js-sandbox-worker/host.js +1 -1
  285. package/dist/js-sandbox-worker/host.js.map +1 -1
  286. package/dist/js-sandbox-worker/interpreter.d.ts +1 -1
  287. package/dist/js-sandbox-worker/interpreter.d.ts.map +1 -1
  288. package/dist/js-sandbox-worker/interpreter.js +94 -2
  289. package/dist/js-sandbox-worker/interpreter.js.map +1 -1
  290. package/dist/js-sandbox-worker/worker-entry.d.ts.map +1 -1
  291. package/dist/js-sandbox-worker/worker-entry.js +3 -2
  292. package/dist/js-sandbox-worker/worker-entry.js.map +1 -1
  293. package/dist/js-sandbox.d.ts +2 -0
  294. package/dist/js-sandbox.d.ts.map +1 -1
  295. package/dist/js-sandbox.js +56 -11
  296. package/dist/js-sandbox.js.map +1 -1
  297. package/dist/js-script-app-runner.d.ts +2 -0
  298. package/dist/js-script-app-runner.d.ts.map +1 -1
  299. package/dist/js-script-app-runner.js +8 -3
  300. package/dist/js-script-app-runner.js.map +1 -1
  301. package/dist/js-script-sandbox.d.ts +12 -18
  302. package/dist/js-script-sandbox.d.ts.map +1 -1
  303. package/dist/js-script-sandbox.js +3 -49
  304. package/dist/js-script-sandbox.js.map +1 -1
  305. package/dist/prompts/product-knowledge.d.ts +1 -1
  306. package/dist/prompts/product-knowledge.d.ts.map +1 -1
  307. package/dist/prompts/product-knowledge.js +82 -41
  308. package/dist/prompts/product-knowledge.js.map +1 -1
  309. package/dist/sandbox-pack-resolution.d.ts +21 -0
  310. package/dist/sandbox-pack-resolution.d.ts.map +1 -0
  311. package/dist/sandbox-pack-resolution.js +42 -0
  312. package/dist/sandbox-pack-resolution.js.map +1 -0
  313. package/dist/sandbox-toolbelt.d.ts +3 -1
  314. package/dist/sandbox-toolbelt.d.ts.map +1 -1
  315. package/dist/sandbox-toolbelt.js +8 -2
  316. package/dist/sandbox-toolbelt.js.map +1 -1
  317. package/dist/skill-prompt.d.ts +11 -1
  318. package/dist/skill-prompt.d.ts.map +1 -1
  319. package/dist/skill-prompt.js +17 -6
  320. package/dist/skill-prompt.js.map +1 -1
  321. package/dist/system-skills.d.ts +7 -1
  322. package/dist/system-skills.d.ts.map +1 -1
  323. package/dist/system-skills.js +2 -2
  324. package/dist/system-skills.js.map +1 -1
  325. package/dist/timeline-capture.d.ts +21 -0
  326. package/dist/timeline-capture.d.ts.map +1 -0
  327. package/dist/timeline-capture.js +564 -0
  328. package/dist/timeline-capture.js.map +1 -0
  329. package/dist/timeline-code-bake.d.ts +75 -0
  330. package/dist/timeline-code-bake.d.ts.map +1 -0
  331. package/dist/timeline-code-bake.js +327 -0
  332. package/dist/timeline-code-bake.js.map +1 -0
  333. package/dist/timeline-preview/frames.d.ts +26 -0
  334. package/dist/timeline-preview/frames.d.ts.map +1 -1
  335. package/dist/timeline-preview/frames.js +155 -70
  336. package/dist/timeline-preview/frames.js.map +1 -1
  337. package/dist/tools/base-tool.d.ts +1 -0
  338. package/dist/tools/base-tool.d.ts.map +1 -1
  339. package/dist/tools/base-tool.js +1 -1
  340. package/dist/tools/base-tool.js.map +1 -1
  341. package/dist/tools/builtin-tools.d.ts.map +1 -1
  342. package/dist/tools/builtin-tools.js +13 -0
  343. package/dist/tools/builtin-tools.js.map +1 -1
  344. package/dist/tools/external-capability-tools.d.ts +1 -1
  345. package/dist/tools/external-capability-tools.js +1 -1
  346. package/dist/tools/mcp-tool-support.d.ts +10 -9
  347. package/dist/tools/mcp-tool-support.d.ts.map +1 -1
  348. package/dist/tools/mcp-tool-support.js +14 -13
  349. package/dist/tools/mcp-tool-support.js.map +1 -1
  350. package/dist/tools/mcp-tools.d.ts +6 -4
  351. package/dist/tools/mcp-tools.d.ts.map +1 -1
  352. package/dist/tools/mcp-tools.js +65 -62
  353. package/dist/tools/mcp-tools.js.map +1 -1
  354. package/dist/tools/tool-permissions.d.ts +1 -1
  355. package/dist/tools/tool-permissions.js +1 -1
  356. package/package.json +24 -22
  357. package/dist/capabilities/gate-tools.d.ts +0 -18
  358. package/dist/capabilities/gate-tools.d.ts.map +0 -1
  359. package/dist/capabilities/gate-tools.js +0 -66
  360. package/dist/capabilities/gate-tools.js.map +0 -1
  361. package/dist/tsconfig.tests.tmp.tsbuildinfo +0 -1
@@ -17,8 +17,16 @@
17
17
  *
18
18
  * Design: docs/tool-class-retirement-design.md § "Migration".
19
19
  */
20
- import { listTimelinesSpec, createTimelineSpec, getTimelineSpec, listTimelineVersionsSpec, getTimelineVersionSpec, createTimelineVersionSpec, restoreTimelineVersionSpec, deleteTimelineVersionSpec, editTimelineSpec, validateTimelineSpec, setTimelineDocumentSpec, previewTimelineFrameSpec, compareTimelineFramesSpec, renderTimelineSpec, bakeAudioAnimationSpec, isolateSubjectSpec, ISOLATE_SUBJECT_MODELS, ISOLATE_SUBJECT_RESOLUTIONS, DEFAULT_ISOLATE_SUBJECT_MODEL, DEFAULT_ISOLATE_SUBJECT_RESOLUTION, DEFAULT_BAKE_ATTACK_MS, DEFAULT_BAKE_RELEASE_MS, DEFAULT_BAKE_TOLERANCE, DEFAULT_BAKE_MAX_POINTS, DEFAULT_BAKE_MAX_SECONDS, MAX_BAKE_POINTS, DEFAULT_RENDER_TIMEOUT_MS, MAX_RENDER_TIMEOUT_MS, DEFAULT_PREVIEW_COUNT, DEFAULT_COMPARE_WIDTH, MAX_PREVIEW_TIMES, MAX_PREVIEW_RANGE_COUNT, DEFAULT_VERSION_LIMIT, MAX_VERSION_LIMIT, trackObjectSpec, TRACK_OBJECT_DIRECTIONS, deleteTimelineSpec } from "./timelines.specs.js";
21
- import { AUDIO_BAKED_ANIMATION_KIND, normalizeAuthoredDocument } from "@nodetool-ai/timeline";
20
+ import { z } from "zod";
21
+ import { applyTimelineTrackOp } from "@nodetool-ai/timeline/ops";
22
+ import { STAGGER_UNITS, ANIMATED_PROPERTIES, DEFAULT_BEAT_TOLERANCE_MS } from "@nodetool-ai/timeline";
23
+ import { buildTimelineToolContracts } from "@nodetool-ai/protocol/api-schemas/timeline-tool-contracts.js";
24
+ import { resolveMoveTrackArgs, resolveDeleteTrackArgs } from "@nodetool-ai/protocol/api-schemas/timeline-tool-params.js";
25
+ import { parseWithTypeCoercion } from "@nodetool-ai/runtime";
26
+ import { listTimelinesSpec, createTimelineSpec, getTimelineSpec, listTimelineVersionsSpec, getTimelineVersionSpec, createTimelineVersionSpec, restoreTimelineVersionSpec, deleteTimelineVersionSpec, editTimelineSpec, validateTimelineSpec, setTimelineDocumentSpec, previewTimelineFrameSpec, compareTimelineFramesSpec, renderTimelineSpec, bakeAudioAnimationSpec, isolateSubjectSpec, ISOLATE_SUBJECT_MODELS, ISOLATE_SUBJECT_RESOLUTIONS, DEFAULT_ISOLATE_SUBJECT_MODEL, DEFAULT_ISOLATE_SUBJECT_RESOLUTION, DEFAULT_BAKE_ATTACK_MS, DEFAULT_BAKE_RELEASE_MS, DEFAULT_BAKE_TOLERANCE, DEFAULT_BAKE_MAX_POINTS, DEFAULT_BAKE_MAX_SECONDS, MAX_BAKE_POINTS, DEFAULT_RENDER_TIMEOUT_MS, MAX_RENDER_TIMEOUT_MS, DEFAULT_PREVIEW_COUNT, DEFAULT_COMPARE_WIDTH, MAX_PREVIEW_TIMES, MAX_PREVIEW_RANGE_COUNT, DEFAULT_VERSION_LIMIT, MAX_VERSION_LIMIT, trackObjectSpec, TRACK_OBJECT_DIRECTIONS, getTimelineCodeSpec, setTimelineCodeSpec, editTimelineCodeSpec, rebakeTimelineCodeSpec, detachTimelineCodeSpec, deleteTimelineSpec } from "./timelines.specs.js";
27
+ import { AUDIO_BAKED_ANIMATION_KIND, normalizeAuthoredDocument, hashSceneSubtree, mergeTimelineSource } from "@nodetool-ai/timeline";
28
+ import { bakeTimelineCode, bakedDocumentMatchesSaved } from "../timeline-code-bake.js";
29
+ import { applyJsScriptEditOps, parseJsScriptEditOps } from "./js-scripts.js";
22
30
  import { clipSourceWindowMs } from "./timeline-audio-bake.js";
23
31
  import { TIMELINE_NATIVE_MEDIA_CAPABILITIES } from "./timeline-native-media.js";
24
32
  import { isFiniteNumber, isRecord, isString } from "../utils/type-guards.js";
@@ -193,7 +201,7 @@ const getTimeline = {
193
201
  const seq = await loadTimeline(run, params["timeline_id"]);
194
202
  if (isError(seq))
195
203
  return seq;
196
- return { timeline: seq.toTimelineSequence() };
204
+ return { timeline: seq.toTimelineSequence(), revision: seq.revision };
197
205
  }
198
206
  };
199
207
  const listTimelineVersions = {
@@ -367,20 +375,9 @@ const CAS_ATTEMPTS = 2;
367
375
  /** Operations one call may apply, so a runaway script cannot rewrite a cut. */
368
376
  const MAX_OPS = 60;
369
377
  const TOOL_PREFIX = "ui_timeline_";
370
- /**
371
- * Tools the browser bridge exposes that this capability cannot run safely.
372
- *
373
- * `edit_timeline` persists document mutations but has no durable media-edit
374
- * generation adapter or callback path. Exposing generative editing here would
375
- * require it to invent an asset id, so it remains browser-only until a server
376
- * generation runner can create and attach the candidate take. `apply_take`
377
- * stays available because it only promotes an existing candidate.
378
- */
379
- const EXCLUDED_OPS = new Set([
380
- `${TOOL_PREFIX}get_clip_frames`,
381
- `${TOOL_PREFIX}generate_clip`,
382
- `${TOOL_PREFIX}generatively_edit_clip`
383
- ]);
378
+ /** Browser generation without a real provider adapter remains excluded. */
379
+ const EXCLUDED_OPS = new Set([`${TOOL_PREFIX}generate_clip`]);
380
+ const READ_ONLY_OPS = new Set([`${TOOL_PREFIX}get_clip_frames`]);
384
381
  /** `add_track`, `ui_add_track`, and `ui_timeline_add_track` all name one tool. */
385
382
  function normalizeOpName(name) {
386
383
  const trimmed = name.trim();
@@ -392,7 +389,7 @@ function normalizeOpName(name) {
392
389
  return `${TOOL_PREFIX}${bare}`;
393
390
  }
394
391
  /** An op is `{op, ...args}` — the arguments sit alongside the verb. */
395
- function parseOps(raw) {
392
+ export function parseOps(raw) {
396
393
  if (!Array.isArray(raw) || raw.length === 0) {
397
394
  return {
398
395
  error: 'ops must be a non-empty array, e.g. [{"op": "add_track", "type": "audio"}].'
@@ -506,34 +503,11 @@ async function bakeTimelineAnimation(run, request) {
506
503
  return result;
507
504
  }
508
505
  /**
509
- * Run `ops` against a bridge seeded from `document`.
510
- *
511
- * A failing op is recorded and the script continues: stopping at the first
512
- * error hides every problem behind it, and the caller wants the whole picture.
506
+ * The bridge hooks that write outside the document: a Blender render of a 3D
507
+ * clip, and the new sequence `retarget_format` creates.
513
508
  */
514
- async function applyOps(run, sequence, document, ops) {
515
- const { createTimelineToolBridge } = await import("../evals/surfaces/timeline.js");
516
- const bridge = createTimelineToolBridge({
517
- sequenceId: sequence.id,
518
- sequenceName: sequence.name,
519
- projectId: sequence.project_id,
520
- sequence: {
521
- fps: sequence.fps,
522
- width: sequence.width,
523
- height: sequence.height,
524
- tracks: document.tracks,
525
- clips: document.clips,
526
- markers: document.markers,
527
- transcript: document.transcript,
528
- scriptEnabled: document.scriptEnabled,
529
- templateId: document.templateId,
530
- tempo: document.tempo,
531
- camera2d: document.camera2d,
532
- setup: document.setup,
533
- mediaTracks: document.mediaTracks
534
- },
535
- resolveAsset: (ref) => resolveTimelineAsset(run, ref),
536
- bakeAnimation: (request) => bakeTimelineAnimation(run, request),
509
+ function writingHooks(run, sequence) {
510
+ return {
537
511
  bakeModel3DClip: async (request) => {
538
512
  const { bakeModel3DClipOnServer } = await import("./timeline-bake.js");
539
513
  return bakeModel3DClipOnServer(run.context, request);
@@ -576,7 +550,156 @@ async function applyOps(run, sequence, document, ops) {
576
550
  document: JSON.stringify(derivedDocument)
577
551
  });
578
552
  return { sequenceId: created.id, name };
553
+ }
554
+ };
555
+ }
556
+ /** Track-only batches need no simulated editor or external hooks. */
557
+ function applyTrackOps(sequence, document, ops) {
558
+ const contracts = buildTimelineToolContracts({
559
+ staggerUnits: STAGGER_UNITS,
560
+ animatedProperties: ANIMATED_PROPERTIES,
561
+ beatToleranceMs: DEFAULT_BEAT_TOLERANCE_MS
562
+ });
563
+ let state = {
564
+ fps: sequence.fps,
565
+ width: sequence.width,
566
+ height: sequence.height,
567
+ tracks: document.tracks,
568
+ clips: document.clips,
569
+ markers: document.markers,
570
+ mediaTracks: document.mediaTracks,
571
+ tempo: document.tempo,
572
+ playheadMs: 0,
573
+ selectedClipIds: []
574
+ };
575
+ const usedIds = new Set([
576
+ ...[
577
+ ...document.tracks,
578
+ ...document.clips,
579
+ ...document.markers,
580
+ ...(document.mediaTracks ?? [])
581
+ ].map((unit) => unit.id),
582
+ ...document.clips.flatMap((clip) => (clip.animations ?? []).map((animation) => animation.id))
583
+ ]);
584
+ let nextId = 0;
585
+ const newId = () => {
586
+ let id;
587
+ do {
588
+ id = `track_${++nextId}`;
589
+ } while (usedIds.has(id));
590
+ usedIds.add(id);
591
+ return id;
592
+ };
593
+ const records = [];
594
+ for (const entry of ops) {
595
+ try {
596
+ let op;
597
+ if (entry.op === "ui_timeline_add_track") {
598
+ const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_add_track.shape), entry.input);
599
+ op = { op: "add_track", ...args };
600
+ }
601
+ else if (entry.op === "ui_timeline_move_track") {
602
+ const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_move_track.shape), entry.input);
603
+ op = { op: "move_track", ...resolveMoveTrackArgs(args) };
604
+ }
605
+ else {
606
+ const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_delete_track.shape), entry.input);
607
+ op = { op: "delete_track", ...resolveDeleteTrackArgs(args) };
608
+ }
609
+ const outcome = applyTimelineTrackOp(state, op, { newId });
610
+ if (outcome.error)
611
+ throw new Error(outcome.error);
612
+ state = outcome.state;
613
+ records.push({ op: entry.op, ok: true, result: outcome.result });
614
+ }
615
+ catch (error) {
616
+ records.push({
617
+ op: entry.op,
618
+ ok: false,
619
+ error: error instanceof Error ? error.message : String(error)
620
+ });
621
+ }
622
+ }
623
+ return {
624
+ records,
625
+ state: {
626
+ fps: state.fps,
627
+ width: state.width,
628
+ height: state.height,
629
+ durationMs: state.clips.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs), 0),
630
+ playheadMs: state.playheadMs,
631
+ tracks: state.tracks.map(({ id, name, type, index }) => ({
632
+ id,
633
+ name,
634
+ type,
635
+ index
636
+ })),
637
+ clips: state.clips.map((clip) => ({
638
+ id: clip.id,
639
+ name: clip.name,
640
+ trackId: clip.trackId,
641
+ mediaType: clip.mediaType,
642
+ startMs: clip.startMs,
643
+ durationMs: clip.durationMs,
644
+ prompt: clip.prompt,
645
+ animations: (clip.animations ?? []).map(({ role, preset }) => ({
646
+ role,
647
+ preset
648
+ }))
649
+ })),
650
+ documentTracks: state.tracks,
651
+ documentClips: state.clips,
652
+ markers: state.markers,
653
+ mediaTracks: state.mediaTracks ?? [],
654
+ derivedSequences: [],
655
+ tempo: state.tempo,
656
+ camera2d: document.camera2d ?? null,
657
+ setup: document.setup ?? null,
658
+ transitionCandidates: [],
659
+ appliedTransitionCandidates: [],
660
+ toolLog: ops.map((entry) => entry.op),
661
+ previewTimesMs: [],
662
+ previewedLayerKinds: []
663
+ }
664
+ };
665
+ }
666
+ /**
667
+ * Run `ops` against a bridge seeded from `document`.
668
+ *
669
+ * A failing op is recorded and the script continues: stopping at the first
670
+ * error hides every problem behind it, and the caller wants the whole picture.
671
+ *
672
+ * `hermetic` leaves out the hooks that write outside the document: the 3D
673
+ * render and the new sequence a retarget creates. A code bake sets it. The
674
+ * bridge then uses its in-memory fallbacks for those ops.
675
+ */
676
+ export async function applyOps(run, sequence, document, ops, options = {}) {
677
+ if (ops.every(({ op }) => op === "ui_timeline_add_track" || op === "ui_timeline_move_track" || op === "ui_timeline_delete_track")) {
678
+ return applyTrackOps(sequence, document, ops);
679
+ }
680
+ const { createTimelineToolBridge } = await import("./timeline-bridge.js");
681
+ let operationIndex = 0;
682
+ const init = {
683
+ sequenceId: sequence.id,
684
+ sequenceName: sequence.name,
685
+ projectId: sequence.project_id,
686
+ sequence: {
687
+ fps: sequence.fps,
688
+ width: sequence.width,
689
+ height: sequence.height,
690
+ tracks: document.tracks,
691
+ clips: document.clips,
692
+ markers: document.markers,
693
+ transcript: document.transcript,
694
+ scriptEnabled: document.scriptEnabled,
695
+ templateId: document.templateId,
696
+ tempo: document.tempo,
697
+ camera2d: document.camera2d,
698
+ setup: document.setup,
699
+ mediaTracks: document.mediaTracks
579
700
  },
701
+ resolveAsset: (ref) => resolveTimelineAsset(run, ref),
702
+ bakeAnimation: (request) => bakeTimelineAnimation(run, request),
580
703
  loadComposition: {
581
704
  get: async (id) => {
582
705
  const { loadComposition } = await import("./compositions.js");
@@ -592,12 +715,31 @@ async function applyOps(run, sequence, document, ops) {
592
715
  ];
593
716
  }
594
717
  }
595
- });
718
+ };
719
+ if (!options.hermetic) {
720
+ Object.assign(init, writingHooks(run, sequence));
721
+ const { timelineMediaEditGenerator } = await import("./timeline-media-edit.js");
722
+ const generate = options.generateMediaEdit ?? timelineMediaEditGenerator(run);
723
+ init.generateMediaEdit = (request) => generate(request, operationIndex);
724
+ init.getClipFrames = async (clip, frameOptions) => {
725
+ const current = bridge.finalState();
726
+ const { inspectTimelineClipFrames } = await import("./timeline-clip-frames.js");
727
+ return inspectTimelineClipFrames(run.context, { ...sequence.toDocument(),
728
+ tracks: current.documentTracks, clips: current.documentClips, markers: current.markers,
729
+ mediaTracks: current.mediaTracks, tempo: current.tempo, camera2d: current.camera2d,
730
+ id: sequence.id, projectId: sequence.project_id ?? "default", name: sequence.name,
731
+ fps: sequence.fps, width: sequence.width, height: sequence.height,
732
+ durationMs: sequence.duration_ms, createdAt: sequence.created_at, updatedAt: sequence.updated_at
733
+ }, clip, frameOptions);
734
+ };
735
+ }
736
+ const bridge = createTimelineToolBridge(init);
596
737
  const byName = new Map(bridge.tools
597
- .filter((tool) => !EXCLUDED_OPS.has(tool.name))
738
+ .filter((tool) => !EXCLUDED_OPS.has(tool.name) && !(options.hermetic && (tool.name === `${TOOL_PREFIX}generatively_edit_clip` || READ_ONLY_OPS.has(tool.name))))
598
739
  .map((tool) => [tool.name, tool]));
599
740
  const records = [];
600
- for (const { op, input } of ops) {
741
+ for (const [index, { op, input }] of ops.entries()) {
742
+ operationIndex = index;
601
743
  const tool = byName.get(op);
602
744
  if (!tool) {
603
745
  const known = [...byName.keys()]
@@ -741,7 +883,9 @@ const editTimeline = {
741
883
  "the returned timeline id."
742
884
  };
743
885
  }
744
- const { TimelineSequence } = await import("@nodetool-ai/models");
886
+ const { TimelineSequence, TimelineSequenceVersion } = await import("@nodetool-ai/models");
887
+ const { timelineMediaEditGenerator } = await import("./timeline-media-edit.js");
888
+ const generateMediaEdit = timelineMediaEditGenerator(run);
745
889
  for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
746
890
  const sequence = await TimelineSequence.findById(timelineId);
747
891
  // A sequence owned by someone else reads as missing — the rule the tRPC
@@ -750,7 +894,7 @@ const editTimeline = {
750
894
  return { error: `Timeline ${timelineId} was not found.` };
751
895
  }
752
896
  const document = sequence.toDocument();
753
- const { records, state } = await applyOps(run, sequence, document, ops);
897
+ const { records, state } = await applyOps(run, sequence, document, ops, { generateMediaEdit });
754
898
  // The transcript rides along untouched — no timeline operation edits it,
755
899
  // so the stored copy stays authoritative. Markers come back from the
756
900
  // bridge, which seeded them and which the marker ops write to.
@@ -784,12 +928,12 @@ const editTimeline = {
784
928
  // hand an open editor a merge with nothing in it.
785
929
  const appliedOps = ops
786
930
  .map((parsed, index) => ({ parsed, index }))
787
- .filter(({ index }) => records[index]?.ok);
931
+ .filter(({ parsed, index }) => records[index]?.ok && !READ_ONLY_OPS.has(parsed.op));
788
932
  if (appliedOps.length === 0) {
789
933
  return {
790
934
  timeline_id: timelineId,
791
935
  updated_at: sequence.updated_at,
792
- applied: 0,
936
+ applied: records.filter((record) => record.ok).length,
793
937
  failed: failed.length,
794
938
  ops: records,
795
939
  tracks: state.tracks,
@@ -826,15 +970,23 @@ const editTimeline = {
826
970
  }))
827
971
  };
828
972
  }
973
+ const undo = appliedOps.some(({ parsed }) => parsed.op === `${TOOL_PREFIX}apply_take`)
974
+ ? await TimelineSequenceVersion.snapshot(sequence, { saveType: "manual", name: "Before applying AI edit take" })
975
+ : undefined;
829
976
  const saved = await TimelineSequence.updateDocumentIfUnchanged(sequence.id, sequence.updated_at, next, {
830
977
  ops: appliedOps.map(({ parsed, index }) => ({
831
978
  tool: parsed.op,
832
979
  input: resolveTimelineOpInput(parsed.input, document, state, records[index]?.result)
833
980
  }))
981
+ }).catch(async (error) => {
982
+ await undo?.delete();
983
+ throw error;
834
984
  });
835
- if (!saved)
985
+ if (!saved) {
986
+ await undo?.delete();
836
987
  continue;
837
- return {
988
+ }
989
+ const response = {
838
990
  timeline_id: timelineId,
839
991
  updated_at: saved.updated_at,
840
992
  applied: records.length - failed.length,
@@ -851,6 +1003,10 @@ const editTimeline = {
851
1003
  animations: clip.animations
852
1004
  }))
853
1005
  };
1006
+ if (undo) {
1007
+ Object.assign(response, { undo_version: undo.version });
1008
+ }
1009
+ return response;
854
1010
  }
855
1011
  return {
856
1012
  error: `Timeline ${timelineId} is being modified concurrently; nothing was saved. Retry the call.`
@@ -1333,6 +1489,7 @@ const previewTimelineFrame = {
1333
1489
  mime: "image/png"
1334
1490
  });
1335
1491
  sheet = {
1492
+ complete: result.complete,
1336
1493
  columns: tiled.columns,
1337
1494
  rows: tiled.rows,
1338
1495
  cells: tiled.cells,
@@ -1346,12 +1503,15 @@ const previewTimelineFrame = {
1346
1503
  const frames = [];
1347
1504
  for (const frame of result.frames) {
1348
1505
  const entry = {
1506
+ complete: frame.complete,
1349
1507
  time_ms: frame.time_ms,
1350
1508
  width: frame.width,
1351
1509
  height: frame.height,
1352
1510
  layers: frame.layers,
1511
+ failures: frame.failures,
1353
1512
  dropped: frame.dropped,
1354
- degraded: frame.degraded
1513
+ degraded: frame.degraded,
1514
+ degradations: frame.degraded
1355
1515
  };
1356
1516
  if (!wantSheet) {
1357
1517
  const saved = await persistOutput(run.context, frame.png, {
@@ -1363,6 +1523,8 @@ const previewTimelineFrame = {
1363
1523
  frames.push(entry);
1364
1524
  }
1365
1525
  return {
1526
+ complete: result.complete,
1527
+ visual_output_verified: false,
1366
1528
  timeline_id: isString(timelineId) ? timelineId : undefined,
1367
1529
  name,
1368
1530
  fps: meta.fps,
@@ -1372,12 +1534,15 @@ const previewTimelineFrame = {
1372
1534
  frames,
1373
1535
  sheet,
1374
1536
  effects_not_applied: result.effectsNotApplied,
1375
- hint: wantSheet
1537
+ fonts_unavailable: result.fontsUnavailable,
1538
+ hint: (!result.complete
1539
+ ? "Incomplete preview: requested content was skipped or approximated. Inspect failures, dropped, degradations, skipped layers, effects_not_applied and fonts_unavailable. This image cannot verify the complete composition. "
1540
+ : "") + (wantSheet
1376
1541
  ? "Call view_image with the sheet's asset_id to see every frame at " +
1377
1542
  "once; the cells run left to right, labelled with their timecode. " +
1378
1543
  "Each frame's layers are listed top of the stack first."
1379
1544
  : "Call view_image with a frame's asset_id to see it. The layers are " +
1380
- "listed top of the stack first — the first one covers the rest."
1545
+ "listed top of the stack first — the first one covers the rest.")
1381
1546
  };
1382
1547
  }
1383
1548
  };
@@ -1510,10 +1675,15 @@ const compareTimelineFrames = {
1510
1675
  const frames = [];
1511
1676
  const cells = [];
1512
1677
  for (let i = 0; i < times.length; i++) {
1513
- const difference = Number((await frameDifference(left.frames[i].png, right.frames[i].png)).toFixed(4));
1514
- frames.push({ time_ms: times[i], difference });
1678
+ const complete = left.frames[i].complete && right.frames[i].complete;
1679
+ const difference = complete ? Number((await frameDifference(left.frames[i].png, right.frames[i].png)).toFixed(4)) : null;
1680
+ frames.push({
1681
+ time_ms: times[i], complete, difference,
1682
+ a: { failures: left.frames[i].failures, dropped: left.frames[i].dropped, degradations: left.frames[i].degraded, skipped: left.frames[i].layers.filter((layer) => layer.skipped) },
1683
+ b: { failures: right.frames[i].failures, dropped: right.frames[i].dropped, degradations: right.frames[i].degraded, skipped: right.frames[i].layers.filter((layer) => layer.skipped) }
1684
+ });
1515
1685
  cells.push({
1516
- label: `${times[i]}ms ${difference.toFixed(3)}`,
1686
+ label: `${times[i]}ms ${difference === null ? "incomplete" : difference.toFixed(3)}`,
1517
1687
  png: await sideBySide(left.frames[i].png, right.frames[i].png)
1518
1688
  });
1519
1689
  }
@@ -1522,16 +1692,20 @@ const compareTimelineFrames = {
1522
1692
  namePrefix: "timeline-compare",
1523
1693
  mime: "image/png"
1524
1694
  });
1525
- const differences = frames.map((frame) => frame.difference);
1526
- const changed = frames.filter((frame) => frame.difference > 0);
1695
+ const complete = left.complete && right.complete;
1696
+ const differences = frames.flatMap((frame) => frame.difference === null ? [] : [frame.difference]);
1697
+ const changed = frames.filter((frame) => frame.difference !== null && frame.difference > 0);
1527
1698
  return {
1699
+ complete,
1700
+ visual_output_verified: false,
1528
1701
  a: a.source,
1529
1702
  b: b.source,
1530
1703
  frames,
1531
- changed_times_ms: changed.map((frame) => frame.time_ms),
1532
- max_difference: Math.max(...differences),
1533
- mean_difference: Number((differences.reduce((sum, d) => sum + d, 0) / differences.length).toFixed(4)),
1704
+ changed_times_ms: complete ? changed.map((frame) => frame.time_ms) : null,
1705
+ max_difference: complete ? Math.max(...differences) : null,
1706
+ mean_difference: complete ? Number((differences.reduce((sum, d) => sum + d, 0) / differences.length).toFixed(4)) : null,
1534
1707
  sheet: {
1708
+ complete,
1535
1709
  columns: tiled.columns,
1536
1710
  rows: tiled.rows,
1537
1711
  cells: tiled.cells,
@@ -1542,7 +1716,11 @@ const compareTimelineFrames = {
1542
1716
  effects_not_applied: [
1543
1717
  ...new Set([...left.effectsNotApplied, ...right.effectsNotApplied])
1544
1718
  ].sort(),
1545
- hint: "difference is the mean absolute RGB difference, 0 (identical) to 1. " +
1719
+ fonts_unavailable: [
1720
+ ...new Set([...left.fontsUnavailable, ...right.fontsUnavailable])
1721
+ ].sort(),
1722
+ hint: (!complete ? "Incomplete previews cannot establish visual equivalence. Difference scores are null for incomplete pairs and aggregate metrics are unavailable. " : "") +
1723
+ "difference is the mean absolute RGB difference, 0 (identical) to 1. " +
1546
1724
  "Each cell of the sheet is a pair: `a` on the left, `b` on the right, " +
1547
1725
  "labelled with its timecode and score."
1548
1726
  };
@@ -2283,6 +2461,322 @@ const trackObject = {
2283
2461
  return result;
2284
2462
  }
2285
2463
  };
2464
+ /**
2465
+ * The report shape `get_timeline_code`, `set_timeline_code`, `edit_timeline_code`
2466
+ * and `rebake_timeline_code` all answer for `scenes`: `edited` means "kept
2467
+ * as-is rather than reflecting the code" — either the code has never been
2468
+ * baked into a current merge (`get`), or this call's own merge kept it as a
2469
+ * conflict (`set`/`edit`/`rebake`).
2470
+ */
2471
+ function sceneReport(scenes, editedNames) {
2472
+ return Object.entries(scenes).map(([name, recorded]) => ({
2473
+ name,
2474
+ group_id: recorded.groupId,
2475
+ edited: editedNames.has(name)
2476
+ }));
2477
+ }
2478
+ /** `force` is a boolean or an array of scene names; anything else is refused. */
2479
+ function normalizeForceOption(value) {
2480
+ if (value === undefined)
2481
+ return undefined;
2482
+ if (value === true)
2483
+ return true;
2484
+ if (value === false)
2485
+ return undefined;
2486
+ if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
2487
+ return value;
2488
+ }
2489
+ return { error: "force must be a boolean or an array of scene names." };
2490
+ }
2491
+ /**
2492
+ * Validate, snapshot, and CAS-write a document the way `set_timeline_document`
2493
+ * does (see its own docstring for why each step is there): validate first so
2494
+ * a document that would not render never reaches the database, snapshot the
2495
+ * state being replaced so the write is undoable, then a CAS write so a
2496
+ * concurrent edit is reported rather than clobbered.
2497
+ */
2498
+ export async function writeTimelineCodeDocument(sequence, document, snapshotName) {
2499
+ const { validateTimelineSequence } = await import("@nodetool-ai/execution/timeline-debug");
2500
+ const before = validateTimelineSequence(document, { fps: sequence.fps, width: sequence.width, height: sequence.height });
2501
+ const errors = before.errors.map((issue) => issue.message);
2502
+ const warnings = before.warnings.map((issue) => issue.message);
2503
+ if (!before.ok) {
2504
+ return {
2505
+ written: false,
2506
+ error: `The baked document has ${before.errors.length} error${before.errors.length === 1 ? "" : "s"} and was not written to timeline ${sequence.id}.`,
2507
+ errors,
2508
+ warnings
2509
+ };
2510
+ }
2511
+ const { TimelineSequence: TimelineSequenceModel, TimelineSequenceVersion } = await import("@nodetool-ai/models");
2512
+ await TimelineSequenceVersion.snapshot(sequence, {
2513
+ saveType: "manual",
2514
+ name: snapshotName
2515
+ });
2516
+ const saved = await TimelineSequenceModel.updateFieldsIfUnchanged(sequence.id, sequence.updated_at, {
2517
+ document: JSON.stringify(document),
2518
+ duration_ms: documentDurationMs(document)
2519
+ });
2520
+ if (!saved) {
2521
+ return {
2522
+ written: false,
2523
+ error: `Timeline ${sequence.id} was modified concurrently; nothing was written. Read it again and try again.`,
2524
+ errors,
2525
+ warnings
2526
+ };
2527
+ }
2528
+ return { written: true, timeline_id: saved.id, errors, warnings };
2529
+ }
2530
+ /**
2531
+ * Bake `code`, merge the result into `sequence`'s current document (see
2532
+ * `mergeTimelineSource` in `@nodetool-ai/timeline` for the merge rule), and
2533
+ * write it through the same path `set_timeline_document` uses. Shared by
2534
+ * `set_timeline_code`, `edit_timeline_code` and `rebake_timeline_code` — they
2535
+ * differ only in where `code` comes from.
2536
+ */
2537
+ async function bakeAndMergeTimelineCode(run, sequence, code, force) {
2538
+ const current = sequence.toDocument();
2539
+ const baked = await bakeTimelineCode(run.context, code);
2540
+ if (!baked.ok || !baked.document) {
2541
+ return {
2542
+ timeline_id: sequence.id,
2543
+ errors: [baked.error ?? "The code failed to build a document."],
2544
+ warnings: [],
2545
+ conflicts: [],
2546
+ scenes: []
2547
+ };
2548
+ }
2549
+ const previousScenes = current.source?.scenes ?? {};
2550
+ const merged = mergeTimelineSource(current, baked.document, previousScenes, force !== undefined ? { force } : {});
2551
+ const mergedWithSource = {
2552
+ ...merged.document,
2553
+ source: {
2554
+ lang: "js",
2555
+ code,
2556
+ bakedAt: new Date().toISOString(),
2557
+ scenes: merged.scenes
2558
+ }
2559
+ };
2560
+ const editedNames = new Set(merged.conflicts.map((c) => c.scene));
2561
+ const scenes = sceneReport(merged.scenes, editedNames);
2562
+ const write = await writeTimelineCodeDocument(sequence, mergedWithSource, "Before writing timeline code");
2563
+ if (!write.written) {
2564
+ return {
2565
+ timeline_id: sequence.id,
2566
+ errors: [write.error, ...write.errors],
2567
+ warnings: write.warnings,
2568
+ conflicts: merged.conflicts,
2569
+ scenes
2570
+ };
2571
+ }
2572
+ return {
2573
+ timeline_id: write.timeline_id,
2574
+ errors: write.errors,
2575
+ warnings: write.warnings,
2576
+ conflicts: merged.conflicts,
2577
+ scenes
2578
+ };
2579
+ }
2580
+ /**
2581
+ * Attach `code` as `sequence`'s authoring source only when a hermetic bake
2582
+ * of it gives exactly the document that is saved now. This is
2583
+ * `set_timeline_code` with `require_match: true`, which `v.save()` calls
2584
+ * with the retained program it printed (docs/timeline-code-capture.md).
2585
+ *
2586
+ * A mismatch writes nothing and returns a warning. The document stays as it
2587
+ * was saved, only without code, because code that does not rebuild it would
2588
+ * make a later rebake diverge from what the timeline shows.
2589
+ */
2590
+ async function attachMatchingTimelineCode(run, sequence, code) {
2591
+ const current = sequence.toDocument();
2592
+ const notEmbedded = (warning) => ({
2593
+ timeline_id: sequence.id,
2594
+ embedded: false,
2595
+ errors: [],
2596
+ warnings: [warning],
2597
+ conflicts: [],
2598
+ scenes: []
2599
+ });
2600
+ const baked = await bakeTimelineCode(run.context, code);
2601
+ if (!baked.ok || !baked.document) {
2602
+ return notEmbedded(`Code not embedded: the retained program failed to rebake (${baked.error ?? "unknown error"}).`);
2603
+ }
2604
+ if (!bakedDocumentMatchesSaved(current, baked.document)) {
2605
+ return notEmbedded("Code not embedded: the retained program rebakes to a different " +
2606
+ "timeline than the one saved. A scene clip probably changed after " +
2607
+ "v.series(), or a value came from Math.random() or Date.now().");
2608
+ }
2609
+ // Every scene is new here: the saved document was just written from this
2610
+ // same run, so there are no hand edits to keep.
2611
+ const merged = mergeTimelineSource(current, baked.document, {}, { force: true });
2612
+ const write = await writeTimelineCodeDocument(sequence, {
2613
+ ...merged.document,
2614
+ source: {
2615
+ lang: "js",
2616
+ code,
2617
+ bakedAt: new Date().toISOString(),
2618
+ scenes: merged.scenes
2619
+ }
2620
+ }, "Attach authoring code");
2621
+ if (!write.written) {
2622
+ return notEmbedded(`Code not embedded: ${write.error}`);
2623
+ }
2624
+ return {
2625
+ timeline_id: write.timeline_id,
2626
+ embedded: true,
2627
+ errors: write.errors,
2628
+ warnings: write.warnings,
2629
+ conflicts: [],
2630
+ scenes: sceneReport(merged.scenes, new Set())
2631
+ };
2632
+ }
2633
+ const getTimelineCode = {
2634
+ spec: getTimelineCodeSpec,
2635
+ impl: async (run, params) => {
2636
+ const sequence = await loadTimeline(run, params["timeline_id"]);
2637
+ if (isError(sequence))
2638
+ return sequence;
2639
+ const doc = sequence.toDocument();
2640
+ const source = doc.source;
2641
+ if (!source)
2642
+ return { code: null, baked_at: null, scenes: [] };
2643
+ const groupIdByScene = new Map();
2644
+ for (const clip of doc.clips) {
2645
+ if (typeof clip.sourceScene === "string" && clip.sourceScene) {
2646
+ groupIdByScene.set(clip.sourceScene, clip.id);
2647
+ }
2648
+ }
2649
+ const editedNames = new Set();
2650
+ for (const [name, recorded] of Object.entries(source.scenes)) {
2651
+ const groupId = groupIdByScene.get(name);
2652
+ const edited = groupId === undefined ||
2653
+ hashSceneSubtree(doc.clips, groupId) !== recorded.hash;
2654
+ if (edited)
2655
+ editedNames.add(name);
2656
+ }
2657
+ return {
2658
+ code: source.code,
2659
+ baked_at: source.bakedAt,
2660
+ scenes: sceneReport(source.scenes, editedNames)
2661
+ };
2662
+ }
2663
+ };
2664
+ const setTimelineCode = {
2665
+ spec: setTimelineCodeSpec,
2666
+ impl: async (run, params) => {
2667
+ const sequence = await loadTimeline(run, params["timeline_id"]);
2668
+ if (isError(sequence))
2669
+ return sequence;
2670
+ const code = params["code"];
2671
+ if (!isString(code) || !code.trim()) {
2672
+ return { error: "code is required and must be a non-empty script." };
2673
+ }
2674
+ if (params["require_match"] === true) {
2675
+ return attachMatchingTimelineCode(run, sequence, code);
2676
+ }
2677
+ const force = normalizeForceOption(params["force"]);
2678
+ if (isError(force))
2679
+ return force;
2680
+ return bakeAndMergeTimelineCode(run, sequence, code, force);
2681
+ }
2682
+ };
2683
+ const editTimelineCode = {
2684
+ spec: editTimelineCodeSpec,
2685
+ impl: async (run, params) => {
2686
+ const sequence = await loadTimeline(run, params["timeline_id"]);
2687
+ if (isError(sequence))
2688
+ return sequence;
2689
+ const source = sequence.toDocument().source;
2690
+ if (!source) {
2691
+ return {
2692
+ error: `Timeline ${sequence.id} has no authoring code to edit. Call set_timeline_code first.`
2693
+ };
2694
+ }
2695
+ const ops = parseJsScriptEditOps(params["edits"]);
2696
+ if (isError(ops))
2697
+ return ops;
2698
+ const applied = applyJsScriptEditOps(source.code, ops);
2699
+ if (isError(applied))
2700
+ return applied;
2701
+ const force = normalizeForceOption(params["force"]);
2702
+ if (isError(force))
2703
+ return force;
2704
+ return bakeAndMergeTimelineCode(run, sequence, applied.code, force);
2705
+ }
2706
+ };
2707
+ const rebakeTimelineCode = {
2708
+ spec: rebakeTimelineCodeSpec,
2709
+ impl: async (run, params) => {
2710
+ const sequence = await loadTimeline(run, params["timeline_id"]);
2711
+ if (isError(sequence))
2712
+ return sequence;
2713
+ const source = sequence.toDocument().source;
2714
+ if (!source) {
2715
+ return { error: `Timeline ${sequence.id} has no authoring code to rebake.` };
2716
+ }
2717
+ const force = normalizeForceOption(params["force"]);
2718
+ if (isError(force))
2719
+ return force;
2720
+ return bakeAndMergeTimelineCode(run, sequence, source.code, force);
2721
+ }
2722
+ };
2723
+ const detachTimelineCode = {
2724
+ spec: detachTimelineCodeSpec,
2725
+ impl: async (run, params) => {
2726
+ const sequence = await loadTimeline(run, params["timeline_id"]);
2727
+ if (isError(sequence))
2728
+ return sequence;
2729
+ const current = sequence.toDocument();
2730
+ const source = current.source;
2731
+ if (!source)
2732
+ return { scenes: [] };
2733
+ const scenesParam = params["scenes"];
2734
+ // "all" drops the code entirely — there is nothing left to track, and
2735
+ // nothing left worth rebaking. Naming every currently-tracked scene one
2736
+ // by one is a different request: those scenes stop being tracked, but
2737
+ // the code stays, so a later edit that reintroduces a scene (or adds a
2738
+ // new one) still has somewhere to bake into.
2739
+ const dropCode = scenesParam === "all";
2740
+ let detachedNames;
2741
+ if (dropCode) {
2742
+ detachedNames = Object.keys(source.scenes);
2743
+ }
2744
+ else if (Array.isArray(scenesParam) &&
2745
+ scenesParam.length > 0 &&
2746
+ scenesParam.every((s) => typeof s === "string")) {
2747
+ detachedNames = scenesParam.filter((name) => name in source.scenes);
2748
+ }
2749
+ else {
2750
+ return {
2751
+ error: 'scenes must be "all" or a non-empty array of scene names.'
2752
+ };
2753
+ }
2754
+ if (detachedNames.length === 0)
2755
+ return { scenes: [] };
2756
+ const remaining = { ...source.scenes };
2757
+ for (const name of detachedNames)
2758
+ delete remaining[name];
2759
+ const nextDocument = { ...current };
2760
+ if (dropCode) {
2761
+ delete nextDocument["source"];
2762
+ }
2763
+ else {
2764
+ nextDocument["source"] = { ...source, scenes: remaining };
2765
+ }
2766
+ const { TimelineSequence: TimelineSequenceModel, TimelineSequenceVersion } = await import("@nodetool-ai/models");
2767
+ await TimelineSequenceVersion.snapshot(sequence, {
2768
+ saveType: "manual",
2769
+ name: "Before detaching timeline code"
2770
+ });
2771
+ const saved = await TimelineSequenceModel.updateFieldsIfUnchanged(sequence.id, sequence.updated_at, { document: JSON.stringify(nextDocument) });
2772
+ if (!saved) {
2773
+ return {
2774
+ error: `Timeline ${sequence.id} was modified concurrently; nothing was written. Try again.`
2775
+ };
2776
+ }
2777
+ return { timeline_id: saved.id, scenes: detachedNames };
2778
+ }
2779
+ };
2286
2780
  const deleteTimeline = {
2287
2781
  spec: deleteTimelineSpec,
2288
2782
  impl: async (run, params) => {
@@ -2316,11 +2810,16 @@ export const TIMELINE_CAPABILITIES = [
2316
2810
  isolateSubject,
2317
2811
  trackObject,
2318
2812
  ...TIMELINE_NATIVE_MEDIA_CAPABILITIES,
2813
+ getTimelineCode,
2814
+ setTimelineCode,
2815
+ editTimelineCode,
2816
+ rebakeTimelineCode,
2817
+ detachTimelineCode,
2319
2818
  deleteTimeline
2320
2819
  ];
2321
2820
  export const module = {
2322
2821
  module: "timelines",
2323
2822
  exports: TIMELINE_CAPABILITIES
2324
2823
  };
2325
- export { listTimelines, createTimeline, getTimeline, listTimelineVersions, getTimelineVersion, createTimelineVersion, restoreTimelineVersion, deleteTimelineVersion, editTimeline, validateTimeline, setTimelineDocument, previewTimelineFrame, compareTimelineFrames, renderTimeline, bakeAudioAnimation, isolateSubject, trackObject, deleteTimeline };
2824
+ export { listTimelines, createTimeline, getTimeline, listTimelineVersions, getTimelineVersion, createTimelineVersion, restoreTimelineVersion, deleteTimelineVersion, editTimeline, validateTimeline, setTimelineDocument, previewTimelineFrame, compareTimelineFrames, renderTimeline, bakeAudioAnimation, isolateSubject, trackObject, getTimelineCode, setTimelineCode, editTimelineCode, rebakeTimelineCode, detachTimelineCode, deleteTimeline };
2326
2825
  //# sourceMappingURL=timelines.js.map