@nodetool-ai/agents 0.7.0-rc.22 → 0.8.1

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 (1554) hide show
  1. package/README.md +35 -0
  2. package/dist/.nodetool-build-stamp +1 -0
  3. package/dist/analysis/audio-dsp.d.ts +223 -0
  4. package/dist/analysis/audio-dsp.d.ts.map +1 -0
  5. package/dist/analysis/audio-dsp.js +642 -0
  6. package/dist/analysis/audio-dsp.js.map +1 -0
  7. package/dist/analysis/media-decode.d.ts +85 -0
  8. package/dist/analysis/media-decode.d.ts.map +1 -0
  9. package/dist/analysis/media-decode.js +199 -0
  10. package/dist/analysis/media-decode.js.map +1 -0
  11. package/dist/analysis/mediabunny-runtime.d.ts +15 -0
  12. package/dist/analysis/mediabunny-runtime.d.ts.map +1 -0
  13. package/dist/analysis/mediabunny-runtime.js +30 -0
  14. package/dist/analysis/mediabunny-runtime.js.map +1 -0
  15. package/dist/analysis/video-frames.d.ts +120 -0
  16. package/dist/analysis/video-frames.d.ts.map +1 -0
  17. package/dist/analysis/video-frames.js +288 -0
  18. package/dist/analysis/video-frames.js.map +1 -0
  19. package/dist/apify/assets.d.ts +80 -0
  20. package/dist/apify/assets.d.ts.map +1 -0
  21. package/dist/apify/assets.js +192 -0
  22. package/dist/apify/assets.js.map +1 -0
  23. package/dist/apify/catalog.d.ts +44 -0
  24. package/dist/apify/catalog.d.ts.map +1 -0
  25. package/dist/apify/catalog.js +108 -0
  26. package/dist/apify/catalog.js.map +1 -0
  27. package/dist/apify/client.d.ts +137 -0
  28. package/dist/apify/client.d.ts.map +1 -0
  29. package/dist/apify/client.js +346 -0
  30. package/dist/apify/client.js.map +1 -0
  31. package/dist/apify/errors.d.ts +85 -0
  32. package/dist/apify/errors.d.ts.map +1 -0
  33. package/dist/apify/errors.js +90 -0
  34. package/dist/apify/errors.js.map +1 -0
  35. package/dist/apify/normalize.d.ts +131 -0
  36. package/dist/apify/normalize.d.ts.map +1 -0
  37. package/dist/apify/normalize.js +202 -0
  38. package/dist/apify/normalize.js.map +1 -0
  39. package/dist/apify/policy.d.ts +136 -0
  40. package/dist/apify/policy.d.ts.map +1 -0
  41. package/dist/apify/policy.js +223 -0
  42. package/dist/apify/policy.js.map +1 -0
  43. package/dist/apify/run.d.ts +86 -0
  44. package/dist/apify/run.d.ts.map +1 -0
  45. package/dist/apify/run.js +158 -0
  46. package/dist/apify/run.js.map +1 -0
  47. package/dist/app-build/author.d.ts +67 -0
  48. package/dist/app-build/author.d.ts.map +1 -0
  49. package/dist/app-build/author.js +231 -0
  50. package/dist/app-build/author.js.map +1 -0
  51. package/dist/app-build/bridge.d.ts +130 -0
  52. package/dist/app-build/bridge.d.ts.map +1 -0
  53. package/dist/app-build/bridge.js +647 -0
  54. package/dist/app-build/bridge.js.map +1 -0
  55. package/dist/app-build/build-service.d.ts +66 -0
  56. package/dist/app-build/build-service.d.ts.map +1 -0
  57. package/dist/app-build/build-service.js +249 -0
  58. package/dist/app-build/build-service.js.map +1 -0
  59. package/dist/app-build/build.d.ts +115 -0
  60. package/dist/app-build/build.d.ts.map +1 -0
  61. package/dist/app-build/build.js +901 -0
  62. package/dist/app-build/build.js.map +1 -0
  63. package/dist/app-build/interactions.d.ts +20 -0
  64. package/dist/app-build/interactions.d.ts.map +1 -0
  65. package/dist/app-build/interactions.js +121 -0
  66. package/dist/app-build/interactions.js.map +1 -0
  67. package/dist/app-build/judge.d.ts +113 -0
  68. package/dist/app-build/judge.d.ts.map +1 -0
  69. package/dist/app-build/judge.js +318 -0
  70. package/dist/app-build/judge.js.map +1 -0
  71. package/dist/app-build/markdown.d.ts +10 -0
  72. package/dist/app-build/markdown.d.ts.map +1 -0
  73. package/dist/app-build/markdown.js +65 -0
  74. package/dist/app-build/markdown.js.map +1 -0
  75. package/dist/app-build/spec.d.ts +228 -0
  76. package/dist/app-build/spec.d.ts.map +1 -0
  77. package/dist/app-build/spec.js +651 -0
  78. package/dist/app-build/spec.js.map +1 -0
  79. package/dist/app-build/tool-loop.d.ts +84 -0
  80. package/dist/app-build/tool-loop.d.ts.map +1 -0
  81. package/dist/app-build/tool-loop.js +113 -0
  82. package/dist/app-build/tool-loop.js.map +1 -0
  83. package/dist/app-build/types.d.ts +215 -0
  84. package/dist/app-build/types.d.ts.map +1 -0
  85. package/dist/app-build/types.js +28 -0
  86. package/dist/app-build/types.js.map +1 -0
  87. package/dist/author-graph.d.ts +91 -0
  88. package/dist/author-graph.d.ts.map +1 -0
  89. package/dist/author-graph.js +319 -0
  90. package/dist/author-graph.js.map +1 -0
  91. package/dist/background-subtasks.d.ts +79 -0
  92. package/dist/background-subtasks.d.ts.map +1 -0
  93. package/dist/background-subtasks.js +139 -0
  94. package/dist/background-subtasks.js.map +1 -0
  95. package/dist/blender-gate.d.ts +21 -0
  96. package/dist/blender-gate.d.ts.map +1 -0
  97. package/dist/blender-gate.js +27 -0
  98. package/dist/blender-gate.js.map +1 -0
  99. package/dist/browser-gate.d.ts +28 -0
  100. package/dist/browser-gate.d.ts.map +1 -0
  101. package/dist/browser-gate.js +34 -0
  102. package/dist/browser-gate.js.map +1 -0
  103. package/dist/capabilities/adapters.d.ts +36 -0
  104. package/dist/capabilities/adapters.d.ts.map +1 -0
  105. package/dist/capabilities/adapters.js +44 -0
  106. package/dist/capabilities/adapters.js.map +1 -0
  107. package/dist/capabilities/agents.d.ts +78 -0
  108. package/dist/capabilities/agents.d.ts.map +1 -0
  109. package/dist/capabilities/agents.js +366 -0
  110. package/dist/capabilities/agents.js.map +1 -0
  111. package/dist/capabilities/agents.specs.d.ts +27 -0
  112. package/dist/capabilities/agents.specs.d.ts.map +1 -0
  113. package/dist/capabilities/agents.specs.js +243 -0
  114. package/dist/capabilities/agents.specs.js.map +1 -0
  115. package/dist/capabilities/analysis.d.ts +93 -0
  116. package/dist/capabilities/analysis.d.ts.map +1 -0
  117. package/dist/capabilities/analysis.js +812 -0
  118. package/dist/capabilities/analysis.js.map +1 -0
  119. package/dist/capabilities/analysis.specs.d.ts +39 -0
  120. package/dist/capabilities/analysis.specs.d.ts.map +1 -0
  121. package/dist/capabilities/analysis.specs.js +217 -0
  122. package/dist/capabilities/analysis.specs.js.map +1 -0
  123. package/dist/capabilities/apify.d.ts +34 -0
  124. package/dist/capabilities/apify.d.ts.map +1 -0
  125. package/dist/capabilities/apify.js +449 -0
  126. package/dist/capabilities/apify.js.map +1 -0
  127. package/dist/capabilities/apify.specs.d.ts +28 -0
  128. package/dist/capabilities/apify.specs.d.ts.map +1 -0
  129. package/dist/capabilities/apify.specs.js +232 -0
  130. package/dist/capabilities/apify.specs.js.map +1 -0
  131. package/dist/capabilities/apps.d.ts +40 -0
  132. package/dist/capabilities/apps.d.ts.map +1 -0
  133. package/dist/capabilities/apps.js +413 -0
  134. package/dist/capabilities/apps.js.map +1 -0
  135. package/dist/capabilities/apps.specs.d.ts +23 -0
  136. package/dist/capabilities/apps.specs.d.ts.map +1 -0
  137. package/dist/capabilities/apps.specs.js +236 -0
  138. package/dist/capabilities/apps.specs.js.map +1 -0
  139. package/dist/capabilities/args.d.ts +65 -0
  140. package/dist/capabilities/args.d.ts.map +1 -0
  141. package/dist/capabilities/args.js +218 -0
  142. package/dist/capabilities/args.js.map +1 -0
  143. package/dist/capabilities/assets.d.ts +45 -0
  144. package/dist/capabilities/assets.d.ts.map +1 -0
  145. package/dist/capabilities/assets.js +952 -0
  146. package/dist/capabilities/assets.js.map +1 -0
  147. package/dist/capabilities/assets.specs.d.ts +54 -0
  148. package/dist/capabilities/assets.specs.d.ts.map +1 -0
  149. package/dist/capabilities/assets.specs.js +291 -0
  150. package/dist/capabilities/assets.specs.js.map +1 -0
  151. package/dist/capabilities/background-generation.d.ts +14 -0
  152. package/dist/capabilities/background-generation.d.ts.map +1 -0
  153. package/dist/capabilities/background-generation.js +29 -0
  154. package/dist/capabilities/background-generation.js.map +1 -0
  155. package/dist/capabilities/browser.d.ts +30 -0
  156. package/dist/capabilities/browser.d.ts.map +1 -0
  157. package/dist/capabilities/browser.js +250 -0
  158. package/dist/capabilities/browser.js.map +1 -0
  159. package/dist/capabilities/browser.specs.d.ts +37 -0
  160. package/dist/capabilities/browser.specs.d.ts.map +1 -0
  161. package/dist/capabilities/browser.specs.js +173 -0
  162. package/dist/capabilities/browser.specs.js.map +1 -0
  163. package/dist/capabilities/code-grading.d.ts +68 -0
  164. package/dist/capabilities/code-grading.d.ts.map +1 -0
  165. package/dist/capabilities/code-grading.js +75 -0
  166. package/dist/capabilities/code-grading.js.map +1 -0
  167. package/dist/capabilities/code.d.ts +56 -0
  168. package/dist/capabilities/code.d.ts.map +1 -0
  169. package/dist/capabilities/code.js +327 -0
  170. package/dist/capabilities/code.js.map +1 -0
  171. package/dist/capabilities/code.specs.d.ts +22 -0
  172. package/dist/capabilities/code.specs.d.ts.map +1 -0
  173. package/dist/capabilities/code.specs.js +175 -0
  174. package/dist/capabilities/code.specs.js.map +1 -0
  175. package/dist/capabilities/collections.d.ts +44 -0
  176. package/dist/capabilities/collections.d.ts.map +1 -0
  177. package/dist/capabilities/collections.js +432 -0
  178. package/dist/capabilities/collections.js.map +1 -0
  179. package/dist/capabilities/collections.specs.d.ts +25 -0
  180. package/dist/capabilities/collections.specs.d.ts.map +1 -0
  181. package/dist/capabilities/collections.specs.js +299 -0
  182. package/dist/capabilities/collections.specs.js.map +1 -0
  183. package/dist/capabilities/compositions.d.ts +57 -0
  184. package/dist/capabilities/compositions.d.ts.map +1 -0
  185. package/dist/capabilities/compositions.js +384 -0
  186. package/dist/capabilities/compositions.js.map +1 -0
  187. package/dist/capabilities/compositions.specs.d.ts +22 -0
  188. package/dist/capabilities/compositions.specs.d.ts.map +1 -0
  189. package/dist/capabilities/compositions.specs.js +126 -0
  190. package/dist/capabilities/compositions.specs.js.map +1 -0
  191. package/dist/capabilities/concurrency.d.ts +12 -0
  192. package/dist/capabilities/concurrency.d.ts.map +1 -0
  193. package/dist/capabilities/concurrency.js +30 -0
  194. package/dist/capabilities/concurrency.js.map +1 -0
  195. package/dist/capabilities/costs.d.ts +20 -0
  196. package/dist/capabilities/costs.d.ts.map +1 -0
  197. package/dist/capabilities/costs.js +40 -0
  198. package/dist/capabilities/costs.js.map +1 -0
  199. package/dist/capabilities/costs.specs.d.ts +12 -0
  200. package/dist/capabilities/costs.specs.d.ts.map +1 -0
  201. package/dist/capabilities/costs.specs.js +35 -0
  202. package/dist/capabilities/costs.specs.js.map +1 -0
  203. package/dist/capabilities/dispatcher.d.ts +57 -0
  204. package/dist/capabilities/dispatcher.d.ts.map +1 -0
  205. package/dist/capabilities/dispatcher.js +117 -0
  206. package/dist/capabilities/dispatcher.js.map +1 -0
  207. package/dist/capabilities/documents.d.ts +25 -0
  208. package/dist/capabilities/documents.d.ts.map +1 -0
  209. package/dist/capabilities/documents.js +278 -0
  210. package/dist/capabilities/documents.js.map +1 -0
  211. package/dist/capabilities/documents.specs.d.ts +18 -0
  212. package/dist/capabilities/documents.specs.d.ts.map +1 -0
  213. package/dist/capabilities/documents.specs.js +201 -0
  214. package/dist/capabilities/documents.specs.js.map +1 -0
  215. package/dist/capabilities/email.d.ts +23 -0
  216. package/dist/capabilities/email.d.ts.map +1 -0
  217. package/dist/capabilities/email.js +226 -0
  218. package/dist/capabilities/email.js.map +1 -0
  219. package/dist/capabilities/email.specs.d.ts +16 -0
  220. package/dist/capabilities/email.specs.d.ts.map +1 -0
  221. package/dist/capabilities/email.specs.js +102 -0
  222. package/dist/capabilities/email.specs.js.map +1 -0
  223. package/dist/capabilities/entities.d.ts +52 -0
  224. package/dist/capabilities/entities.d.ts.map +1 -0
  225. package/dist/capabilities/entities.js +478 -0
  226. package/dist/capabilities/entities.js.map +1 -0
  227. package/dist/capabilities/entities.specs.d.ts +25 -0
  228. package/dist/capabilities/entities.specs.d.ts.map +1 -0
  229. package/dist/capabilities/entities.specs.js +247 -0
  230. package/dist/capabilities/entities.specs.js.map +1 -0
  231. package/dist/capabilities/example-timelines.d.ts +3 -0
  232. package/dist/capabilities/example-timelines.d.ts.map +1 -0
  233. package/dist/capabilities/example-timelines.js +191 -0
  234. package/dist/capabilities/example-timelines.js.map +1 -0
  235. package/dist/capabilities/example-timelines.specs.d.ts +5 -0
  236. package/dist/capabilities/example-timelines.specs.d.ts.map +1 -0
  237. package/dist/capabilities/example-timelines.specs.js +53 -0
  238. package/dist/capabilities/example-timelines.specs.js.map +1 -0
  239. package/dist/capabilities/files.d.ts +53 -0
  240. package/dist/capabilities/files.d.ts.map +1 -0
  241. package/dist/capabilities/files.js +1172 -0
  242. package/dist/capabilities/files.js.map +1 -0
  243. package/dist/capabilities/files.specs.d.ts +28 -0
  244. package/dist/capabilities/files.specs.d.ts.map +1 -0
  245. package/dist/capabilities/files.specs.js +259 -0
  246. package/dist/capabilities/files.specs.js.map +1 -0
  247. package/dist/capabilities/flow.d.ts +93 -0
  248. package/dist/capabilities/flow.d.ts.map +1 -0
  249. package/dist/capabilities/flow.js +285 -0
  250. package/dist/capabilities/flow.js.map +1 -0
  251. package/dist/capabilities/flow.specs.d.ts +25 -0
  252. package/dist/capabilities/flow.specs.d.ts.map +1 -0
  253. package/dist/capabilities/flow.specs.js +114 -0
  254. package/dist/capabilities/flow.specs.js.map +1 -0
  255. package/dist/capabilities/game-asset-source.d.ts +31 -0
  256. package/dist/capabilities/game-asset-source.d.ts.map +1 -0
  257. package/dist/capabilities/game-asset-source.js +81 -0
  258. package/dist/capabilities/game-asset-source.js.map +1 -0
  259. package/dist/capabilities/game.d.ts +12 -0
  260. package/dist/capabilities/game.d.ts.map +1 -0
  261. package/dist/capabilities/game.js +1262 -0
  262. package/dist/capabilities/game.js.map +1 -0
  263. package/dist/capabilities/game.specs.d.ts +3 -0
  264. package/dist/capabilities/game.specs.d.ts.map +1 -0
  265. package/dist/capabilities/game.specs.js +96 -0
  266. package/dist/capabilities/game.specs.js.map +1 -0
  267. package/dist/capabilities/gate-from-context.d.ts +39 -0
  268. package/dist/capabilities/gate-from-context.d.ts.map +1 -0
  269. package/dist/capabilities/gate-from-context.js +97 -0
  270. package/dist/capabilities/gate-from-context.js.map +1 -0
  271. package/dist/capabilities/gate-tools.d.ts +18 -0
  272. package/dist/capabilities/gate-tools.d.ts.map +1 -0
  273. package/dist/capabilities/gate-tools.js +66 -0
  274. package/dist/capabilities/gate-tools.js.map +1 -0
  275. package/dist/capabilities/generations.d.ts +79 -0
  276. package/dist/capabilities/generations.d.ts.map +1 -0
  277. package/dist/capabilities/generations.js +382 -0
  278. package/dist/capabilities/generations.js.map +1 -0
  279. package/dist/capabilities/generations.specs.d.ts +62 -0
  280. package/dist/capabilities/generations.specs.d.ts.map +1 -0
  281. package/dist/capabilities/generations.specs.js +230 -0
  282. package/dist/capabilities/generations.specs.js.map +1 -0
  283. package/dist/capabilities/google.d.ts +50 -0
  284. package/dist/capabilities/google.d.ts.map +1 -0
  285. package/dist/capabilities/google.js +246 -0
  286. package/dist/capabilities/google.js.map +1 -0
  287. package/dist/capabilities/google.specs.d.ts +52 -0
  288. package/dist/capabilities/google.specs.d.ts.map +1 -0
  289. package/dist/capabilities/google.specs.js +391 -0
  290. package/dist/capabilities/google.specs.js.map +1 -0
  291. package/dist/capabilities/html-text.d.ts +28 -0
  292. package/dist/capabilities/html-text.d.ts.map +1 -0
  293. package/dist/capabilities/html-text.js +76 -0
  294. package/dist/capabilities/html-text.js.map +1 -0
  295. package/dist/capabilities/index.d.ts +19 -0
  296. package/dist/capabilities/index.d.ts.map +1 -0
  297. package/dist/capabilities/index.js +14 -0
  298. package/dist/capabilities/index.js.map +1 -0
  299. package/dist/capabilities/invoke.d.ts +89 -0
  300. package/dist/capabilities/invoke.d.ts.map +1 -0
  301. package/dist/capabilities/invoke.js +214 -0
  302. package/dist/capabilities/invoke.js.map +1 -0
  303. package/dist/capabilities/jobs.d.ts +32 -0
  304. package/dist/capabilities/jobs.d.ts.map +1 -0
  305. package/dist/capabilities/jobs.js +123 -0
  306. package/dist/capabilities/jobs.js.map +1 -0
  307. package/dist/capabilities/jobs.specs.d.ts +20 -0
  308. package/dist/capabilities/jobs.specs.d.ts.map +1 -0
  309. package/dist/capabilities/jobs.specs.js +103 -0
  310. package/dist/capabilities/jobs.specs.js.map +1 -0
  311. package/dist/capabilities/js-scripts.d.ts +90 -0
  312. package/dist/capabilities/js-scripts.d.ts.map +1 -0
  313. package/dist/capabilities/js-scripts.js +700 -0
  314. package/dist/capabilities/js-scripts.js.map +1 -0
  315. package/dist/capabilities/js-scripts.specs.d.ts +50 -0
  316. package/dist/capabilities/js-scripts.specs.d.ts.map +1 -0
  317. package/dist/capabilities/js-scripts.specs.js +344 -0
  318. package/dist/capabilities/js-scripts.specs.js.map +1 -0
  319. package/dist/capabilities/lazy-tool.d.ts +42 -0
  320. package/dist/capabilities/lazy-tool.d.ts.map +1 -0
  321. package/dist/capabilities/lazy-tool.js +88 -0
  322. package/dist/capabilities/lazy-tool.js.map +1 -0
  323. package/dist/capabilities/local-browser.d.ts +24 -0
  324. package/dist/capabilities/local-browser.d.ts.map +1 -0
  325. package/dist/capabilities/local-browser.js +105 -0
  326. package/dist/capabilities/local-browser.js.map +1 -0
  327. package/dist/capabilities/media.d.ts +121 -0
  328. package/dist/capabilities/media.d.ts.map +1 -0
  329. package/dist/capabilities/media.js +2217 -0
  330. package/dist/capabilities/media.js.map +1 -0
  331. package/dist/capabilities/media.specs.d.ts +65 -0
  332. package/dist/capabilities/media.specs.d.ts.map +1 -0
  333. package/dist/capabilities/media.specs.js +779 -0
  334. package/dist/capabilities/media.specs.js.map +1 -0
  335. package/dist/capabilities/memory.d.ts +31 -0
  336. package/dist/capabilities/memory.d.ts.map +1 -0
  337. package/dist/capabilities/memory.js +403 -0
  338. package/dist/capabilities/memory.js.map +1 -0
  339. package/dist/capabilities/memory.specs.d.ts +55 -0
  340. package/dist/capabilities/memory.specs.d.ts.map +1 -0
  341. package/dist/capabilities/memory.specs.js +208 -0
  342. package/dist/capabilities/memory.specs.js.map +1 -0
  343. package/dist/capabilities/model3d.d.ts +29 -0
  344. package/dist/capabilities/model3d.d.ts.map +1 -0
  345. package/dist/capabilities/model3d.js +482 -0
  346. package/dist/capabilities/model3d.js.map +1 -0
  347. package/dist/capabilities/model3d.specs.d.ts +28 -0
  348. package/dist/capabilities/model3d.specs.d.ts.map +1 -0
  349. package/dist/capabilities/model3d.specs.js +305 -0
  350. package/dist/capabilities/model3d.specs.js.map +1 -0
  351. package/dist/capabilities/models.d.ts +138 -0
  352. package/dist/capabilities/models.d.ts.map +1 -0
  353. package/dist/capabilities/models.js +803 -0
  354. package/dist/capabilities/models.js.map +1 -0
  355. package/dist/capabilities/models.specs.d.ts +21 -0
  356. package/dist/capabilities/models.specs.d.ts.map +1 -0
  357. package/dist/capabilities/models.specs.js +141 -0
  358. package/dist/capabilities/models.specs.js.map +1 -0
  359. package/dist/capabilities/nodes.d.ts +26 -0
  360. package/dist/capabilities/nodes.d.ts.map +1 -0
  361. package/dist/capabilities/nodes.js +266 -0
  362. package/dist/capabilities/nodes.js.map +1 -0
  363. package/dist/capabilities/nodes.specs.d.ts +20 -0
  364. package/dist/capabilities/nodes.specs.d.ts.map +1 -0
  365. package/dist/capabilities/nodes.specs.js +106 -0
  366. package/dist/capabilities/nodes.specs.js.map +1 -0
  367. package/dist/capabilities/packs.d.ts +69 -0
  368. package/dist/capabilities/packs.d.ts.map +1 -0
  369. package/dist/capabilities/packs.js +246 -0
  370. package/dist/capabilities/packs.js.map +1 -0
  371. package/dist/capabilities/packs.specs.d.ts +27 -0
  372. package/dist/capabilities/packs.specs.d.ts.map +1 -0
  373. package/dist/capabilities/packs.specs.js +64 -0
  374. package/dist/capabilities/packs.specs.js.map +1 -0
  375. package/dist/capabilities/project-scope.d.ts +12 -0
  376. package/dist/capabilities/project-scope.d.ts.map +1 -0
  377. package/dist/capabilities/project-scope.js +19 -0
  378. package/dist/capabilities/project-scope.js.map +1 -0
  379. package/dist/capabilities/projects.d.ts +23 -0
  380. package/dist/capabilities/projects.d.ts.map +1 -0
  381. package/dist/capabilities/projects.js +206 -0
  382. package/dist/capabilities/projects.js.map +1 -0
  383. package/dist/capabilities/projects.specs.d.ts +23 -0
  384. package/dist/capabilities/projects.specs.d.ts.map +1 -0
  385. package/dist/capabilities/projects.specs.js +118 -0
  386. package/dist/capabilities/projects.specs.js.map +1 -0
  387. package/dist/capabilities/registry.d.ts +109 -0
  388. package/dist/capabilities/registry.d.ts.map +1 -0
  389. package/dist/capabilities/registry.js +445 -0
  390. package/dist/capabilities/registry.js.map +1 -0
  391. package/dist/capabilities/script-link.d.ts +22 -0
  392. package/dist/capabilities/script-link.d.ts.map +1 -0
  393. package/dist/capabilities/script-link.js +35 -0
  394. package/dist/capabilities/script-link.js.map +1 -0
  395. package/dist/capabilities/scripts.d.ts +43 -0
  396. package/dist/capabilities/scripts.d.ts.map +1 -0
  397. package/dist/capabilities/scripts.js +1789 -0
  398. package/dist/capabilities/scripts.js.map +1 -0
  399. package/dist/capabilities/scripts.specs.d.ts +40 -0
  400. package/dist/capabilities/scripts.specs.d.ts.map +1 -0
  401. package/dist/capabilities/scripts.specs.js +319 -0
  402. package/dist/capabilities/scripts.specs.js.map +1 -0
  403. package/dist/capabilities/serpapi.d.ts +42 -0
  404. package/dist/capabilities/serpapi.d.ts.map +1 -0
  405. package/dist/capabilities/serpapi.js +262 -0
  406. package/dist/capabilities/serpapi.js.map +1 -0
  407. package/dist/capabilities/serpapi.specs.d.ts +24 -0
  408. package/dist/capabilities/serpapi.specs.d.ts.map +1 -0
  409. package/dist/capabilities/serpapi.specs.js +174 -0
  410. package/dist/capabilities/serpapi.specs.js.map +1 -0
  411. package/dist/capabilities/settings.d.ts +48 -0
  412. package/dist/capabilities/settings.d.ts.map +1 -0
  413. package/dist/capabilities/settings.js +226 -0
  414. package/dist/capabilities/settings.js.map +1 -0
  415. package/dist/capabilities/settings.specs.d.ts +42 -0
  416. package/dist/capabilities/settings.specs.d.ts.map +1 -0
  417. package/dist/capabilities/settings.specs.js +126 -0
  418. package/dist/capabilities/settings.specs.js.map +1 -0
  419. package/dist/capabilities/shared.d.ts +35 -0
  420. package/dist/capabilities/shared.d.ts.map +1 -0
  421. package/dist/capabilities/shared.js +165 -0
  422. package/dist/capabilities/shared.js.map +1 -0
  423. package/dist/capabilities/shared.specs.d.ts +19 -0
  424. package/dist/capabilities/shared.specs.d.ts.map +1 -0
  425. package/dist/capabilities/shared.specs.js +134 -0
  426. package/dist/capabilities/shared.specs.js.map +1 -0
  427. package/dist/capabilities/sketches.d.ts +52 -0
  428. package/dist/capabilities/sketches.d.ts.map +1 -0
  429. package/dist/capabilities/sketches.js +1163 -0
  430. package/dist/capabilities/sketches.js.map +1 -0
  431. package/dist/capabilities/sketches.specs.d.ts +44 -0
  432. package/dist/capabilities/sketches.specs.d.ts.map +1 -0
  433. package/dist/capabilities/sketches.specs.js +392 -0
  434. package/dist/capabilities/sketches.specs.js.map +1 -0
  435. package/dist/capabilities/skills.d.ts +28 -0
  436. package/dist/capabilities/skills.d.ts.map +1 -0
  437. package/dist/capabilities/skills.js +269 -0
  438. package/dist/capabilities/skills.js.map +1 -0
  439. package/dist/capabilities/skills.specs.d.ts +17 -0
  440. package/dist/capabilities/skills.specs.d.ts.map +1 -0
  441. package/dist/capabilities/skills.specs.js +108 -0
  442. package/dist/capabilities/skills.specs.js.map +1 -0
  443. package/dist/capabilities/storyboards.d.ts +68 -0
  444. package/dist/capabilities/storyboards.d.ts.map +1 -0
  445. package/dist/capabilities/storyboards.js +2246 -0
  446. package/dist/capabilities/storyboards.js.map +1 -0
  447. package/dist/capabilities/storyboards.specs.d.ts +42 -0
  448. package/dist/capabilities/storyboards.specs.d.ts.map +1 -0
  449. package/dist/capabilities/storyboards.specs.js +418 -0
  450. package/dist/capabilities/storyboards.specs.js.map +1 -0
  451. package/dist/capabilities/threads.d.ts +25 -0
  452. package/dist/capabilities/threads.d.ts.map +1 -0
  453. package/dist/capabilities/threads.js +240 -0
  454. package/dist/capabilities/threads.js.map +1 -0
  455. package/dist/capabilities/threads.specs.d.ts +26 -0
  456. package/dist/capabilities/threads.specs.d.ts.map +1 -0
  457. package/dist/capabilities/threads.specs.js +108 -0
  458. package/dist/capabilities/threads.specs.js.map +1 -0
  459. package/dist/capabilities/timeline-audio-bake.d.ts +114 -0
  460. package/dist/capabilities/timeline-audio-bake.d.ts.map +1 -0
  461. package/dist/capabilities/timeline-audio-bake.js +188 -0
  462. package/dist/capabilities/timeline-audio-bake.js.map +1 -0
  463. package/dist/capabilities/timeline-bake.d.ts +21 -0
  464. package/dist/capabilities/timeline-bake.d.ts.map +1 -0
  465. package/dist/capabilities/timeline-bake.js +118 -0
  466. package/dist/capabilities/timeline-bake.js.map +1 -0
  467. package/dist/capabilities/timeline-isolate-subject.d.ts +157 -0
  468. package/dist/capabilities/timeline-isolate-subject.d.ts.map +1 -0
  469. package/dist/capabilities/timeline-isolate-subject.js +293 -0
  470. package/dist/capabilities/timeline-isolate-subject.js.map +1 -0
  471. package/dist/capabilities/timeline-native-media.d.ts +4 -0
  472. package/dist/capabilities/timeline-native-media.d.ts.map +1 -0
  473. package/dist/capabilities/timeline-native-media.js +920 -0
  474. package/dist/capabilities/timeline-native-media.js.map +1 -0
  475. package/dist/capabilities/timeline-track-object.d.ts +83 -0
  476. package/dist/capabilities/timeline-track-object.d.ts.map +1 -0
  477. package/dist/capabilities/timeline-track-object.js +195 -0
  478. package/dist/capabilities/timeline-track-object.js.map +1 -0
  479. package/dist/capabilities/timelines.d.ts +190 -0
  480. package/dist/capabilities/timelines.d.ts.map +1 -0
  481. package/dist/capabilities/timelines.js +2326 -0
  482. package/dist/capabilities/timelines.js.map +1 -0
  483. package/dist/capabilities/timelines.specs.d.ts +134 -0
  484. package/dist/capabilities/timelines.specs.d.ts.map +1 -0
  485. package/dist/capabilities/timelines.specs.js +1362 -0
  486. package/dist/capabilities/timelines.specs.js.map +1 -0
  487. package/dist/capabilities/types.d.ts +197 -0
  488. package/dist/capabilities/types.d.ts.map +1 -0
  489. package/dist/capabilities/types.js +19 -0
  490. package/dist/capabilities/types.js.map +1 -0
  491. package/dist/capabilities/ui.d.ts +38 -0
  492. package/dist/capabilities/ui.d.ts.map +1 -0
  493. package/dist/capabilities/ui.js +128 -0
  494. package/dist/capabilities/ui.js.map +1 -0
  495. package/dist/capabilities/ui.specs.d.ts +22 -0
  496. package/dist/capabilities/ui.specs.d.ts.map +1 -0
  497. package/dist/capabilities/ui.specs.js +59 -0
  498. package/dist/capabilities/ui.specs.js.map +1 -0
  499. package/dist/capabilities/video-production-acceptance.d.ts +27 -0
  500. package/dist/capabilities/video-production-acceptance.d.ts.map +1 -0
  501. package/dist/capabilities/video-production-acceptance.js +41 -0
  502. package/dist/capabilities/video-production-acceptance.js.map +1 -0
  503. package/dist/capabilities/video-production.d.ts +179 -0
  504. package/dist/capabilities/video-production.d.ts.map +1 -0
  505. package/dist/capabilities/video-production.js +1492 -0
  506. package/dist/capabilities/video-production.js.map +1 -0
  507. package/dist/capabilities/video-production.specs.d.ts +13 -0
  508. package/dist/capabilities/video-production.specs.d.ts.map +1 -0
  509. package/dist/capabilities/video-production.specs.js +256 -0
  510. package/dist/capabilities/video-production.specs.js.map +1 -0
  511. package/dist/capabilities/web.d.ts +86 -0
  512. package/dist/capabilities/web.d.ts.map +1 -0
  513. package/dist/capabilities/web.js +850 -0
  514. package/dist/capabilities/web.js.map +1 -0
  515. package/dist/capabilities/web.specs.d.ts +33 -0
  516. package/dist/capabilities/web.specs.d.ts.map +1 -0
  517. package/dist/capabilities/web.specs.js +286 -0
  518. package/dist/capabilities/web.specs.js.map +1 -0
  519. package/dist/capabilities/workflows.d.ts +58 -0
  520. package/dist/capabilities/workflows.d.ts.map +1 -0
  521. package/dist/capabilities/workflows.js +1530 -0
  522. package/dist/capabilities/workflows.js.map +1 -0
  523. package/dist/capabilities/workflows.specs.d.ts +63 -0
  524. package/dist/capabilities/workflows.specs.d.ts.map +1 -0
  525. package/dist/capabilities/workflows.specs.js +852 -0
  526. package/dist/capabilities/workflows.specs.js.map +1 -0
  527. package/dist/code-gen/analyze.d.ts +26 -0
  528. package/dist/code-gen/analyze.d.ts.map +1 -0
  529. package/dist/code-gen/analyze.js +79 -0
  530. package/dist/code-gen/analyze.js.map +1 -0
  531. package/dist/code-gen/port-types.d.ts +58 -0
  532. package/dist/code-gen/port-types.d.ts.map +1 -0
  533. package/dist/code-gen/port-types.js +115 -0
  534. package/dist/code-gen/port-types.js.map +1 -0
  535. package/dist/code-gen/prompt.d.ts +37 -0
  536. package/dist/code-gen/prompt.d.ts.map +1 -0
  537. package/dist/code-gen/prompt.js +129 -0
  538. package/dist/code-gen/prompt.js.map +1 -0
  539. package/dist/code-gen/sandbox-manifest.d.ts +120 -0
  540. package/dist/code-gen/sandbox-manifest.d.ts.map +1 -0
  541. package/dist/code-gen/sandbox-manifest.js +1023 -0
  542. package/dist/code-gen/sandbox-manifest.js.map +1 -0
  543. package/dist/code-gen/sandbox-prompt.d.ts +26 -0
  544. package/dist/code-gen/sandbox-prompt.d.ts.map +1 -0
  545. package/dist/code-gen/sandbox-prompt.js +126 -0
  546. package/dist/code-gen/sandbox-prompt.js.map +1 -0
  547. package/dist/code-planner.d.ts +72 -0
  548. package/dist/code-planner.d.ts.map +1 -0
  549. package/dist/code-planner.js +271 -0
  550. package/dist/code-planner.js.map +1 -0
  551. package/dist/codeact/action-diagnostics.d.ts +35 -0
  552. package/dist/codeact/action-diagnostics.d.ts.map +1 -0
  553. package/dist/codeact/action-diagnostics.js +147 -0
  554. package/dist/codeact/action-diagnostics.js.map +1 -0
  555. package/dist/codeact/capability-modules.d.ts +66 -0
  556. package/dist/codeact/capability-modules.d.ts.map +1 -0
  557. package/dist/codeact/capability-modules.js +278 -0
  558. package/dist/codeact/capability-modules.js.map +1 -0
  559. package/dist/codeact/chat-codeact.d.ts +104 -0
  560. package/dist/codeact/chat-codeact.d.ts.map +1 -0
  561. package/dist/codeact/chat-codeact.js +424 -0
  562. package/dist/codeact/chat-codeact.js.map +1 -0
  563. package/dist/codeact/codeact-executor.d.ts +247 -0
  564. package/dist/codeact/codeact-executor.d.ts.map +1 -0
  565. package/dist/codeact/codeact-executor.js +1176 -0
  566. package/dist/codeact/codeact-executor.js.map +1 -0
  567. package/dist/codeact/compact-ids.d.ts +28 -0
  568. package/dist/codeact/compact-ids.d.ts.map +1 -0
  569. package/dist/codeact/compact-ids.js +77 -0
  570. package/dist/codeact/compact-ids.js.map +1 -0
  571. package/dist/codeact/execute-code-contract.d.ts +97 -0
  572. package/dist/codeact/execute-code-contract.d.ts.map +1 -0
  573. package/dist/codeact/execute-code-contract.js +225 -0
  574. package/dist/codeact/execute-code-contract.js.map +1 -0
  575. package/dist/codeact/fabric-package.d.ts +29 -0
  576. package/dist/codeact/fabric-package.d.ts.map +1 -0
  577. package/dist/codeact/fabric-package.js +62 -0
  578. package/dist/codeact/fabric-package.js.map +1 -0
  579. package/dist/codeact/flow-package.d.ts +31 -0
  580. package/dist/codeact/flow-package.d.ts.map +1 -0
  581. package/dist/codeact/flow-package.js +80 -0
  582. package/dist/codeact/flow-package.js.map +1 -0
  583. package/dist/codeact/graph-dsl-package.d.ts +42 -0
  584. package/dist/codeact/graph-dsl-package.d.ts.map +1 -0
  585. package/dist/codeact/graph-dsl-package.js +100 -0
  586. package/dist/codeact/graph-dsl-package.js.map +1 -0
  587. package/dist/codeact/graph-model.d.ts +30 -0
  588. package/dist/codeact/graph-model.d.ts.map +1 -0
  589. package/dist/codeact/graph-model.js +408 -0
  590. package/dist/codeact/graph-model.js.map +1 -0
  591. package/dist/codeact/mcp-guest-contract.d.ts +66 -0
  592. package/dist/codeact/mcp-guest-contract.d.ts.map +1 -0
  593. package/dist/codeact/mcp-guest-contract.js +108 -0
  594. package/dist/codeact/mcp-guest-contract.js.map +1 -0
  595. package/dist/codeact/nodetool-api.d.ts +60 -0
  596. package/dist/codeact/nodetool-api.d.ts.map +1 -0
  597. package/dist/codeact/nodetool-api.js +2056 -0
  598. package/dist/codeact/nodetool-api.js.map +1 -0
  599. package/dist/codeact/prompt.d.ts +88 -0
  600. package/dist/codeact/prompt.d.ts.map +1 -0
  601. package/dist/codeact/prompt.js +326 -0
  602. package/dist/codeact/prompt.js.map +1 -0
  603. package/dist/codeact/sandbox-package-docs.d.ts +48 -0
  604. package/dist/codeact/sandbox-package-docs.d.ts.map +1 -0
  605. package/dist/codeact/sandbox-package-docs.js +55 -0
  606. package/dist/codeact/sandbox-package-docs.js.map +1 -0
  607. package/dist/codeact/sandbox-package-listing.d.ts +81 -0
  608. package/dist/codeact/sandbox-package-listing.d.ts.map +1 -0
  609. package/dist/codeact/sandbox-package-listing.js +101 -0
  610. package/dist/codeact/sandbox-package-listing.js.map +1 -0
  611. package/dist/codeact/sandbox-packages.d.ts +72 -0
  612. package/dist/codeact/sandbox-packages.d.ts.map +1 -0
  613. package/dist/codeact/sandbox-packages.js +230 -0
  614. package/dist/codeact/sandbox-packages.js.map +1 -0
  615. package/dist/codeact/timeline-package.d.ts +5 -0
  616. package/dist/codeact/timeline-package.d.ts.map +1 -0
  617. package/dist/codeact/timeline-package.js +10 -0
  618. package/dist/codeact/timeline-package.js.map +1 -0
  619. package/dist/codeact/tool-api.d.ts +197 -0
  620. package/dist/codeact/tool-api.d.ts.map +1 -0
  621. package/dist/codeact/tool-api.js +445 -0
  622. package/dist/codeact/tool-api.js.map +1 -0
  623. package/dist/codeact/tools-prelude.d.ts +32 -0
  624. package/dist/codeact/tools-prelude.d.ts.map +1 -0
  625. package/dist/codeact/tools-prelude.js +65 -0
  626. package/dist/codeact/tools-prelude.js.map +1 -0
  627. package/dist/constants.d.ts +47 -2
  628. package/dist/constants.d.ts.map +1 -1
  629. package/dist/constants.js +64 -2
  630. package/dist/constants.js.map +1 -1
  631. package/dist/custom-animation-bake.d.ts +57 -0
  632. package/dist/custom-animation-bake.d.ts.map +1 -0
  633. package/dist/custom-animation-bake.js +88 -0
  634. package/dist/custom-animation-bake.js.map +1 -0
  635. package/dist/dynamic-slots.d.ts +72 -0
  636. package/dist/dynamic-slots.d.ts.map +1 -0
  637. package/dist/dynamic-slots.js +0 -0
  638. package/dist/dynamic-slots.js.map +1 -0
  639. package/dist/evals/app-build-cases.d.ts +35 -0
  640. package/dist/evals/app-build-cases.d.ts.map +1 -0
  641. package/dist/evals/app-build-cases.js +722 -0
  642. package/dist/evals/app-build-cases.js.map +1 -0
  643. package/dist/evals/app-build-eval.d.ts +208 -0
  644. package/dist/evals/app-build-eval.d.ts.map +1 -0
  645. package/dist/evals/app-build-eval.js +518 -0
  646. package/dist/evals/app-build-eval.js.map +1 -0
  647. package/dist/evals/app-build-plan.d.ts +76 -0
  648. package/dist/evals/app-build-plan.d.ts.map +1 -0
  649. package/dist/evals/app-build-plan.js +140 -0
  650. package/dist/evals/app-build-plan.js.map +1 -0
  651. package/dist/evals/code-gen-cases.d.ts +13 -0
  652. package/dist/evals/code-gen-cases.d.ts.map +1 -0
  653. package/dist/evals/code-gen-cases.js +124 -0
  654. package/dist/evals/code-gen-cases.js.map +1 -0
  655. package/dist/evals/code-gen-eval.d.ts +109 -0
  656. package/dist/evals/code-gen-eval.d.ts.map +1 -0
  657. package/dist/evals/code-gen-eval.js +299 -0
  658. package/dist/evals/code-gen-eval.js.map +1 -0
  659. package/dist/evals/codeact-api-cases.d.ts +14 -0
  660. package/dist/evals/codeact-api-cases.d.ts.map +1 -0
  661. package/dist/evals/codeact-api-cases.js +29 -0
  662. package/dist/evals/codeact-api-cases.js.map +1 -0
  663. package/dist/evals/codeact-api-core.d.ts +23 -0
  664. package/dist/evals/codeact-api-core.d.ts.map +1 -0
  665. package/dist/evals/codeact-api-core.js +1411 -0
  666. package/dist/evals/codeact-api-core.js.map +1 -0
  667. package/dist/evals/codeact-api-surfaces.d.ts +25 -0
  668. package/dist/evals/codeact-api-surfaces.d.ts.map +1 -0
  669. package/dist/evals/codeact-api-surfaces.js +2191 -0
  670. package/dist/evals/codeact-api-surfaces.js.map +1 -0
  671. package/dist/evals/codeact-cases.d.ts +76 -0
  672. package/dist/evals/codeact-cases.d.ts.map +1 -0
  673. package/dist/evals/codeact-cases.js +151 -0
  674. package/dist/evals/codeact-cases.js.map +1 -0
  675. package/dist/evals/codeact-eval.d.ts +66 -0
  676. package/dist/evals/codeact-eval.d.ts.map +1 -0
  677. package/dist/evals/codeact-eval.js +250 -0
  678. package/dist/evals/codeact-eval.js.map +1 -0
  679. package/dist/evals/codeact-sandbox-pack-cases.d.ts +31 -0
  680. package/dist/evals/codeact-sandbox-pack-cases.d.ts.map +1 -0
  681. package/dist/evals/codeact-sandbox-pack-cases.js +219 -0
  682. package/dist/evals/codeact-sandbox-pack-cases.js.map +1 -0
  683. package/dist/evals/escalation-cases.d.ts +22 -0
  684. package/dist/evals/escalation-cases.d.ts.map +1 -0
  685. package/dist/evals/escalation-cases.js +343 -0
  686. package/dist/evals/escalation-cases.js.map +1 -0
  687. package/dist/evals/escalation.d.ts +92 -0
  688. package/dist/evals/escalation.d.ts.map +1 -0
  689. package/dist/evals/escalation.js +152 -0
  690. package/dist/evals/escalation.js.map +1 -0
  691. package/dist/evals/goal-judge.d.ts +51 -0
  692. package/dist/evals/goal-judge.d.ts.map +1 -0
  693. package/dist/evals/goal-judge.js +152 -0
  694. package/dist/evals/goal-judge.js.map +1 -0
  695. package/dist/evals/graph-e2e-cases.d.ts +21 -0
  696. package/dist/evals/graph-e2e-cases.d.ts.map +1 -0
  697. package/dist/evals/graph-e2e-cases.js +262 -0
  698. package/dist/evals/graph-e2e-cases.js.map +1 -0
  699. package/dist/evals/graph-e2e-eval.d.ts +202 -0
  700. package/dist/evals/graph-e2e-eval.d.ts.map +1 -0
  701. package/dist/evals/graph-e2e-eval.js +438 -0
  702. package/dist/evals/graph-e2e-eval.js.map +1 -0
  703. package/dist/evals/graph-planner-cases.d.ts +12 -0
  704. package/dist/evals/graph-planner-cases.d.ts.map +1 -0
  705. package/dist/evals/graph-planner-cases.js +256 -0
  706. package/dist/evals/graph-planner-cases.js.map +1 -0
  707. package/dist/evals/graph-planner-eval.d.ts +178 -0
  708. package/dist/evals/graph-planner-eval.d.ts.map +1 -0
  709. package/dist/evals/graph-planner-eval.js +495 -0
  710. package/dist/evals/graph-planner-eval.js.map +1 -0
  711. package/dist/evals/planner-tools.d.ts +21 -0
  712. package/dist/evals/planner-tools.d.ts.map +1 -0
  713. package/dist/evals/planner-tools.js +66 -0
  714. package/dist/evals/planner-tools.js.map +1 -0
  715. package/dist/evals/subtask-cases.d.ts +88 -0
  716. package/dist/evals/subtask-cases.d.ts.map +1 -0
  717. package/dist/evals/subtask-cases.js +322 -0
  718. package/dist/evals/subtask-cases.js.map +1 -0
  719. package/dist/evals/subtask-eval.d.ts +129 -0
  720. package/dist/evals/subtask-eval.d.ts.map +1 -0
  721. package/dist/evals/subtask-eval.js +397 -0
  722. package/dist/evals/subtask-eval.js.map +1 -0
  723. package/dist/evals/surfaces/app.d.ts +19 -0
  724. package/dist/evals/surfaces/app.d.ts.map +1 -0
  725. package/dist/evals/surfaces/app.js +341 -0
  726. package/dist/evals/surfaces/app.js.map +1 -0
  727. package/dist/evals/surfaces/creative-pipeline.d.ts +203 -0
  728. package/dist/evals/surfaces/creative-pipeline.d.ts.map +1 -0
  729. package/dist/evals/surfaces/creative-pipeline.js +909 -0
  730. package/dist/evals/surfaces/creative-pipeline.js.map +1 -0
  731. package/dist/evals/surfaces/game.d.ts +5 -0
  732. package/dist/evals/surfaces/game.d.ts.map +1 -0
  733. package/dist/evals/surfaces/game.js +42 -0
  734. package/dist/evals/surfaces/game.js.map +1 -0
  735. package/dist/evals/surfaces/js-script.d.ts +66 -0
  736. package/dist/evals/surfaces/js-script.d.ts.map +1 -0
  737. package/dist/evals/surfaces/js-script.js +407 -0
  738. package/dist/evals/surfaces/js-script.js.map +1 -0
  739. package/dist/evals/surfaces/memory.d.ts +34 -0
  740. package/dist/evals/surfaces/memory.d.ts.map +1 -0
  741. package/dist/evals/surfaces/memory.js +264 -0
  742. package/dist/evals/surfaces/memory.js.map +1 -0
  743. package/dist/evals/surfaces/model3d.d.ts +45 -0
  744. package/dist/evals/surfaces/model3d.d.ts.map +1 -0
  745. package/dist/evals/surfaces/model3d.js +346 -0
  746. package/dist/evals/surfaces/model3d.js.map +1 -0
  747. package/dist/evals/surfaces/script.d.ts +96 -0
  748. package/dist/evals/surfaces/script.d.ts.map +1 -0
  749. package/dist/evals/surfaces/script.js +709 -0
  750. package/dist/evals/surfaces/script.js.map +1 -0
  751. package/dist/evals/surfaces/sketch.d.ts +124 -0
  752. package/dist/evals/surfaces/sketch.d.ts.map +1 -0
  753. package/dist/evals/surfaces/sketch.js +1298 -0
  754. package/dist/evals/surfaces/sketch.js.map +1 -0
  755. package/dist/evals/surfaces/storyboard.d.ts +97 -0
  756. package/dist/evals/surfaces/storyboard.d.ts.map +1 -0
  757. package/dist/evals/surfaces/storyboard.js +693 -0
  758. package/dist/evals/surfaces/storyboard.js.map +1 -0
  759. package/dist/evals/surfaces/timeline.d.ts +416 -0
  760. package/dist/evals/surfaces/timeline.d.ts.map +1 -0
  761. package/dist/evals/surfaces/timeline.js +3267 -0
  762. package/dist/evals/surfaces/timeline.js.map +1 -0
  763. package/dist/evals/task-planner-cases.d.ts +57 -0
  764. package/dist/evals/task-planner-cases.d.ts.map +1 -0
  765. package/dist/evals/task-planner-cases.js +117 -0
  766. package/dist/evals/task-planner-cases.js.map +1 -0
  767. package/dist/evals/task-planner-eval.d.ts +101 -0
  768. package/dist/evals/task-planner-eval.d.ts.map +1 -0
  769. package/dist/evals/task-planner-eval.js +380 -0
  770. package/dist/evals/task-planner-eval.js.map +1 -0
  771. package/dist/evals/tool-loop-bridge.d.ts +57 -0
  772. package/dist/evals/tool-loop-bridge.d.ts.map +1 -0
  773. package/dist/evals/tool-loop-bridge.js +129 -0
  774. package/dist/evals/tool-loop-bridge.js.map +1 -0
  775. package/dist/evals/tool-loop-cases.d.ts +21 -0
  776. package/dist/evals/tool-loop-cases.d.ts.map +1 -0
  777. package/dist/evals/tool-loop-cases.js +401 -0
  778. package/dist/evals/tool-loop-cases.js.map +1 -0
  779. package/dist/evals/tool-loop-eval.d.ts +234 -0
  780. package/dist/evals/tool-loop-eval.d.ts.map +1 -0
  781. package/dist/evals/tool-loop-eval.js +373 -0
  782. package/dist/evals/tool-loop-eval.js.map +1 -0
  783. package/dist/evals/tool-loop-permission.d.ts +43 -0
  784. package/dist/evals/tool-loop-permission.d.ts.map +1 -0
  785. package/dist/evals/tool-loop-permission.js +76 -0
  786. package/dist/evals/tool-loop-permission.js.map +1 -0
  787. package/dist/game-examples.d.ts +447 -0
  788. package/dist/game-examples.d.ts.map +1 -0
  789. package/dist/game-examples.js +187 -0
  790. package/dist/game-examples.js.map +1 -0
  791. package/dist/graph-builder.d.ts +55 -3
  792. package/dist/graph-builder.d.ts.map +1 -1
  793. package/dist/graph-builder.js +159 -4
  794. package/dist/graph-builder.js.map +1 -1
  795. package/dist/graph-dsl-core.d.ts +25 -0
  796. package/dist/graph-dsl-core.d.ts.map +1 -0
  797. package/dist/graph-dsl-core.js +312 -0
  798. package/dist/graph-dsl-core.js.map +1 -0
  799. package/dist/graph-dsl.d.ts +30 -0
  800. package/dist/graph-dsl.d.ts.map +1 -0
  801. package/dist/graph-dsl.js +116 -0
  802. package/dist/graph-dsl.js.map +1 -0
  803. package/dist/host-binaries.d.ts +6 -0
  804. package/dist/host-binaries.d.ts.map +1 -0
  805. package/dist/host-binaries.js +6 -0
  806. package/dist/host-binaries.js.map +1 -0
  807. package/dist/host-binary-guard.d.ts +100 -0
  808. package/dist/host-binary-guard.d.ts.map +1 -0
  809. package/dist/host-binary-guard.js +205 -0
  810. package/dist/host-binary-guard.js.map +1 -0
  811. package/dist/host-modules/aws.d.ts +57 -0
  812. package/dist/host-modules/aws.d.ts.map +1 -0
  813. package/dist/host-modules/aws.js +309 -0
  814. package/dist/host-modules/aws.js.map +1 -0
  815. package/dist/host-modules/chrono.d.ts +15 -0
  816. package/dist/host-modules/chrono.d.ts.map +1 -0
  817. package/dist/host-modules/chrono.js +35 -0
  818. package/dist/host-modules/chrono.js.map +1 -0
  819. package/dist/host-modules/csv.d.ts +17 -0
  820. package/dist/host-modules/csv.d.ts.map +1 -0
  821. package/dist/host-modules/csv.js +64 -0
  822. package/dist/host-modules/csv.js.map +1 -0
  823. package/dist/host-modules/diff.d.ts +9 -0
  824. package/dist/host-modules/diff.d.ts.map +1 -0
  825. package/dist/host-modules/diff.js +26 -0
  826. package/dist/host-modules/diff.js.map +1 -0
  827. package/dist/host-modules/dispatcher.d.ts +41 -0
  828. package/dist/host-modules/dispatcher.d.ts.map +1 -0
  829. package/dist/host-modules/dispatcher.js +99 -0
  830. package/dist/host-modules/dispatcher.js.map +1 -0
  831. package/dist/host-modules/docx.d.ts +19 -0
  832. package/dist/host-modules/docx.d.ts.map +1 -0
  833. package/dist/host-modules/docx.js +120 -0
  834. package/dist/host-modules/docx.js.map +1 -0
  835. package/dist/host-modules/epub.d.ts +48 -0
  836. package/dist/host-modules/epub.d.ts.map +1 -0
  837. package/dist/host-modules/epub.js +100 -0
  838. package/dist/host-modules/epub.js.map +1 -0
  839. package/dist/host-modules/exif.d.ts +8 -0
  840. package/dist/host-modules/exif.d.ts.map +1 -0
  841. package/dist/host-modules/exif.js +19 -0
  842. package/dist/host-modules/exif.js.map +1 -0
  843. package/dist/host-modules/expr.d.ts +16 -0
  844. package/dist/host-modules/expr.d.ts.map +1 -0
  845. package/dist/host-modules/expr.js +18 -0
  846. package/dist/host-modules/expr.js.map +1 -0
  847. package/dist/host-modules/fabric.d.ts +46 -0
  848. package/dist/host-modules/fabric.d.ts.map +1 -0
  849. package/dist/host-modules/fabric.js +178 -0
  850. package/dist/host-modules/fabric.js.map +1 -0
  851. package/dist/host-modules/html.d.ts +51 -0
  852. package/dist/host-modules/html.d.ts.map +1 -0
  853. package/dist/host-modules/html.js +198 -0
  854. package/dist/host-modules/html.js.map +1 -0
  855. package/dist/host-modules/ics.d.ts +9 -0
  856. package/dist/host-modules/ics.d.ts.map +1 -0
  857. package/dist/host-modules/ics.js +35 -0
  858. package/dist/host-modules/ics.js.map +1 -0
  859. package/dist/host-modules/limits.d.ts +41 -0
  860. package/dist/host-modules/limits.d.ts.map +1 -0
  861. package/dist/host-modules/limits.js +76 -0
  862. package/dist/host-modules/limits.js.map +1 -0
  863. package/dist/host-modules/mammoth.d.ts +11 -0
  864. package/dist/host-modules/mammoth.d.ts.map +1 -0
  865. package/dist/host-modules/mammoth.js +29 -0
  866. package/dist/host-modules/mammoth.js.map +1 -0
  867. package/dist/host-modules/notion.d.ts +45 -0
  868. package/dist/host-modules/notion.d.ts.map +1 -0
  869. package/dist/host-modules/notion.js +128 -0
  870. package/dist/host-modules/notion.js.map +1 -0
  871. package/dist/host-modules/ocr.d.ts +25 -0
  872. package/dist/host-modules/ocr.d.ts.map +1 -0
  873. package/dist/host-modules/ocr.js +74 -0
  874. package/dist/host-modules/ocr.js.map +1 -0
  875. package/dist/host-modules/pdf.d.ts +21 -0
  876. package/dist/host-modules/pdf.d.ts.map +1 -0
  877. package/dist/host-modules/pdf.js +53 -0
  878. package/dist/host-modules/pdf.js.map +1 -0
  879. package/dist/host-modules/pdflib.d.ts +17 -0
  880. package/dist/host-modules/pdflib.d.ts.map +1 -0
  881. package/dist/host-modules/pdflib.js +138 -0
  882. package/dist/host-modules/pdflib.js.map +1 -0
  883. package/dist/host-modules/pptx.d.ts +19 -0
  884. package/dist/host-modules/pptx.d.ts.map +1 -0
  885. package/dist/host-modules/pptx.js +67 -0
  886. package/dist/host-modules/pptx.js.map +1 -0
  887. package/dist/host-modules/pptxgen.d.ts +13 -0
  888. package/dist/host-modules/pptxgen.d.ts.map +1 -0
  889. package/dist/host-modules/pptxgen.js +98 -0
  890. package/dist/host-modules/pptxgen.js.map +1 -0
  891. package/dist/host-modules/prepared-request.d.ts +41 -0
  892. package/dist/host-modules/prepared-request.d.ts.map +1 -0
  893. package/dist/host-modules/prepared-request.js +104 -0
  894. package/dist/host-modules/prepared-request.js.map +1 -0
  895. package/dist/host-modules/registry.d.ts +31 -0
  896. package/dist/host-modules/registry.d.ts.map +1 -0
  897. package/dist/host-modules/registry.js +193 -0
  898. package/dist/host-modules/registry.js.map +1 -0
  899. package/dist/host-modules/subtitle.d.ts +15 -0
  900. package/dist/host-modules/subtitle.d.ts.map +1 -0
  901. package/dist/host-modules/subtitle.js +45 -0
  902. package/dist/host-modules/subtitle.js.map +1 -0
  903. package/dist/host-modules/supabase.d.ts +42 -0
  904. package/dist/host-modules/supabase.d.ts.map +1 -0
  905. package/dist/host-modules/supabase.js +141 -0
  906. package/dist/host-modules/supabase.js.map +1 -0
  907. package/dist/host-modules/tfjs.d.ts +42 -0
  908. package/dist/host-modules/tfjs.d.ts.map +1 -0
  909. package/dist/host-modules/tfjs.js +186 -0
  910. package/dist/host-modules/tfjs.js.map +1 -0
  911. package/dist/host-modules/tokens.d.ts +17 -0
  912. package/dist/host-modules/tokens.d.ts.map +1 -0
  913. package/dist/host-modules/tokens.js +74 -0
  914. package/dist/host-modules/tokens.js.map +1 -0
  915. package/dist/host-modules/twilio.d.ts +34 -0
  916. package/dist/host-modules/twilio.d.ts.map +1 -0
  917. package/dist/host-modules/twilio.js +67 -0
  918. package/dist/host-modules/twilio.js.map +1 -0
  919. package/dist/host-modules/xlsx.d.ts +26 -0
  920. package/dist/host-modules/xlsx.d.ts.map +1 -0
  921. package/dist/host-modules/xlsx.js +323 -0
  922. package/dist/host-modules/xlsx.js.map +1 -0
  923. package/dist/host-modules/xml.d.ts +23 -0
  924. package/dist/host-modules/xml.d.ts.map +1 -0
  925. package/dist/host-modules/xml.js +38 -0
  926. package/dist/host-modules/xml.js.map +1 -0
  927. package/dist/host-modules/zip.d.ts +24 -0
  928. package/dist/host-modules/zip.d.ts.map +1 -0
  929. package/dist/host-modules/zip.js +70 -0
  930. package/dist/host-modules/zip.js.map +1 -0
  931. package/dist/index.d.ts +197 -60
  932. package/dist/index.d.ts.map +1 -1
  933. package/dist/index.js +153 -60
  934. package/dist/index.js.map +1 -1
  935. package/dist/js-sandbox-worker/host.d.ts +136 -0
  936. package/dist/js-sandbox-worker/host.d.ts.map +1 -0
  937. package/dist/js-sandbox-worker/host.js +482 -0
  938. package/dist/js-sandbox-worker/host.js.map +1 -0
  939. package/dist/js-sandbox-worker/interpreter.d.ts +276 -0
  940. package/dist/js-sandbox-worker/interpreter.d.ts.map +1 -0
  941. package/dist/js-sandbox-worker/interpreter.js +1201 -0
  942. package/dist/js-sandbox-worker/interpreter.js.map +1 -0
  943. package/dist/js-sandbox-worker/protocol.d.ts +168 -0
  944. package/dist/js-sandbox-worker/protocol.d.ts.map +1 -0
  945. package/dist/js-sandbox-worker/protocol.js +217 -0
  946. package/dist/js-sandbox-worker/protocol.js.map +1 -0
  947. package/dist/js-sandbox-worker/worker-entry.d.ts +26 -0
  948. package/dist/js-sandbox-worker/worker-entry.d.ts.map +1 -0
  949. package/dist/js-sandbox-worker/worker-entry.js +307 -0
  950. package/dist/js-sandbox-worker/worker-entry.js.map +1 -0
  951. package/dist/js-sandbox.d.ts +455 -15
  952. package/dist/js-sandbox.d.ts.map +1 -1
  953. package/dist/js-sandbox.js +1961 -309
  954. package/dist/js-sandbox.js.map +1 -1
  955. package/dist/js-script-app-runner.d.ts +9 -0
  956. package/dist/js-script-app-runner.d.ts.map +1 -0
  957. package/dist/js-script-app-runner.js +49 -0
  958. package/dist/js-script-app-runner.js.map +1 -0
  959. package/dist/js-script-sandbox.d.ts +33 -0
  960. package/dist/js-script-sandbox.d.ts.map +1 -0
  961. package/dist/js-script-sandbox.js +82 -0
  962. package/dist/js-script-sandbox.js.map +1 -0
  963. package/dist/jtbd/friction.d.ts +26 -0
  964. package/dist/jtbd/friction.d.ts.map +1 -0
  965. package/dist/jtbd/friction.js +169 -0
  966. package/dist/jtbd/friction.js.map +1 -0
  967. package/dist/jtbd/index.d.ts +6 -0
  968. package/dist/jtbd/index.d.ts.map +1 -0
  969. package/dist/jtbd/index.js +6 -0
  970. package/dist/jtbd/index.js.map +1 -0
  971. package/dist/jtbd/optimize.d.ts +71 -0
  972. package/dist/jtbd/optimize.d.ts.map +1 -0
  973. package/dist/jtbd/optimize.js +176 -0
  974. package/dist/jtbd/optimize.js.map +1 -0
  975. package/dist/jtbd/registry.d.ts +29 -0
  976. package/dist/jtbd/registry.d.ts.map +1 -0
  977. package/dist/jtbd/registry.js +451 -0
  978. package/dist/jtbd/registry.js.map +1 -0
  979. package/dist/jtbd/run.d.ts +70 -0
  980. package/dist/jtbd/run.d.ts.map +1 -0
  981. package/dist/jtbd/run.js +177 -0
  982. package/dist/jtbd/run.js.map +1 -0
  983. package/dist/jtbd/types.d.ts +141 -0
  984. package/dist/jtbd/types.d.ts.map +1 -0
  985. package/dist/jtbd/types.js +25 -0
  986. package/dist/jtbd/types.js.map +1 -0
  987. package/dist/model-prompting-skills.d.ts +45 -0
  988. package/dist/model-prompting-skills.d.ts.map +1 -0
  989. package/dist/model-prompting-skills.js +116 -0
  990. package/dist/model-prompting-skills.js.map +1 -0
  991. package/dist/network-guard.d.ts +50 -0
  992. package/dist/network-guard.d.ts.map +1 -0
  993. package/dist/network-guard.js +106 -0
  994. package/dist/network-guard.js.map +1 -0
  995. package/dist/normalize-model-properties.d.ts +26 -0
  996. package/dist/normalize-model-properties.d.ts.map +1 -0
  997. package/dist/normalize-model-properties.js +58 -0
  998. package/dist/normalize-model-properties.js.map +1 -0
  999. package/dist/output-format.d.ts +5 -1
  1000. package/dist/output-format.d.ts.map +1 -1
  1001. package/dist/output-format.js.map +1 -1
  1002. package/dist/parallel-task-executor.d.ts +68 -37
  1003. package/dist/parallel-task-executor.d.ts.map +1 -1
  1004. package/dist/parallel-task-executor.js +234 -203
  1005. package/dist/parallel-task-executor.js.map +1 -1
  1006. package/dist/prompts/background-subtask-prompt.d.ts +14 -0
  1007. package/dist/prompts/background-subtask-prompt.d.ts.map +1 -0
  1008. package/dist/prompts/background-subtask-prompt.js +64 -0
  1009. package/dist/prompts/background-subtask-prompt.js.map +1 -0
  1010. package/dist/prompts/product-knowledge.d.ts +3 -0
  1011. package/dist/prompts/product-knowledge.d.ts.map +1 -0
  1012. package/dist/prompts/product-knowledge.js +79 -0
  1013. package/dist/prompts/product-knowledge.js.map +1 -0
  1014. package/dist/prompts/read-only-search-prompt.d.ts +33 -0
  1015. package/dist/prompts/read-only-search-prompt.d.ts.map +1 -0
  1016. package/dist/prompts/read-only-search-prompt.js +109 -0
  1017. package/dist/prompts/read-only-search-prompt.js.map +1 -0
  1018. package/dist/prompts/sandbox-pack-catalog.d.ts +23 -0
  1019. package/dist/prompts/sandbox-pack-catalog.d.ts.map +1 -0
  1020. package/dist/prompts/sandbox-pack-catalog.js +40 -0
  1021. package/dist/prompts/sandbox-pack-catalog.js.map +1 -0
  1022. package/dist/prompts/security-monitor-prompt.d.ts +32 -0
  1023. package/dist/prompts/security-monitor-prompt.d.ts.map +1 -0
  1024. package/dist/prompts/security-monitor-prompt.js +99 -0
  1025. package/dist/prompts/security-monitor-prompt.js.map +1 -0
  1026. package/dist/prompts/workflow-authoring-knowledge.d.ts +77 -0
  1027. package/dist/prompts/workflow-authoring-knowledge.d.ts.map +1 -0
  1028. package/dist/prompts/workflow-authoring-knowledge.js +319 -0
  1029. package/dist/prompts/workflow-authoring-knowledge.js.map +1 -0
  1030. package/dist/run-policy.d.ts +25 -0
  1031. package/dist/run-policy.d.ts.map +1 -0
  1032. package/dist/run-policy.js +54 -0
  1033. package/dist/run-policy.js.map +1 -0
  1034. package/dist/sandbox-av-media.d.ts +33 -0
  1035. package/dist/sandbox-av-media.d.ts.map +1 -0
  1036. package/dist/sandbox-av-media.js +666 -0
  1037. package/dist/sandbox-av-media.js.map +1 -0
  1038. package/dist/sandbox-bootstrap-modules.d.ts +54 -0
  1039. package/dist/sandbox-bootstrap-modules.d.ts.map +1 -0
  1040. package/dist/sandbox-bootstrap-modules.js +104 -0
  1041. package/dist/sandbox-bootstrap-modules.js.map +1 -0
  1042. package/dist/sandbox-bytes.d.ts +43 -0
  1043. package/dist/sandbox-bytes.d.ts.map +1 -0
  1044. package/dist/sandbox-bytes.js +61 -0
  1045. package/dist/sandbox-bytes.js.map +1 -0
  1046. package/dist/sandbox-canvas-api.d.ts +30 -0
  1047. package/dist/sandbox-canvas-api.d.ts.map +1 -0
  1048. package/dist/sandbox-canvas-api.js +81 -0
  1049. package/dist/sandbox-canvas-api.js.map +1 -0
  1050. package/dist/sandbox-constants.d.ts +5 -0
  1051. package/dist/sandbox-constants.d.ts.map +1 -0
  1052. package/dist/sandbox-constants.js +13 -0
  1053. package/dist/sandbox-constants.js.map +1 -0
  1054. package/dist/sandbox-json-transport.d.ts +107 -0
  1055. package/dist/sandbox-json-transport.d.ts.map +1 -0
  1056. package/dist/sandbox-json-transport.js +324 -0
  1057. package/dist/sandbox-json-transport.js.map +1 -0
  1058. package/dist/sandbox-media-handle.d.ts +78 -0
  1059. package/dist/sandbox-media-handle.d.ts.map +1 -0
  1060. package/dist/sandbox-media-handle.js +114 -0
  1061. package/dist/sandbox-media-handle.js.map +1 -0
  1062. package/dist/sandbox-media-ref.d.ts +134 -0
  1063. package/dist/sandbox-media-ref.d.ts.map +1 -0
  1064. package/dist/sandbox-media-ref.js +442 -0
  1065. package/dist/sandbox-media-ref.js.map +1 -0
  1066. package/dist/sandbox-media.d.ts +177 -0
  1067. package/dist/sandbox-media.d.ts.map +1 -0
  1068. package/dist/sandbox-media.js +963 -0
  1069. package/dist/sandbox-media.js.map +1 -0
  1070. package/dist/sandbox-module-fixtures.d.ts +445 -0
  1071. package/dist/sandbox-module-fixtures.d.ts.map +1 -0
  1072. package/dist/sandbox-module-fixtures.js +214 -0
  1073. package/dist/sandbox-module-fixtures.js.map +1 -0
  1074. package/dist/sandbox-toolbelt.d.ts +13 -0
  1075. package/dist/sandbox-toolbelt.d.ts.map +1 -0
  1076. package/dist/sandbox-toolbelt.js +64 -0
  1077. package/dist/sandbox-toolbelt.js.map +1 -0
  1078. package/dist/security-monitor.d.ts +89 -0
  1079. package/dist/security-monitor.d.ts.map +1 -0
  1080. package/dist/security-monitor.js +307 -0
  1081. package/dist/security-monitor.js.map +1 -0
  1082. package/dist/serpapi/catalog.d.ts +101 -0
  1083. package/dist/serpapi/catalog.d.ts.map +1 -0
  1084. package/dist/serpapi/catalog.js +246 -0
  1085. package/dist/serpapi/catalog.js.map +1 -0
  1086. package/dist/serpapi/client.d.ts +81 -0
  1087. package/dist/serpapi/client.d.ts.map +1 -0
  1088. package/dist/serpapi/client.js +228 -0
  1089. package/dist/serpapi/client.js.map +1 -0
  1090. package/dist/serpapi/errors.d.ts +49 -0
  1091. package/dist/serpapi/errors.d.ts.map +1 -0
  1092. package/dist/serpapi/errors.js +64 -0
  1093. package/dist/serpapi/errors.js.map +1 -0
  1094. package/dist/serpapi/normalize.d.ts +58 -0
  1095. package/dist/serpapi/normalize.d.ts.map +1 -0
  1096. package/dist/serpapi/normalize.js +140 -0
  1097. package/dist/serpapi/normalize.js.map +1 -0
  1098. package/dist/skill-prompt.d.ts +27 -0
  1099. package/dist/skill-prompt.d.ts.map +1 -0
  1100. package/dist/skill-prompt.js +61 -0
  1101. package/dist/skill-prompt.js.map +1 -0
  1102. package/dist/step-executor.d.ts +90 -77
  1103. package/dist/step-executor.d.ts.map +1 -1
  1104. package/dist/step-executor.js +384 -690
  1105. package/dist/step-executor.js.map +1 -1
  1106. package/dist/subagent.d.ts +275 -0
  1107. package/dist/subagent.d.ts.map +1 -0
  1108. package/dist/subagent.js +444 -0
  1109. package/dist/subagent.js.map +1 -0
  1110. package/dist/supervisor/prompt.d.ts +12 -0
  1111. package/dist/supervisor/prompt.d.ts.map +1 -0
  1112. package/dist/supervisor/prompt.js +51 -0
  1113. package/dist/supervisor/prompt.js.map +1 -0
  1114. package/dist/supervisor/supervisor-agent.d.ts +72 -0
  1115. package/dist/supervisor/supervisor-agent.d.ts.map +1 -0
  1116. package/dist/supervisor/supervisor-agent.js +185 -0
  1117. package/dist/supervisor/supervisor-agent.js.map +1 -0
  1118. package/dist/supervisor/tools.d.ts +56 -0
  1119. package/dist/supervisor/tools.d.ts.map +1 -0
  1120. package/dist/supervisor/tools.js +83 -0
  1121. package/dist/supervisor/tools.js.map +1 -0
  1122. package/dist/supervisor/verdict-schema.d.ts +18 -0
  1123. package/dist/supervisor/verdict-schema.d.ts.map +1 -0
  1124. package/dist/supervisor/verdict-schema.js +58 -0
  1125. package/dist/supervisor/verdict-schema.js.map +1 -0
  1126. package/dist/system-skills.d.ts +68 -0
  1127. package/dist/system-skills.d.ts.map +1 -0
  1128. package/dist/system-skills.js +168 -0
  1129. package/dist/system-skills.js.map +1 -0
  1130. package/dist/task-executor.d.ts +90 -40
  1131. package/dist/task-executor.d.ts.map +1 -1
  1132. package/dist/task-executor.js +259 -282
  1133. package/dist/task-executor.js.map +1 -1
  1134. package/dist/task-planner.d.ts +18 -12
  1135. package/dist/task-planner.d.ts.map +1 -1
  1136. package/dist/task-planner.js +342 -328
  1137. package/dist/task-planner.js.map +1 -1
  1138. package/dist/timeline-preview/compare.d.ts +27 -0
  1139. package/dist/timeline-preview/compare.d.ts.map +1 -0
  1140. package/dist/timeline-preview/compare.js +71 -0
  1141. package/dist/timeline-preview/compare.js.map +1 -0
  1142. package/dist/timeline-preview/frames.d.ts +167 -0
  1143. package/dist/timeline-preview/frames.d.ts.map +1 -0
  1144. package/dist/timeline-preview/frames.js +646 -0
  1145. package/dist/timeline-preview/frames.js.map +1 -0
  1146. package/dist/timeline-preview/model3d.d.ts +67 -0
  1147. package/dist/timeline-preview/model3d.d.ts.map +1 -0
  1148. package/dist/timeline-preview/model3d.js +165 -0
  1149. package/dist/timeline-preview/model3d.js.map +1 -0
  1150. package/dist/timeline-preview/rasterize.d.ts +33 -0
  1151. package/dist/timeline-preview/rasterize.d.ts.map +1 -0
  1152. package/dist/timeline-preview/rasterize.js +83 -0
  1153. package/dist/timeline-preview/rasterize.js.map +1 -0
  1154. package/dist/timeline-preview/sheet.d.ts +38 -0
  1155. package/dist/timeline-preview/sheet.d.ts.map +1 -0
  1156. package/dist/timeline-preview/sheet.js +72 -0
  1157. package/dist/timeline-preview/sheet.js.map +1 -0
  1158. package/dist/tools/asset-persist.d.ts +29 -0
  1159. package/dist/tools/asset-persist.d.ts.map +1 -0
  1160. package/dist/tools/asset-persist.js +78 -0
  1161. package/dist/tools/asset-persist.js.map +1 -0
  1162. package/dist/tools/base-tool.d.ts +52 -5
  1163. package/dist/tools/base-tool.d.ts.map +1 -1
  1164. package/dist/tools/base-tool.js +180 -3
  1165. package/dist/tools/base-tool.js.map +1 -1
  1166. package/dist/tools/binary-output.d.ts +54 -0
  1167. package/dist/tools/binary-output.d.ts.map +1 -0
  1168. package/dist/tools/binary-output.js +127 -0
  1169. package/dist/tools/binary-output.js.map +1 -0
  1170. package/dist/tools/browser-tools.d.ts +5 -29
  1171. package/dist/tools/browser-tools.d.ts.map +1 -1
  1172. package/dist/tools/browser-tools.js +5 -205
  1173. package/dist/tools/browser-tools.js.map +1 -1
  1174. package/dist/tools/builtin-tools.d.ts +52 -0
  1175. package/dist/tools/builtin-tools.d.ts.map +1 -0
  1176. package/dist/tools/builtin-tools.js +276 -0
  1177. package/dist/tools/builtin-tools.js.map +1 -0
  1178. package/dist/tools/control-tool.d.ts +2 -5
  1179. package/dist/tools/control-tool.d.ts.map +1 -1
  1180. package/dist/tools/control-tool.js +18 -15
  1181. package/dist/tools/control-tool.js.map +1 -1
  1182. package/dist/tools/dataseo-tools.d.ts +34 -32
  1183. package/dist/tools/dataseo-tools.d.ts.map +1 -1
  1184. package/dist/tools/dataseo-tools.js +148 -233
  1185. package/dist/tools/dataseo-tools.js.map +1 -1
  1186. package/dist/tools/external-capability-tools.d.ts +36 -0
  1187. package/dist/tools/external-capability-tools.d.ts.map +1 -0
  1188. package/dist/tools/external-capability-tools.js +41 -0
  1189. package/dist/tools/external-capability-tools.js.map +1 -0
  1190. package/dist/tools/external-mcp-tools.d.ts +139 -0
  1191. package/dist/tools/external-mcp-tools.d.ts.map +1 -0
  1192. package/dist/tools/external-mcp-tools.js +552 -0
  1193. package/dist/tools/external-mcp-tools.js.map +1 -0
  1194. package/dist/tools/finish-step-tool.d.ts +1 -1
  1195. package/dist/tools/finish-step-tool.d.ts.map +1 -1
  1196. package/dist/tools/finish-step-tool.js +3 -3
  1197. package/dist/tools/finish-step-tool.js.map +1 -1
  1198. package/dist/tools/google-tools.d.ts +28 -39
  1199. package/dist/tools/google-tools.d.ts.map +1 -1
  1200. package/dist/tools/google-tools.js +51 -164
  1201. package/dist/tools/google-tools.js.map +1 -1
  1202. package/dist/tools/google-workspace-tools.d.ts +23 -0
  1203. package/dist/tools/google-workspace-tools.d.ts.map +1 -0
  1204. package/dist/tools/google-workspace-tools.js +26 -0
  1205. package/dist/tools/google-workspace-tools.js.map +1 -0
  1206. package/dist/tools/graph-validation-registry.d.ts +33 -0
  1207. package/dist/tools/graph-validation-registry.d.ts.map +1 -0
  1208. package/dist/tools/graph-validation-registry.js +54 -0
  1209. package/dist/tools/graph-validation-registry.js.map +1 -0
  1210. package/dist/tools/http-tools.d.ts +5 -53
  1211. package/dist/tools/http-tools.d.ts.map +1 -1
  1212. package/dist/tools/http-tools.js +5 -177
  1213. package/dist/tools/http-tools.js.map +1 -1
  1214. package/dist/tools/image-injection.d.ts +51 -0
  1215. package/dist/tools/image-injection.d.ts.map +1 -0
  1216. package/dist/tools/image-injection.js +85 -0
  1217. package/dist/tools/image-injection.js.map +1 -0
  1218. package/dist/tools/mcp-tool-support.d.ts +203 -0
  1219. package/dist/tools/mcp-tool-support.d.ts.map +1 -0
  1220. package/dist/tools/mcp-tool-support.js +536 -0
  1221. package/dist/tools/mcp-tool-support.js.map +1 -0
  1222. package/dist/tools/mcp-tools.d.ts +104 -390
  1223. package/dist/tools/mcp-tools.d.ts.map +1 -1
  1224. package/dist/tools/mcp-tools.js +128 -600
  1225. package/dist/tools/mcp-tools.js.map +1 -1
  1226. package/dist/tools/media-tools.d.ts +9 -272
  1227. package/dist/tools/media-tools.d.ts.map +1 -1
  1228. package/dist/tools/media-tools.js +11 -676
  1229. package/dist/tools/media-tools.js.map +1 -1
  1230. package/dist/tools/memory-tools.d.ts +36 -0
  1231. package/dist/tools/memory-tools.d.ts.map +1 -0
  1232. package/dist/tools/memory-tools.js +52 -0
  1233. package/dist/tools/memory-tools.js.map +1 -0
  1234. package/dist/tools/openai-tools.d.ts +28 -68
  1235. package/dist/tools/openai-tools.d.ts.map +1 -1
  1236. package/dist/tools/openai-tools.js +51 -181
  1237. package/dist/tools/openai-tools.js.map +1 -1
  1238. package/dist/tools/plan-builder-tools.d.ts +141 -7
  1239. package/dist/tools/plan-builder-tools.d.ts.map +1 -1
  1240. package/dist/tools/plan-builder-tools.js +159 -86
  1241. package/dist/tools/plan-builder-tools.js.map +1 -1
  1242. package/dist/tools/run-search-tool.d.ts +82 -0
  1243. package/dist/tools/run-search-tool.d.ts.map +1 -0
  1244. package/dist/tools/run-search-tool.js +121 -0
  1245. package/dist/tools/run-search-tool.js.map +1 -0
  1246. package/dist/tools/run-subtask-tool.d.ts +49 -0
  1247. package/dist/tools/run-subtask-tool.d.ts.map +1 -0
  1248. package/dist/tools/run-subtask-tool.js +88 -0
  1249. package/dist/tools/run-subtask-tool.js.map +1 -0
  1250. package/dist/tools/search-tools.d.ts +5 -36
  1251. package/dist/tools/search-tools.d.ts.map +1 -1
  1252. package/dist/tools/search-tools.js +5 -215
  1253. package/dist/tools/search-tools.js.map +1 -1
  1254. package/dist/tools/serp-providers/apify-provider.d.ts +18 -0
  1255. package/dist/tools/serp-providers/apify-provider.d.ts.map +1 -0
  1256. package/dist/tools/serp-providers/apify-provider.js +93 -0
  1257. package/dist/tools/serp-providers/apify-provider.js.map +1 -0
  1258. package/dist/tools/serp-providers/brave-provider.d.ts +34 -0
  1259. package/dist/tools/serp-providers/brave-provider.d.ts.map +1 -0
  1260. package/dist/tools/serp-providers/brave-provider.js +86 -0
  1261. package/dist/tools/serp-providers/brave-provider.js.map +1 -0
  1262. package/dist/tools/serp-providers/dataforseo-provider.d.ts +14 -1
  1263. package/dist/tools/serp-providers/dataforseo-provider.d.ts.map +1 -1
  1264. package/dist/tools/serp-providers/dataforseo-provider.js +0 -9
  1265. package/dist/tools/serp-providers/dataforseo-provider.js.map +1 -1
  1266. package/dist/tools/serp-providers/gemini-provider.d.ts +45 -0
  1267. package/dist/tools/serp-providers/gemini-provider.d.ts.map +1 -0
  1268. package/dist/tools/serp-providers/gemini-provider.js +148 -0
  1269. package/dist/tools/serp-providers/gemini-provider.js.map +1 -0
  1270. package/dist/tools/serp-providers/index.d.ts +61 -0
  1271. package/dist/tools/serp-providers/index.d.ts.map +1 -1
  1272. package/dist/tools/serp-providers/index.js +119 -3
  1273. package/dist/tools/serp-providers/index.js.map +1 -1
  1274. package/dist/tools/serp-providers/openai-provider.d.ts +58 -0
  1275. package/dist/tools/serp-providers/openai-provider.d.ts.map +1 -0
  1276. package/dist/tools/serp-providers/openai-provider.js +113 -0
  1277. package/dist/tools/serp-providers/openai-provider.js.map +1 -0
  1278. package/dist/tools/serp-providers/serpapi-provider.d.ts +9 -5
  1279. package/dist/tools/serp-providers/serpapi-provider.d.ts.map +1 -1
  1280. package/dist/tools/serp-providers/serpapi-provider.js +26 -44
  1281. package/dist/tools/serp-providers/serpapi-provider.js.map +1 -1
  1282. package/dist/tools/serp-tool-factory.d.ts +27 -0
  1283. package/dist/tools/serp-tool-factory.d.ts.map +1 -0
  1284. package/dist/tools/serp-tool-factory.js +56 -0
  1285. package/dist/tools/serp-tool-factory.js.map +1 -0
  1286. package/dist/tools/shared-tools.d.ts +33 -0
  1287. package/dist/tools/shared-tools.d.ts.map +1 -0
  1288. package/dist/tools/shared-tools.js +39 -0
  1289. package/dist/tools/shared-tools.js.map +1 -0
  1290. package/dist/tools/start-subtask-tool.d.ts +51 -0
  1291. package/dist/tools/start-subtask-tool.d.ts.map +1 -0
  1292. package/dist/tools/start-subtask-tool.js +189 -0
  1293. package/dist/tools/start-subtask-tool.js.map +1 -0
  1294. package/dist/tools/submit-code-tool.d.ts +159 -0
  1295. package/dist/tools/submit-code-tool.d.ts.map +1 -0
  1296. package/dist/tools/submit-code-tool.js +209 -0
  1297. package/dist/tools/submit-code-tool.js.map +1 -0
  1298. package/dist/tools/subtask-fields.d.ts +11 -0
  1299. package/dist/tools/subtask-fields.d.ts.map +1 -0
  1300. package/dist/tools/subtask-fields.js +11 -0
  1301. package/dist/tools/subtask-fields.js.map +1 -0
  1302. package/dist/tools/todo-tools.d.ts +15 -0
  1303. package/dist/tools/todo-tools.d.ts.map +1 -0
  1304. package/dist/tools/todo-tools.js +15 -0
  1305. package/dist/tools/todo-tools.js.map +1 -0
  1306. package/dist/tools/tool-permissions.d.ts +40 -0
  1307. package/dist/tools/tool-permissions.d.ts.map +1 -0
  1308. package/dist/tools/tool-permissions.js +52 -0
  1309. package/dist/tools/tool-permissions.js.map +1 -0
  1310. package/dist/tools/tool-search.d.ts +39 -0
  1311. package/dist/tools/tool-search.d.ts.map +1 -0
  1312. package/dist/tools/tool-search.js +108 -0
  1313. package/dist/tools/tool-search.js.map +1 -0
  1314. package/dist/tools/vector-tool-support.d.ts +15 -0
  1315. package/dist/tools/vector-tool-support.d.ts.map +1 -0
  1316. package/dist/tools/vector-tool-support.js +133 -0
  1317. package/dist/tools/vector-tool-support.js.map +1 -0
  1318. package/dist/tools/vector-tools.d.ts +13 -220
  1319. package/dist/tools/vector-tools.d.ts.map +1 -1
  1320. package/dist/tools/vector-tools.js +7 -578
  1321. package/dist/tools/vector-tools.js.map +1 -1
  1322. package/dist/tools/wait-subtasks-tool.d.ts +27 -0
  1323. package/dist/tools/wait-subtasks-tool.d.ts.map +1 -0
  1324. package/dist/tools/wait-subtasks-tool.js +66 -0
  1325. package/dist/tools/wait-subtasks-tool.js.map +1 -0
  1326. package/dist/tsconfig.tests.tmp.tsbuildinfo +1 -0
  1327. package/dist/types.d.ts +16 -31
  1328. package/dist/types.d.ts.map +1 -1
  1329. package/dist/types.js +8 -1
  1330. package/dist/types.js.map +1 -1
  1331. package/dist/utils/dag-scheduler.d.ts +85 -0
  1332. package/dist/utils/dag-scheduler.d.ts.map +1 -0
  1333. package/dist/utils/dag-scheduler.js +180 -0
  1334. package/dist/utils/dag-scheduler.js.map +1 -0
  1335. package/dist/utils/json-parser.d.ts +32 -1
  1336. package/dist/utils/json-parser.d.ts.map +1 -1
  1337. package/dist/utils/json-parser.js +145 -10
  1338. package/dist/utils/json-parser.js.map +1 -1
  1339. package/dist/utils/json-schema-validate.d.ts +29 -0
  1340. package/dist/utils/json-schema-validate.d.ts.map +1 -0
  1341. package/dist/utils/json-schema-validate.js +228 -0
  1342. package/dist/utils/json-schema-validate.js.map +1 -0
  1343. package/dist/utils/link-abort.d.ts +13 -0
  1344. package/dist/utils/link-abort.d.ts.map +1 -0
  1345. package/dist/utils/link-abort.js +23 -0
  1346. package/dist/utils/link-abort.js.map +1 -0
  1347. package/dist/utils/merge-generators.d.ts +58 -0
  1348. package/dist/utils/merge-generators.d.ts.map +1 -0
  1349. package/dist/utils/merge-generators.js +148 -0
  1350. package/dist/utils/merge-generators.js.map +1 -0
  1351. package/dist/utils/remove-base64-images.d.ts +0 -5
  1352. package/dist/utils/remove-base64-images.d.ts.map +1 -1
  1353. package/dist/utils/remove-base64-images.js.map +1 -1
  1354. package/dist/utils/step-failure.d.ts +20 -0
  1355. package/dist/utils/step-failure.d.ts.map +1 -0
  1356. package/dist/utils/step-failure.js +43 -0
  1357. package/dist/utils/step-failure.js.map +1 -0
  1358. package/dist/utils/think-tags.d.ts +16 -0
  1359. package/dist/utils/think-tags.d.ts.map +1 -0
  1360. package/dist/utils/think-tags.js +23 -0
  1361. package/dist/utils/think-tags.js.map +1 -0
  1362. package/dist/utils/type-guards.d.ts +26 -0
  1363. package/dist/utils/type-guards.d.ts.map +1 -0
  1364. package/dist/utils/type-guards.js +44 -0
  1365. package/dist/utils/type-guards.js.map +1 -0
  1366. package/dist/utils/ui-event-buffer.d.ts +18 -0
  1367. package/dist/utils/ui-event-buffer.d.ts.map +1 -0
  1368. package/dist/utils/ui-event-buffer.js +18 -0
  1369. package/dist/utils/ui-event-buffer.js.map +1 -0
  1370. package/dist/utils/wrap-generators-parallel.d.ts +0 -5
  1371. package/dist/utils/wrap-generators-parallel.d.ts.map +1 -1
  1372. package/dist/utils/wrap-generators-parallel.js +3 -6
  1373. package/dist/utils/wrap-generators-parallel.js.map +1 -1
  1374. package/dist/wasm-sandbox/host.d.ts +106 -0
  1375. package/dist/wasm-sandbox/host.d.ts.map +1 -0
  1376. package/dist/wasm-sandbox/host.js +409 -0
  1377. package/dist/wasm-sandbox/host.js.map +1 -0
  1378. package/dist/wasm-sandbox/workers.d.ts +43 -0
  1379. package/dist/wasm-sandbox/workers.d.ts.map +1 -0
  1380. package/dist/wasm-sandbox/workers.js +150 -0
  1381. package/dist/wasm-sandbox/workers.js.map +1 -0
  1382. package/dist/workspace-paths.d.ts +40 -0
  1383. package/dist/workspace-paths.d.ts.map +1 -0
  1384. package/dist/workspace-paths.js +102 -0
  1385. package/dist/workspace-paths.js.map +1 -0
  1386. package/dist/yt-dlp-gate.d.ts +19 -0
  1387. package/dist/yt-dlp-gate.d.ts.map +1 -0
  1388. package/dist/yt-dlp-gate.js +22 -0
  1389. package/dist/yt-dlp-gate.js.map +1 -0
  1390. package/package.json +112 -14
  1391. package/dist/agent-executor.d.ts +0 -62
  1392. package/dist/agent-executor.d.ts.map +0 -1
  1393. package/dist/agent-executor.js +0 -324
  1394. package/dist/agent-executor.js.map +0 -1
  1395. package/dist/agent-step-executor.d.ts +0 -28
  1396. package/dist/agent-step-executor.d.ts.map +0 -1
  1397. package/dist/agent-step-executor.js +0 -99
  1398. package/dist/agent-step-executor.js.map +0 -1
  1399. package/dist/agent-workflow-runner.d.ts +0 -31
  1400. package/dist/agent-workflow-runner.d.ts.map +0 -1
  1401. package/dist/agent-workflow-runner.js +0 -124
  1402. package/dist/agent-workflow-runner.js.map +0 -1
  1403. package/dist/agent.d.ts +0 -115
  1404. package/dist/agent.d.ts.map +0 -1
  1405. package/dist/agent.js +0 -587
  1406. package/dist/agent.js.map +0 -1
  1407. package/dist/base-agent.d.ts +0 -42
  1408. package/dist/base-agent.d.ts.map +0 -1
  1409. package/dist/base-agent.js +0 -29
  1410. package/dist/base-agent.js.map +0 -1
  1411. package/dist/graph-planner.d.ts +0 -56
  1412. package/dist/graph-planner.d.ts.map +0 -1
  1413. package/dist/graph-planner.js +0 -311
  1414. package/dist/graph-planner.js.map +0 -1
  1415. package/dist/multi-mode-agent.d.ts +0 -86
  1416. package/dist/multi-mode-agent.d.ts.map +0 -1
  1417. package/dist/multi-mode-agent.js +0 -476
  1418. package/dist/multi-mode-agent.js.map +0 -1
  1419. package/dist/prompts/graph-planner-prompt.d.ts +0 -48
  1420. package/dist/prompts/graph-planner-prompt.d.ts.map +0 -1
  1421. package/dist/prompts/graph-planner-prompt.js +0 -180
  1422. package/dist/prompts/graph-planner-prompt.js.map +0 -1
  1423. package/dist/reject-agentic-provider.d.ts +0 -21
  1424. package/dist/reject-agentic-provider.d.ts.map +0 -1
  1425. package/dist/reject-agentic-provider.js +0 -32
  1426. package/dist/reject-agentic-provider.js.map +0 -1
  1427. package/dist/simple-agent.d.ts +0 -29
  1428. package/dist/simple-agent.d.ts.map +0 -1
  1429. package/dist/simple-agent.js +0 -54
  1430. package/dist/simple-agent.js.map +0 -1
  1431. package/dist/sub-agent-planner.d.ts +0 -33
  1432. package/dist/sub-agent-planner.d.ts.map +0 -1
  1433. package/dist/sub-agent-planner.js +0 -203
  1434. package/dist/sub-agent-planner.js.map +0 -1
  1435. package/dist/team/db-task-board.d.ts +0 -54
  1436. package/dist/team/db-task-board.d.ts.map +0 -1
  1437. package/dist/team/db-task-board.js +0 -361
  1438. package/dist/team/db-task-board.js.map +0 -1
  1439. package/dist/team/edge-message-bus.d.ts +0 -51
  1440. package/dist/team/edge-message-bus.d.ts.map +0 -1
  1441. package/dist/team/edge-message-bus.js +0 -169
  1442. package/dist/team/edge-message-bus.js.map +0 -1
  1443. package/dist/team/edge-task-board.d.ts +0 -77
  1444. package/dist/team/edge-task-board.d.ts.map +0 -1
  1445. package/dist/team/edge-task-board.js +0 -140
  1446. package/dist/team/edge-task-board.js.map +0 -1
  1447. package/dist/team/index.d.ts +0 -16
  1448. package/dist/team/index.d.ts.map +0 -1
  1449. package/dist/team/index.js +0 -17
  1450. package/dist/team/index.js.map +0 -1
  1451. package/dist/team/message-bus.d.ts +0 -58
  1452. package/dist/team/message-bus.d.ts.map +0 -1
  1453. package/dist/team/message-bus.js +0 -151
  1454. package/dist/team/message-bus.js.map +0 -1
  1455. package/dist/team/task-board.d.ts +0 -106
  1456. package/dist/team/task-board.d.ts.map +0 -1
  1457. package/dist/team/task-board.js +0 -370
  1458. package/dist/team/task-board.js.map +0 -1
  1459. package/dist/team/team-executor.d.ts +0 -72
  1460. package/dist/team/team-executor.d.ts.map +0 -1
  1461. package/dist/team/team-executor.js +0 -466
  1462. package/dist/team/team-executor.js.map +0 -1
  1463. package/dist/team/team-tools.d.ts +0 -252
  1464. package/dist/team/team-tools.d.ts.map +0 -1
  1465. package/dist/team/team-tools.js +0 -412
  1466. package/dist/team/team-tools.js.map +0 -1
  1467. package/dist/team/types.d.ts +0 -198
  1468. package/dist/team/types.d.ts.map +0 -1
  1469. package/dist/team/types.js +0 -8
  1470. package/dist/team/types.js.map +0 -1
  1471. package/dist/tools/add-edge-tool.d.ts +0 -18
  1472. package/dist/tools/add-edge-tool.d.ts.map +0 -1
  1473. package/dist/tools/add-edge-tool.js +0 -92
  1474. package/dist/tools/add-edge-tool.js.map +0 -1
  1475. package/dist/tools/add-node-tool.d.ts +0 -22
  1476. package/dist/tools/add-node-tool.d.ts.map +0 -1
  1477. package/dist/tools/add-node-tool.js +0 -128
  1478. package/dist/tools/add-node-tool.js.map +0 -1
  1479. package/dist/tools/asset-tools.d.ts +0 -58
  1480. package/dist/tools/asset-tools.d.ts.map +0 -1
  1481. package/dist/tools/asset-tools.js +0 -191
  1482. package/dist/tools/asset-tools.js.map +0 -1
  1483. package/dist/tools/calculator-tool.d.ts +0 -24
  1484. package/dist/tools/calculator-tool.d.ts.map +0 -1
  1485. package/dist/tools/calculator-tool.js +0 -64
  1486. package/dist/tools/calculator-tool.js.map +0 -1
  1487. package/dist/tools/claude-code-tools.d.ts +0 -96
  1488. package/dist/tools/claude-code-tools.d.ts.map +0 -1
  1489. package/dist/tools/claude-code-tools.js +0 -463
  1490. package/dist/tools/claude-code-tools.js.map +0 -1
  1491. package/dist/tools/code-tools.d.ts +0 -26
  1492. package/dist/tools/code-tools.d.ts.map +0 -1
  1493. package/dist/tools/code-tools.js +0 -82
  1494. package/dist/tools/code-tools.js.map +0 -1
  1495. package/dist/tools/create-plan-tool.d.ts +0 -29
  1496. package/dist/tools/create-plan-tool.d.ts.map +0 -1
  1497. package/dist/tools/create-plan-tool.js +0 -355
  1498. package/dist/tools/create-plan-tool.js.map +0 -1
  1499. package/dist/tools/create-task-tool.d.ts +0 -28
  1500. package/dist/tools/create-task-tool.d.ts.map +0 -1
  1501. package/dist/tools/create-task-tool.js +0 -254
  1502. package/dist/tools/create-task-tool.js.map +0 -1
  1503. package/dist/tools/email-tools.d.ts +0 -76
  1504. package/dist/tools/email-tools.d.ts.map +0 -1
  1505. package/dist/tools/email-tools.js +0 -259
  1506. package/dist/tools/email-tools.js.map +0 -1
  1507. package/dist/tools/filesystem-tools.d.ts +0 -77
  1508. package/dist/tools/filesystem-tools.d.ts.map +0 -1
  1509. package/dist/tools/filesystem-tools.js +0 -334
  1510. package/dist/tools/filesystem-tools.js.map +0 -1
  1511. package/dist/tools/find-model-tool.d.ts +0 -25
  1512. package/dist/tools/find-model-tool.d.ts.map +0 -1
  1513. package/dist/tools/find-model-tool.js +0 -236
  1514. package/dist/tools/find-model-tool.js.map +0 -1
  1515. package/dist/tools/finish-graph-tool.d.ts +0 -23
  1516. package/dist/tools/finish-graph-tool.d.ts.map +0 -1
  1517. package/dist/tools/finish-graph-tool.js +0 -46
  1518. package/dist/tools/finish-graph-tool.js.map +0 -1
  1519. package/dist/tools/js-code-tool.d.ts +0 -30
  1520. package/dist/tools/js-code-tool.d.ts.map +0 -1
  1521. package/dist/tools/js-code-tool.js +0 -98
  1522. package/dist/tools/js-code-tool.js.map +0 -1
  1523. package/dist/tools/local-get-node-info-tool.d.ts +0 -19
  1524. package/dist/tools/local-get-node-info-tool.d.ts.map +0 -1
  1525. package/dist/tools/local-get-node-info-tool.js +0 -75
  1526. package/dist/tools/local-get-node-info-tool.js.map +0 -1
  1527. package/dist/tools/local-list-nodes-tool.d.ts +0 -19
  1528. package/dist/tools/local-list-nodes-tool.d.ts.map +0 -1
  1529. package/dist/tools/local-list-nodes-tool.js +0 -69
  1530. package/dist/tools/local-list-nodes-tool.js.map +0 -1
  1531. package/dist/tools/local-search-nodes-tool.d.ts +0 -21
  1532. package/dist/tools/local-search-nodes-tool.d.ts.map +0 -1
  1533. package/dist/tools/local-search-nodes-tool.js +0 -111
  1534. package/dist/tools/local-search-nodes-tool.js.map +0 -1
  1535. package/dist/tools/math-tools.d.ts +0 -156
  1536. package/dist/tools/math-tools.d.ts.map +0 -1
  1537. package/dist/tools/math-tools.js +0 -422
  1538. package/dist/tools/math-tools.js.map +0 -1
  1539. package/dist/tools/model-tools.d.ts +0 -25
  1540. package/dist/tools/model-tools.d.ts.map +0 -1
  1541. package/dist/tools/model-tools.js +0 -48
  1542. package/dist/tools/model-tools.js.map +0 -1
  1543. package/dist/tools/pdf-tools.d.ts +0 -152
  1544. package/dist/tools/pdf-tools.d.ts.map +0 -1
  1545. package/dist/tools/pdf-tools.js +0 -339
  1546. package/dist/tools/pdf-tools.js.map +0 -1
  1547. package/dist/tools/tool-registry.d.ts +0 -9
  1548. package/dist/tools/tool-registry.d.ts.map +0 -1
  1549. package/dist/tools/tool-registry.js +0 -17
  1550. package/dist/tools/tool-registry.js.map +0 -1
  1551. package/dist/tools/workspace-tools.d.ts +0 -58
  1552. package/dist/tools/workspace-tools.d.ts.map +0 -1
  1553. package/dist/tools/workspace-tools.js +0 -152
  1554. package/dist/tools/workspace-tools.js.map +0 -1
@@ -0,0 +1,3267 @@
1
+ /**
2
+ * Headless bridge for the timeline / video editor tool-loop eval.
3
+ *
4
+ * The real frontend tools (`web/src/lib/tools/builtin/timeline.ts`) delegate to
5
+ * a `TimelineAgentHandler` the live `TimelineEditor` registers per open
6
+ * sequence on `timelineAgentBridge` — it mutates Zustand document/UI/playback
7
+ * stores and (for `ui_timeline_get_clip_frames`) samples rendered video
8
+ * frames. None of that can run under Node. This bridge reimplements the
9
+ * *effects* of the non-rendering tools against a plain in-memory sequence
10
+ * (tracks + clips from `@nodetool-ai/timeline`), so a model can drive the same
11
+ * `ui_timeline_*` tool surface headlessly.
12
+ *
13
+ * What it does NOT fork is the tool *contract*: names, descriptions, and Zod
14
+ * parameter shapes are copied verbatim from the builtin file (minus the
15
+ * `timeline_id` parameter — this bridge holds a single implicit sequence, so
16
+ * there is nothing to disambiguate). `ui_timeline_get_clip_frames` is
17
+ * intentionally excluded: it requires real rendered video frames and has no
18
+ * meaningful headless equivalent.
19
+ *
20
+ * Real cut/preset logic is reused from the pure `@nodetool-ai/timeline`
21
+ * package (`splitClip`, `ANIMATION_PRESETS`, `makeClip`/`makeTrack`) rather
22
+ * than reimplemented, so this bridge exercises the same behavior the live
23
+ * editor does.
24
+ */
25
+ import { z } from "zod";
26
+ import { isShortResourceId } from "@nodetool-ai/protocol";
27
+ import { parseWithTypeCoercion } from "@nodetool-ai/runtime";
28
+ import { splitClip, trimClip, ANIMATION_PRESETS, ANIMATED_PROPERTIES, buildBakedAnimation, CUSTOM_ANIMATION_CONTRACT, CUSTOM_ANIMATION_PRESET_ID, normalizeCustomCurves, resolveCustomMask, computeModel3DBakeHash, makeClip, makeClipVersion, makeTrack, DEFAULT_TEXT_CLIP_DURATION_MS, DEFAULT_MEDIA_CLIP_DURATION_MS, DEFAULT_MODEL3D_CLIP_DURATION_MS, DEFAULT_MODEL3D_CLIP_NAME, model3dStyleWithPatch, shapeStyleWithDefaults, assertAuthorableFontFamily, textStyleWithDefaults, moveTrackOrder, mediaTypeForContentType, trackTypeForMediaType, STAGGER_UNITS, parseStaggerUnit, typewriterTiming, parseEasing, DEFAULT_BEAT_TOLERANCE_MS, buildBeatGrid, beatCountToCover, snapClipsToGrid, staggerClipAnimations, isGroupClip, moveGroup, ungroup, trimGroup, resliceTracksForSplitClip, resliceTracksForTrimmedClip, applyTransitionAtCutCandidate, planTransitionAtCut, instantiateComposition, DEFAULT_MIDI_INSTRUMENT, DEFAULT_TEMPO, MIDI_INSTRUMENT_PRESETS, createMidiNote, findInstrumentPreset, formatBarsBeats, presetIdForInstrument, quantizeNotes, scaleVelocity, sortNotes, transposeNotes, validateNotes, visibleNotes, rescaleClipsForTempo, resolveTempo, captureMediaEditSourceContext, composeGenerativeTakePatch, createMediaEditRequest, ensureBaselineTake, applyTakeToClip, selectGeneratedMatteVersion, activeTakeIdOf, selectTake, renameTake as renameTakeOnClip, deleteTake as deleteTakeOnClip, adaptSequenceFormat, addReframeKeyframe, clearReframe, isMediaTrackStale, mediaTrackCanDriveReframe } from "@nodetool-ai/timeline";
29
+ import { computeActiveLayers, countTextStaggerUnits, parseSvgPath } from "@nodetool-ai/timeline/scene";
30
+ import { APPLY_TRANSITION_AT_CUT_DESCRIPTION, applyTransitionAtCutParams, buildEffect, buildMask, buildTimeRemap, buildTransition, resolveDeleteTrackArgs, resolveMoveTrackArgs, resolveShapeArg, setBakedAnimationParams, SET_BAKED_ANIMATION_DESCRIPTION, setGeneratedMatteParams, SET_GENERATED_MATTE_DESCRIPTION, textStyleParams, textStylePatchParams } from "@nodetool-ai/protocol/api-schemas/timeline-tool-params.js";
31
+ import { buildTimelineToolContracts, liftCustomAnimation, rejectUnknownClipParams } from "@nodetool-ai/protocol/api-schemas/timeline-tool-contracts.js";
32
+ import { uiToolParams } from "@nodetool-ai/protocol/api-schemas/ui-tool-contract.js";
33
+ import { findSystemSkill } from "../../system-skills.js";
34
+ /**
35
+ * The `ui_timeline_*` contracts, shared with the browser registry
36
+ * (`web/src/lib/tools/builtin/timeline.ts`). The vocabulary comes from
37
+ * `@nodetool-ai/timeline`, which the protocol package sits below.
38
+ */
39
+ const CONTRACTS = buildTimelineToolContracts({
40
+ staggerUnits: STAGGER_UNITS,
41
+ animatedProperties: ANIMATED_PROPERTIES,
42
+ beatToleranceMs: DEFAULT_BEAT_TOLERANCE_MS
43
+ });
44
+ const generatedTransitionCandidateParams = applyTransitionAtCutParams;
45
+ const transitionCandidateParams = z.object({
46
+ candidate_id: z.string().trim().min(1)
47
+ });
48
+ const transitionCandidateLifecycleParams = applyTransitionAtCutParams
49
+ .partial()
50
+ .extend({ candidate_id: z.string().trim().min(1).optional() })
51
+ .superRefine((value, context) => {
52
+ if (value.candidate_id === undefined &&
53
+ (value.outgoingClipId === undefined || value.incomingClipId === undefined)) {
54
+ context.addIssue({
55
+ code: "custom",
56
+ message: "Provide outgoingClipId and incomingClipId to create a candidate, or candidate_id to apply one."
57
+ });
58
+ }
59
+ });
60
+ /** Units a failed lookup names before it stops and points at get_state. */
61
+ const MAX_LISTED_UNITS = 12;
62
+ /**
63
+ * Tools that read the sequence without changing it. Everything else is an
64
+ * edit, which is what {@link previewedAfterLastEdit} measures "last" against.
65
+ */
66
+ export const TIMELINE_READ_ONLY_TOOLS = [
67
+ "ui_timeline_get_state",
68
+ "ui_timeline_list_animation_presets",
69
+ "ui_timeline_select_clip",
70
+ "ui_timeline_preview_transition_candidate",
71
+ "ui_timeline_seek",
72
+ "preview_timeline_frame"
73
+ ];
74
+ /** Whether the last edit in a transcript is followed by a preview call. */
75
+ export function previewedAfterLastEdit(toolLog) {
76
+ const lastEdit = toolLog.reduce((index, name, i) => (TIMELINE_READ_ONLY_TOOLS.includes(name) ? index : i), -1);
77
+ if (lastEdit === -1)
78
+ return false;
79
+ return toolLog.indexOf("preview_timeline_frame", lastEdit + 1) !== -1;
80
+ }
81
+ /**
82
+ * Whether a preview landed inside a motion rather than beside it.
83
+ *
84
+ * {@link previewedAfterLastEdit} reads the transcript's shape: a preview call
85
+ * came last. That passes on a look at 0ms, where an entrance has not started —
86
+ * the endpoints tell you nothing, which is what the skill's "sample the middle
87
+ * of a motion" is about. This reads the timecodes instead: at least one falls
88
+ * strictly inside the window of an animation the document now carries.
89
+ *
90
+ * Every animation in a graded final state is one the run authored, since the
91
+ * seeded worlds carry none.
92
+ */
93
+ export function previewedMidMotion(previewTimesMs, clips, canvas) {
94
+ if (previewTimesMs.length === 0)
95
+ return false;
96
+ return clips.some((clip) => (clip.animations ?? []).some((animation) => {
97
+ const window = animationWindow(clip, animation, canvas);
98
+ if (!(window.endMs > window.startMs))
99
+ return false;
100
+ return previewTimesMs.some((timeMs) => timeMs > window.startMs && timeMs < window.endMs);
101
+ }));
102
+ }
103
+ /**
104
+ * How long an animation is in motion for: its own window, widened by the
105
+ * stagger's last unit. `durationMs` unset takes the preset's default.
106
+ */
107
+ function motionSpanMs(clip, animation, canvas) {
108
+ const preset = ANIMATION_PRESETS.find((p) => p.id === animation.preset);
109
+ const durationMs = animation.durationMs ?? preset?.defaultDurationMs ?? 0;
110
+ const stagger = animation.stagger;
111
+ if (!stagger || !(stagger.offsetMs > 0))
112
+ return durationMs;
113
+ const units = staggerUnitsOf(clip, stagger.unit, canvas);
114
+ if (units < 2)
115
+ return durationMs;
116
+ return durationMs + stagger.offsetMs * (units - 1);
117
+ }
118
+ /**
119
+ * When an animation runs, in timeline ms. `delayMs` offsets an `in` and an
120
+ * `emphasis` from the clip's start and an `out` backwards from its end, which
121
+ * is the role rule the skill states. A `loop` runs for the whole clip.
122
+ */
123
+ export function animationWindow(clip, animation, canvas) {
124
+ const clipEndMs = clip.startMs + clip.durationMs;
125
+ if (animation.role === "loop") {
126
+ return { startMs: clip.startMs, endMs: clipEndMs };
127
+ }
128
+ const delayMs = animation.delayMs ?? 0;
129
+ const spanMs = motionSpanMs(clip, animation, canvas);
130
+ if (animation.role === "out") {
131
+ const endMs = clipEndMs - delayMs;
132
+ return { startMs: endMs - spanMs, endMs };
133
+ }
134
+ const startMs = clip.startMs + delayMs;
135
+ return { startMs, endMs: startMs + spanMs };
136
+ }
137
+ /**
138
+ * Whether a staggered animation finishes inside its clip: the last unit
139
+ * starts `offsetMs × (units − 1)` in and still runs the full `durationMs`.
140
+ * An animation with no stagger, or one on a clip that splits into fewer than
141
+ * two units, fits by construction — it is one block.
142
+ */
143
+ export function staggerSpanFitsClip(clip, animation, canvas) {
144
+ const stagger = animation.stagger;
145
+ if (!stagger || !(stagger.offsetMs > 0))
146
+ return true;
147
+ const units = staggerUnitsOf(clip, stagger.unit, canvas);
148
+ if (units < 2)
149
+ return true;
150
+ const preset = ANIMATION_PRESETS.find((p) => p.id === animation.preset);
151
+ const durationMs = animation.durationMs ?? preset?.defaultDurationMs ?? 0;
152
+ const span = (animation.delayMs ?? 0) + durationMs + stagger.offsetMs * (units - 1);
153
+ return span <= clip.durationMs;
154
+ }
155
+ /**
156
+ * How many units a clip's text splits into for a stagger unit. Line counting
157
+ * wraps against the sequence size; with no text measurer every authored
158
+ * paragraph is one line, which is what a headless surface can know.
159
+ */
160
+ export function staggerUnitsOf(clip, unit, canvas) {
161
+ const style = clip.textStyle;
162
+ const parsed = parseStaggerUnit(unit);
163
+ // An unknown unit compiles as a plain block animation, so it splits into
164
+ // nothing — same answer as a clip with no text.
165
+ if (!style || !parsed)
166
+ return 0;
167
+ return countTextStaggerUnits(style, canvas, parsed);
168
+ }
169
+ /**
170
+ * The easing an animation actually runs with: its own, else the preset's, else
171
+ * the role default (`in` decelerates, `out` accelerates).
172
+ */
173
+ export function effectiveEasing(animation) {
174
+ if (animation.easing)
175
+ return animation.easing;
176
+ const preset = ANIMATION_PRESETS.find((p) => p.id === animation.preset);
177
+ if (preset?.defaultEasing)
178
+ return preset.defaultEasing;
179
+ switch (animation.role) {
180
+ case "in":
181
+ return "easeOut";
182
+ case "out":
183
+ return "easeIn";
184
+ case "emphasis":
185
+ return "easeInOut";
186
+ default:
187
+ return "linear";
188
+ }
189
+ }
190
+ /** How far either side of the curve a slope is measured over. */
191
+ const EASING_SLOPE_STEP = 0.02;
192
+ /**
193
+ * Whether an easing decelerates into its landing.
194
+ *
195
+ * The `easeOut` family qualifies by name: its endpoints are exact, and
196
+ * `easeOutBounce` deliberately accelerates into its last bounce, which a slope
197
+ * reading at t=1 would score as an ease-in. Everything else in the grammar is
198
+ * measured — the curve's slope entering the landing against its slope leaving
199
+ * the start — so `cubic-bezier(0.16,1,0.3,1)`, the deceleration the skill
200
+ * recommends for entrances, passes and `cubic-bezier(0.7,0,0.84,0)`, the exit
201
+ * curve beside it, does not. An easing outside the grammar eases linearly and
202
+ * does not decelerate.
203
+ */
204
+ export function easingDecelerates(easing) {
205
+ const text = easing.trim();
206
+ if (/^easeOut/.test(text))
207
+ return true;
208
+ const curve = parseEasing(text);
209
+ if (!curve)
210
+ return false;
211
+ const entry = (curve(EASING_SLOPE_STEP) - curve(0)) / EASING_SLOPE_STEP;
212
+ const landing = (curve(1) - curve(1 - EASING_SLOPE_STEP)) / EASING_SLOPE_STEP;
213
+ return landing < entry;
214
+ }
215
+ function tool(name, description, parameters, impl) {
216
+ return {
217
+ name,
218
+ description,
219
+ parameters,
220
+ execute: (args) => {
221
+ const parsed = parseWithTypeCoercion(parameters, args ?? {});
222
+ return impl(parsed);
223
+ }
224
+ };
225
+ }
226
+ /**
227
+ * `set_clip_params` takes the style as a patch merged over the clip's own, so
228
+ * every field is optional. The shared contract still declares the whole-bag
229
+ * `textStyleParams`, which refuses `{textStyle: {fontFamily}}` on a finished
230
+ * title. Both hosts override that one field; when protocol carries
231
+ * `textStylePatchParams` the two overrides go together.
232
+ */
233
+ const CONTRACT_SHAPE_OVERRIDES = {
234
+ ui_timeline_set_clip_params: { textStyle: textStylePatchParams.optional() }
235
+ };
236
+ /** A tool whose name, description and argument shape both hosts share. */
237
+ function sharedTool(name, impl) {
238
+ const contract = CONTRACTS[name];
239
+ const shape = { ...contract.shape, ...CONTRACT_SHAPE_OVERRIDES[name] };
240
+ // No host fields: this bridge drives one implicit sequence, so there is no
241
+ // `timeline_id` to disambiguate.
242
+ return tool(name, contract.description, uiToolParams({ ...contract, shape }), impl);
243
+ }
244
+ /**
245
+ * Keys a caller wraps the whole patch in.
246
+ *
247
+ * `set_clip_params` reads its fields off the op itself — `{op, target,
248
+ * textStyle}` — but a REST-shaped guess sends `{op, target, params: {…}}`, and
249
+ * that used to be refused as an unknown key with the real fields hidden one
250
+ * level down inside it. The wrapper says nothing the op does not already know,
251
+ * so it is unwrapped rather than argued with.
252
+ */
253
+ const CLIP_PARAM_WRAPPERS = ["params", "patch", "props", "properties"];
254
+ /** Lift a patch a caller nested under a wrapper key onto the op itself. */
255
+ export function unwrapClipParams(patch) {
256
+ const wrapper = CLIP_PARAM_WRAPPERS.find((key) => isRecordValue(patch[key]));
257
+ if (!wrapper)
258
+ return patch;
259
+ const { [wrapper]: nested, ...rest } = patch;
260
+ // The op's own keys win: a caller that sent both meant the one it spelled
261
+ // out, and silently preferring the wrapper would drop it.
262
+ return { ...nested, ...rest };
263
+ }
264
+ const isRecordValue = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
265
+ function mergeClipTransform(current, patch) {
266
+ return {
267
+ position: { x: 0, y: 0, ...current?.position, ...patch.position },
268
+ scale: { x: 1, y: 1, ...current?.scale, ...patch.scale },
269
+ rotation: patch.rotation ?? current?.rotation ?? 0,
270
+ rotationX: patch.rotationX ?? current?.rotationX,
271
+ rotationY: patch.rotationY ?? current?.rotationY,
272
+ perspective: patch.perspective ?? current?.perspective,
273
+ anchor: { x: 0.5, y: 0.5, ...current?.anchor, ...patch.anchor }
274
+ };
275
+ }
276
+ /**
277
+ * A text style patch, merged over the clip's own and checked for a family that
278
+ * names no face.
279
+ *
280
+ * A clip that carries no text style yet still needs the three fields that make
281
+ * one drawable, so the merged result goes through the same schema the whole
282
+ * bag does rather than being stored half-built.
283
+ */
284
+ function mergeTextStyle(clip, patch) {
285
+ const merged = { ...(clip.textStyle ?? {}), ...patch };
286
+ const parsed = textStyleParams.safeParse(merged);
287
+ if (!parsed.success) {
288
+ const missing = parsed.error.issues
289
+ .filter((issue) => issue.code === "invalid_type")
290
+ .map((issue) => issue.path.join("."));
291
+ throw new Error(missing.length > 0
292
+ ? `Clip "${clip.name}" has no text style yet, so this patch cannot ` +
293
+ `stand on its own — it still needs ${missing.join(", ")}.`
294
+ : `textStyle: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
295
+ }
296
+ assertAuthorableFontFamily(parsed.data.fontFamily);
297
+ return parsed.data;
298
+ }
299
+ function capitalize(s) {
300
+ return s.length > 0 ? s[0].toUpperCase() + s.slice(1) : s;
301
+ }
302
+ /**
303
+ * Units a staggered animation splits into — a text clip's word count, which is
304
+ * what a custom body reads off `inputs.staggerCount`. Zero when the clip does
305
+ * not stagger, matching the un-staggered block case.
306
+ */
307
+ function staggerUnitCount(clip, stagger) {
308
+ if (!stagger || clip.mediaType !== "text")
309
+ return 0;
310
+ const text = clip.textStyle?.text ?? "";
311
+ const words = text.trim().split(/\s+/).filter((word) => word.length > 0);
312
+ return words.length;
313
+ }
314
+ /**
315
+ * Build an in-memory timeline bridge whose tools share the `ui_timeline_*`
316
+ * contract but run headlessly against a plain sequence of tracks/clips (no
317
+ * Zustand stores, no rendering).
318
+ */
319
+ export function createTimelineToolBridge(initial = {}) {
320
+ const seed = initial.sequence;
321
+ const resolveAsset = initial.resolveAsset;
322
+ const bakeAnimation = initial.bakeAnimation;
323
+ const bakeModel3DClip = initial.bakeModel3DClip;
324
+ const loadComposition = initial.loadComposition;
325
+ const retargetFormat = initial.retargetFormat;
326
+ const generateTransition = initial.generateTransition;
327
+ const sequenceId = initial.sequenceId ?? "seq_eval";
328
+ const sequenceName = initial.sequenceName ?? "Sequence";
329
+ const projectId = initial.projectId ?? "";
330
+ const fps = seed?.fps ?? initial.fps ?? 30;
331
+ const width = seed?.width ?? initial.width ?? 1920;
332
+ const height = seed?.height ?? initial.height ?? 1080;
333
+ let playheadMs = 0;
334
+ let selectedClipIds = [];
335
+ // Absent until a midi track or `set_tempo` puts one there — a document with
336
+ // no midi in it should not grow a tempo field it never asked for.
337
+ let tempo = seed?.tempo;
338
+ let trackSeq = 0;
339
+ let clipSeq = 0;
340
+ let animSeq = 0;
341
+ let markerSeq = 0;
342
+ let versionSeq = 0;
343
+ let transitionSeq = 0;
344
+ const tracks = [];
345
+ let clips = [];
346
+ let markers = [];
347
+ // Document-level, like markers: a track is named by `clipId`, and a clip
348
+ // follows one through its own `trackBinding` (P0 AI Video, Phase 2).
349
+ let mediaTracks = [];
350
+ let transitionCandidates = [];
351
+ let auditionedTransitionCandidateId;
352
+ let appliedTransitionCandidates = [];
353
+ const derivedSequences = [];
354
+ let setup = seed?.setup
355
+ ? structuredClone(seed.setup)
356
+ : null;
357
+ let beatSeq = 0;
358
+ const toolLog = [];
359
+ const previewTimesMs = [];
360
+ const previewedLayerKinds = new Set();
361
+ // Ids the sequence already uses. A seeded document brings its own, which the
362
+ // `track_1`/`clip_1` counters would otherwise collide with on the first edit.
363
+ const usedIds = new Set();
364
+ const mint = (prefix, next) => {
365
+ let id = `${prefix}_${next()}`;
366
+ while (usedIds.has(id))
367
+ id = `${prefix}_${next()}`;
368
+ usedIds.add(id);
369
+ return id;
370
+ };
371
+ const nextTrackId = () => mint("track", () => ++trackSeq);
372
+ const nextClipId = () => mint("clip", () => ++clipSeq);
373
+ const nextAnimId = () => mint("anim", () => ++animSeq);
374
+ const nextMarkerId = () => mint("marker", () => ++markerSeq);
375
+ const nextVersionId = () => mint("version", () => ++versionSeq);
376
+ const nextTransitionId = () => mint("transition", () => ++transitionSeq);
377
+ const clipEnd = (clip) => clip.startMs + clip.durationMs;
378
+ function isAmbiguousCut(outgoing, incoming) {
379
+ const cutTimeMs = incoming.startMs;
380
+ return clips.some((candidate) => {
381
+ if (candidate.id === outgoing.id ||
382
+ candidate.id === incoming.id ||
383
+ candidate.trackId !== outgoing.trackId) {
384
+ return false;
385
+ }
386
+ return ((candidate.startMs <= cutTimeMs && clipEnd(candidate) > cutTimeMs) ||
387
+ candidate.startMs === cutTimeMs);
388
+ });
389
+ }
390
+ function transitionSource(outgoing, incoming, durationMs) {
391
+ return {
392
+ sequenceId,
393
+ outgoingClipId: outgoing.id,
394
+ incomingClipId: incoming.id,
395
+ outgoingTakeId: activeTakeIdOf(outgoing),
396
+ incomingTakeId: activeTakeIdOf(incoming),
397
+ outgoingAssetId: outgoing.currentAssetId,
398
+ incomingAssetId: incoming.currentAssetId,
399
+ outgoingTransition: outgoing.transitionIn
400
+ ? structuredClone(outgoing.transitionIn)
401
+ : undefined,
402
+ incomingTransition: incoming.transitionIn
403
+ ? structuredClone(incoming.transitionIn)
404
+ : undefined,
405
+ outgoingStartMs: outgoing.startMs,
406
+ outgoingDurationMs: outgoing.durationMs,
407
+ incomingStartMs: incoming.startMs,
408
+ incomingDurationMs: incoming.durationMs,
409
+ cutTimeMs: incoming.startMs,
410
+ durationMs
411
+ };
412
+ }
413
+ function assertFreshTransitionCandidate(candidate) {
414
+ const outgoing = clips.find((clip) => clip.id === candidate.source.outgoingClipId);
415
+ const incoming = clips.find((clip) => clip.id === candidate.source.incomingClipId);
416
+ if (!outgoing || !incoming) {
417
+ throw new Error("The generated transition candidate is stale because its selected clip no longer exists.");
418
+ }
419
+ if (isAmbiguousCut(outgoing, incoming)) {
420
+ throw new Error("The generated transition candidate is stale because the selected cut is ambiguous.");
421
+ }
422
+ const source = transitionSource(outgoing, incoming, candidate.source.durationMs);
423
+ if (JSON.stringify(source) !== JSON.stringify(candidate.source)) {
424
+ throw new Error("The generated transition candidate is stale because the selected clips or cut changed.");
425
+ }
426
+ }
427
+ async function createTransitionCandidate(input) {
428
+ const outgoingClipId = input.outgoingClipId;
429
+ const incomingClipId = input.incomingClipId;
430
+ const outgoing = resolveClip(outgoingClipId);
431
+ const incoming = resolveClip(incomingClipId);
432
+ if (isAmbiguousCut(outgoing, incoming)) {
433
+ throw new Error("The selected cut is ambiguous. Choose two adjacent clips with no other clip crossing the cut.");
434
+ }
435
+ const planned = planTransitionAtCut(clips, {
436
+ outgoingClipId: outgoing.id,
437
+ incomingClipId: incoming.id,
438
+ durationMs: input.durationMs,
439
+ overlapMs: input.overlapMs,
440
+ type: input.type,
441
+ easing: input.easing,
442
+ color: input.color,
443
+ direction: input.direction,
444
+ softness: input.softness
445
+ });
446
+ if (!planned.ok)
447
+ throw new Error(planned.error);
448
+ if (!generateTransition) {
449
+ throw new Error("Selected-cut transition generation is unavailable because this surface has no provider generator.");
450
+ }
451
+ const source = transitionSource(outgoing, incoming, planned.candidate.durationMs);
452
+ const request = {
453
+ durationMs: planned.candidate.durationMs,
454
+ overlapMs: planned.candidate.overlapMs,
455
+ type: planned.candidate.type,
456
+ easing: input.easing,
457
+ color: input.color,
458
+ direction: input.direction,
459
+ softness: input.softness
460
+ };
461
+ const generated = await generateTransition({
462
+ sequenceId,
463
+ source,
464
+ request
465
+ });
466
+ if (generated.generationId.trim().length === 0 ||
467
+ generated.assetId.trim().length === 0) {
468
+ throw new Error("Selected-cut transition generation returned no generation id or asset.");
469
+ }
470
+ const id = nextTransitionId();
471
+ const candidate = {
472
+ id,
473
+ generationId: generated.generationId,
474
+ kind: "generated_transition_at_cut",
475
+ status: "success",
476
+ asset: {
477
+ id: generated.assetId,
478
+ contentType: generated.contentType ?? "video/mp4"
479
+ },
480
+ source,
481
+ request,
482
+ operation: {
483
+ op: "apply_generated_transition_at_cut",
484
+ candidateId: id,
485
+ outgoingClipId: outgoing.id,
486
+ incomingClipId: incoming.id,
487
+ cutTimeMs: incoming.startMs,
488
+ durationMs: planned.candidate.durationMs,
489
+ undoable: true
490
+ }
491
+ };
492
+ transitionCandidates = [...transitionCandidates, candidate];
493
+ return candidate;
494
+ }
495
+ function applyTransitionCandidate(candidateId) {
496
+ const candidate = transitionCandidates.find((entry) => entry.id === candidateId);
497
+ if (!candidate) {
498
+ throw new Error(`No generated transition candidate named "${candidateId}" exists.`);
499
+ }
500
+ assertFreshTransitionCandidate(candidate);
501
+ if (appliedTransitionCandidates.some((entry) => entry.id === candidate.id)) {
502
+ throw new Error(`Generated transition candidate "${candidate.id}" was already applied.`);
503
+ }
504
+ const planned = planTransitionAtCut(clips, {
505
+ outgoingClipId: candidate.source.outgoingClipId,
506
+ incomingClipId: candidate.source.incomingClipId,
507
+ ...candidate.request
508
+ });
509
+ if (!planned.ok)
510
+ throw new Error(planned.error);
511
+ const applied = applyTransitionAtCutCandidate(clips, planned.candidate);
512
+ if (!applied.ok)
513
+ throw new Error(applied.error);
514
+ clips = applied.clips;
515
+ appliedTransitionCandidates = [
516
+ ...appliedTransitionCandidates,
517
+ structuredClone(candidate)
518
+ ];
519
+ if (auditionedTransitionCandidateId === candidate.id) {
520
+ auditionedTransitionCandidateId = undefined;
521
+ }
522
+ return {
523
+ candidate,
524
+ operation: candidate.operation,
525
+ description: `Apply generated transition candidate "${candidate.id}" to the selected cut as one undoable operation.`
526
+ };
527
+ }
528
+ function addTrackInternal(type, name) {
529
+ const index = tracks.length;
530
+ const track = makeTrack({
531
+ id: nextTrackId(),
532
+ type,
533
+ name: name ?? `${capitalize(type)} ${index + 1}`,
534
+ index
535
+ });
536
+ if (type === "midi") {
537
+ // The track owns the voice, so a midi track without one plays nothing.
538
+ track.instrument = DEFAULT_MIDI_INSTRUMENT;
539
+ // Ticks are read against the tempo, so the first midi track is where a
540
+ // document that never had one gets it — written down rather than left
541
+ // implicit, so the next reader does not have to know the default.
542
+ tempo ??= DEFAULT_TEMPO;
543
+ }
544
+ tracks.push(track);
545
+ return track;
546
+ }
547
+ /** The midi track a clip or instrument op names, refusing any other type. */
548
+ function resolveMidiTrack(idOrName) {
549
+ const track = resolveTrack(idOrName);
550
+ if (track.type !== "midi") {
551
+ throw new Error(`Track "${track.name}" is a ${track.type} track — notes are played by a midi track's instrument. ${validUnits(tracks.filter((t) => t.type === "midi"), "midi track")}`);
552
+ }
553
+ return track;
554
+ }
555
+ /** The midi clip a note op names, refusing any other kind. */
556
+ function resolveMidiClip(target) {
557
+ const clip = resolveClip(target);
558
+ if (clip.mediaType !== "midi") {
559
+ throw new Error(`Clip "${clip.name}" is a ${clip.mediaType} clip — only a midi clip carries notes. Place one with add_midi_clip.`);
560
+ }
561
+ return clip;
562
+ }
563
+ /**
564
+ * The voice an instrument argument names: `{preset}` resolved to the sound
565
+ * it stands for, or a spelled-out instrument taken as it came.
566
+ *
567
+ * The preset's object is copied onto the track rather than referenced, so
568
+ * the document keeps the sound it was given even if the shipped table
569
+ * changes under it.
570
+ */
571
+ function resolveInstrumentArg(raw) {
572
+ const value = (raw ?? {});
573
+ const presetId = value["preset"];
574
+ if (typeof presetId !== "string") {
575
+ return value;
576
+ }
577
+ const preset = findInstrumentPreset(presetId);
578
+ if (!preset) {
579
+ throw new Error(`No instrument preset named "${presetId}". Valid presets: ${MIDI_INSTRUMENT_PRESETS.map((p) => `${p.id} ("${p.name}")`).join(", ")}.`);
580
+ }
581
+ return structuredClone(preset.instrument);
582
+ }
583
+ /**
584
+ * The notes a caller sent, minted and checked as one list.
585
+ *
586
+ * Every problem is reported at once: an agent that fixes one note only to be
587
+ * told about the next spends a call per note.
588
+ */
589
+ function buildNotes(raw, clipName) {
590
+ const notes = (raw ?? []).map((note) => createMidiNote({
591
+ id: note["id"],
592
+ pitch: note["pitch"],
593
+ velocity: note["velocity"],
594
+ startTick: note["start_tick"],
595
+ durationTick: note["duration_tick"]
596
+ }));
597
+ const problems = validateNotes(notes);
598
+ if (problems.length > 0) {
599
+ throw new Error(`Clip "${clipName}" was not given its notes — ${problems.length} problem(s): ` +
600
+ problems
601
+ .map((p) => (p.noteId ? `${p.noteId}: ${p.message}` : p.message))
602
+ .join(" "));
603
+ }
604
+ return sortNotes(notes);
605
+ }
606
+ function findOrCreateTrack(type) {
607
+ const existing = tracks.find((t) => t.type === type);
608
+ if (existing)
609
+ return existing;
610
+ return addTrackInternal(type);
611
+ }
612
+ /**
613
+ * The ids and names a caller could have used, for the message a failed
614
+ * lookup throws. Capped, because a long cut has hundreds of clips and an
615
+ * error listing all of them is one an agent stops reading.
616
+ */
617
+ function validUnits(units, kind) {
618
+ if (units.length === 0)
619
+ return `The timeline has no ${kind}s yet.`;
620
+ const shown = units
621
+ .slice(0, MAX_LISTED_UNITS)
622
+ .map((u) => `${u.id} ("${u.name}")`)
623
+ .join(", ");
624
+ const rest = units.length - MAX_LISTED_UNITS;
625
+ return rest > 0
626
+ ? `Valid ${kind}s: ${shown}, and ${rest} more — call get_state for the full list.`
627
+ : `Valid ${kind}s: ${shown}.`;
628
+ }
629
+ function resolveTrack(idOrName) {
630
+ const byId = tracks.find((t) => t.id === idOrName);
631
+ if (byId)
632
+ return byId;
633
+ if (isShortResourceId(idOrName)) {
634
+ const matches = tracks.filter((track) => track.id.startsWith(idOrName));
635
+ if (matches.length === 1) {
636
+ return matches[0];
637
+ }
638
+ if (matches.length > 1) {
639
+ throw new Error(`Short track id "${idOrName}" matches more than one track. Use the full id or name.`);
640
+ }
641
+ }
642
+ const lower = idOrName.toLowerCase();
643
+ const byName = tracks.find((t) => t.name.toLowerCase() === lower);
644
+ if (byName)
645
+ return byName;
646
+ throw new Error(`No track found matching "${idOrName}". ${validUnits(tracks, "track")}`);
647
+ }
648
+ function trackEndMs(trackId) {
649
+ return clips
650
+ .filter((c) => c.trackId === trackId)
651
+ .reduce((m, c) => Math.max(m, c.startMs + c.durationMs), 0);
652
+ }
653
+ function resolveClip(target) {
654
+ if (target.toLowerCase() === "selected") {
655
+ if (selectedClipIds.length !== 1) {
656
+ throw new Error(`"selected" requires exactly one selected clip (currently ${selectedClipIds.length}).`);
657
+ }
658
+ const clip = clips.find((c) => c.id === selectedClipIds[0]);
659
+ if (!clip)
660
+ throw new Error("Selected clip no longer exists.");
661
+ return clip;
662
+ }
663
+ const byId = clips.find((c) => c.id === target);
664
+ if (byId)
665
+ return byId;
666
+ if (isShortResourceId(target)) {
667
+ const matches = clips.filter((clip) => clip.id.startsWith(target));
668
+ if (matches.length === 1) {
669
+ return matches[0];
670
+ }
671
+ if (matches.length > 1) {
672
+ throw new Error(`Short clip id "${target}" matches more than one clip. Use the full id or name.`);
673
+ }
674
+ }
675
+ const lower = target.toLowerCase();
676
+ const byName = clips.find((c) => c.name.toLowerCase() === lower);
677
+ if (byName)
678
+ return byName;
679
+ throw new Error(`No clip found matching "${target}". ${validUnits(clips, "clip")}`);
680
+ }
681
+ /**
682
+ * The body of `trim_clip`, shared with `set_clip_params`: a caller that sends
683
+ * `durationMs` alongside a style change means the same edit either way, and
684
+ * two copies of the group-trim rule would drift.
685
+ */
686
+ function applyTrim(clip, patch) {
687
+ // A group carries what it holds (D4): shortening one pulls its children
688
+ // inside the window that leaves, rather than leaving them hanging past an
689
+ // edge nothing draws.
690
+ if (isGroupClip(clip) && patch.durationMs !== undefined) {
691
+ clips = trimGroup(clips, clip.id, "end", patch.durationMs - clip.durationMs);
692
+ return clips.find((c) => c.id === clip.id);
693
+ }
694
+ let next = clip;
695
+ if (patch.durationMs !== undefined) {
696
+ if (patch.durationMs <= 0) {
697
+ throw new Error(`durationMs must be greater than 0 (got ${patch.durationMs}); delete the clip instead of trimming it to nothing`);
698
+ }
699
+ next = trimClip(clip, "end", patch.durationMs - clip.durationMs);
700
+ clips = clips.map((entry) => entry.id === clip.id ? next : entry);
701
+ }
702
+ if (patch.inPointMs !== undefined)
703
+ next.inPointMs = patch.inPointMs;
704
+ if (patch.outPointMs !== undefined)
705
+ next.outPointMs = patch.outPointMs;
706
+ return next;
707
+ }
708
+ function applyTrimStart(clip, startMs) {
709
+ const deltaMs = clip.startMs - Math.max(0, startMs);
710
+ if (deltaMs === 0)
711
+ return clip;
712
+ if (isGroupClip(clip)) {
713
+ clips = trimGroup(clips, clip.id, "start", deltaMs);
714
+ return clips.find((entry) => entry.id === clip.id);
715
+ }
716
+ const next = trimClip(clip, "start", deltaMs);
717
+ clips = clips.map((entry) => entry.id === clip.id ? next : entry);
718
+ return next;
719
+ }
720
+ /** The body of `move_clip`, shared with `set_clip_params`. */
721
+ function applyMove(clip, patch) {
722
+ // Moving a group moves what it holds by the same delta (D4). Children keep
723
+ // their own tracks, so their z-order is untouched (I9) — only the group
724
+ // itself takes a new `trackId`.
725
+ let moved = clip;
726
+ if (isGroupClip(clip) && patch.startMs !== undefined) {
727
+ const nextStartMs = Math.max(0, patch.startMs);
728
+ clips = moveGroup(clips, clip.id, nextStartMs - clip.startMs);
729
+ moved = clips.find((c) => c.id === clip.id);
730
+ }
731
+ else if (patch.startMs !== undefined) {
732
+ clip.startMs = Math.max(0, patch.startMs);
733
+ }
734
+ if (patch.trackId !== undefined) {
735
+ moved.trackId = resolveTrack(patch.trackId).id;
736
+ }
737
+ return moved;
738
+ }
739
+ /** Resolve a marker by id, or by case-insensitive label. */
740
+ function resolveMarker(target) {
741
+ const byId = markers.find((m) => m.id === target);
742
+ if (byId)
743
+ return byId;
744
+ const lower = target.toLowerCase();
745
+ const byLabel = markers.find((m) => m.label.toLowerCase() === lower);
746
+ if (byLabel)
747
+ return byLabel;
748
+ const known = markers
749
+ .map((m) => `${m.id} ("${m.label}") at ${m.timeMs}ms`)
750
+ .join(", ");
751
+ throw new Error(`No marker matches "${target}". Use a marker id or its label. ` +
752
+ (known.length > 0
753
+ ? `Markers: ${known}.`
754
+ : "This sequence has no markers yet."));
755
+ }
756
+ /**
757
+ * Resolve the clips one beat op addresses: the named ones, or every clip.
758
+ *
759
+ * A target that matches nothing comes back as a miss rather than throwing —
760
+ * a batch op that dies on one bad name hides what the other targets did.
761
+ */
762
+ function resolveSnapTargets(targets) {
763
+ if (!targets || targets.length === 0) {
764
+ return { clips: [...clips], missing: [] };
765
+ }
766
+ const resolved = [];
767
+ const missing = [];
768
+ for (const target of targets) {
769
+ try {
770
+ const clip = resolveClip(target);
771
+ if (!resolved.includes(clip))
772
+ resolved.push(clip);
773
+ }
774
+ catch {
775
+ // Recorded as a skip in the op's own report, with the reason.
776
+ missing.push(target);
777
+ }
778
+ }
779
+ return { clips: resolved, missing };
780
+ }
781
+ function serializeTrack(t) {
782
+ return {
783
+ id: t.id,
784
+ name: t.name,
785
+ type: t.type,
786
+ index: t.index,
787
+ visible: t.visible,
788
+ locked: t.locked,
789
+ muted: t.muted ?? false,
790
+ solo: t.solo ?? false,
791
+ clipCount: clips.filter((c) => c.trackId === t.id).length,
792
+ // The voice every clip on this track plays. Absent on every other type.
793
+ instrument: t.instrument,
794
+ // Which shipped voice that is, when it is one. A track stores the
795
+ // instrument rather than the id it was picked from, so this is read
796
+ // back by sound — an instrument edited away from a preset reports none.
797
+ presetId: t.instrument ? presetIdForInstrument(t.instrument) : undefined
798
+ };
799
+ }
800
+ /**
801
+ * Build one `preset: "custom"` animation. `curves` are checked and stored;
802
+ * `code` is baked into curves first, by the host that supplied a baker. Both
803
+ * paths end at `normalizeCustomCurves`, the single gate the compiler and the
804
+ * validator also run, so what is stored is what will render.
805
+ */
806
+ async function buildCustomAnimation(clip, input) {
807
+ const code = typeof input.code === "string" ? input.code.trim() : "";
808
+ const hasCurves = input.curves !== undefined;
809
+ const hasCode = code !== "";
810
+ if (hasCurves && hasCode) {
811
+ throw new Error('A "custom" animation takes exactly one of `curves` and `code`; both were given. Pass the keyframes, or the body that produces them.');
812
+ }
813
+ if (!hasCurves && !hasCode) {
814
+ throw new Error('A "custom" animation needs `curves` or `code`. Accepted shape: ' +
815
+ '{role: "in", preset: "custom", curves: [{property: "offsetY", keyframes: [{t: 0, value: 160}, {t: 1, value: 0}]}]}' +
816
+ ' — `t` runs 0..1 over the window. `code` is a JS body baked into curves instead. Either may also be nested under `custom`.');
817
+ }
818
+ // Curves are normalized to 0..1 over the window, so a custom animation
819
+ // with no duration of its own spans the clip and nothing is cropped.
820
+ const durationMs = input.durationMs ?? clip.durationMs;
821
+ let curves;
822
+ let maskInput;
823
+ if (hasCurves) {
824
+ const normalized = normalizeCustomCurves(input.curves);
825
+ if (!normalized.ok)
826
+ throw new Error(normalized.error);
827
+ curves = normalized.curves;
828
+ maskInput = input.mask;
829
+ }
830
+ else {
831
+ if (!bakeAnimation) {
832
+ throw new Error('This surface cannot run `code`: no animation baker is wired to it. Pass `curves` instead, or bake the body through POST /api/timelines/animations/bake.');
833
+ }
834
+ const baked = await bakeAnimation({
835
+ code,
836
+ role: input.role,
837
+ durationMs,
838
+ clipDurationMs: clip.durationMs,
839
+ canvas: { width, height },
840
+ params: input.params,
841
+ staggerCount: staggerUnitCount(clip, input.stagger)
842
+ });
843
+ if (!baked.ok || !baked.curves) {
844
+ throw new Error(baked.error ?? "The animation body returned no curves.");
845
+ }
846
+ const normalized = normalizeCustomCurves(baked.curves);
847
+ if (!normalized.ok)
848
+ throw new Error(normalized.error);
849
+ curves = normalized.curves;
850
+ maskInput = baked.mask ?? input.mask;
851
+ }
852
+ const mask = resolveCustomMask(curves, maskInput);
853
+ if (!mask.ok)
854
+ throw new Error(mask.error);
855
+ const custom = {
856
+ curves,
857
+ bakedAt: new Date().toISOString()
858
+ };
859
+ if (hasCode)
860
+ custom.code = code;
861
+ if (mask.mask)
862
+ custom.mask = mask.mask;
863
+ return {
864
+ id: nextAnimId(),
865
+ role: input.role,
866
+ preset: CUSTOM_ANIMATION_PRESET_ID,
867
+ durationMs,
868
+ delayMs: input.delayMs,
869
+ easing: input.easing,
870
+ params: input.params,
871
+ stagger: input.stagger,
872
+ custom
873
+ };
874
+ }
875
+ function serializeClip(c) {
876
+ const track = tracks.find((t) => t.id === c.trackId);
877
+ return {
878
+ id: c.id,
879
+ name: c.name,
880
+ trackId: c.trackId,
881
+ trackName: track?.name ?? null,
882
+ mediaType: c.mediaType,
883
+ sourceType: c.sourceType,
884
+ startMs: c.startMs,
885
+ durationMs: c.durationMs,
886
+ endMs: c.startMs + c.durationMs,
887
+ inPointMs: c.inPointMs,
888
+ outPointMs: c.outPointMs,
889
+ status: c.status,
890
+ currentAssetId: c.currentAssetId,
891
+ activeTakeId: activeTakeIdOf(c),
892
+ takeCount: c.versions?.length ?? 0,
893
+ prompt: c.prompt,
894
+ provider: c.provider,
895
+ model: c.model,
896
+ voice: c.voice,
897
+ animations: (c.animations ?? []).map((a) => ({
898
+ role: a.role,
899
+ preset: a.preset
900
+ })),
901
+ hidden: c.hidden ?? false,
902
+ muted: c.muted ?? false,
903
+ locked: c.locked,
904
+ opacity: c.opacity,
905
+ transform: c.transform,
906
+ textStyle: c.textStyle,
907
+ shapeStyle: c.shapeStyle,
908
+ captionStyle: c.caption?.style,
909
+ transitionIn: c.transitionIn,
910
+ mask: c.mask,
911
+ matte: c.matte,
912
+ timeRemap: c.timeRemap,
913
+ effects: c.effects,
914
+ parentId: c.parentId,
915
+ reframe: c.reframe
916
+ ? {
917
+ mode: c.reframe.mode,
918
+ trackId: c.reframe.trackId,
919
+ safeMargin: c.reframe.safeMargin,
920
+ smoothing: c.reframe.smoothing,
921
+ sampleCount: c.reframe.samples?.length ?? 0,
922
+ keyframeCount: c.reframe.keyframes?.length ?? 0
923
+ }
924
+ : undefined,
925
+ // The notes themselves are the clip's bulk — a phrase is hundreds of
926
+ // them — so state reports how many there are and how many the window
927
+ // actually plays. `set_notes` sends the list; nothing reads it back.
928
+ noteCount: c.notes?.length,
929
+ audibleNoteCount: c.mediaType === "midi"
930
+ ? visibleNotes(c, resolveTempo({ tempo }).bpm).length
931
+ : undefined,
932
+ // Where the phrase starts in musical time, so placing the next one is
933
+ // reading a bar number rather than dividing milliseconds by the tempo.
934
+ startBarsBeats: c.mediaType === "midi"
935
+ ? formatBarsBeats(c.startMs, resolveTempo({ tempo }))
936
+ : undefined
937
+ };
938
+ }
939
+ // Seed from a real sequence when one was handed over, otherwise from the
940
+ // shorthand. Cloned, so the bridge never writes through to the caller's state.
941
+ if (seed) {
942
+ for (const track of seed.tracks) {
943
+ const copy = structuredClone(track);
944
+ usedIds.add(copy.id);
945
+ tracks.push(copy);
946
+ }
947
+ for (const clip of seed.clips) {
948
+ const copy = structuredClone(clip);
949
+ usedIds.add(copy.id);
950
+ for (const animation of copy.animations ?? [])
951
+ usedIds.add(animation.id);
952
+ clips.push(copy);
953
+ }
954
+ for (const marker of seed.markers ?? []) {
955
+ const copy = structuredClone(marker);
956
+ usedIds.add(copy.id);
957
+ markers.push(copy);
958
+ }
959
+ for (const mediaTrack of seed.mediaTracks ?? []) {
960
+ const copy = structuredClone(mediaTrack);
961
+ usedIds.add(copy.id);
962
+ mediaTracks.push(copy);
963
+ }
964
+ }
965
+ // Seed initial tracks and clips.
966
+ for (const t of seed ? [] : (initial.tracks ?? [])) {
967
+ addTrackInternal(t.type, t.name);
968
+ }
969
+ for (const c of seed ? [] : (initial.clips ?? [])) {
970
+ const track = tracks[c.trackIndex];
971
+ if (!track) {
972
+ throw new Error(`Initial clip "${c.name}" references trackIndex ${c.trackIndex}, but only ${tracks.length} track(s) exist.`);
973
+ }
974
+ clips.push(makeClip({
975
+ id: nextClipId(),
976
+ trackId: track.id,
977
+ name: c.name,
978
+ startMs: c.startMs,
979
+ durationMs: c.durationMs,
980
+ mediaType: c.mediaType ?? "video",
981
+ sourceType: "imported",
982
+ status: "generated"
983
+ }));
984
+ }
985
+ /** Resolve a beat by id, or by its 1-based position in the plan. */
986
+ function resolveBeat(target) {
987
+ const beats = setup?.beats ?? [];
988
+ const byId = beats.find((beat) => beat.id === target);
989
+ if (byId)
990
+ return byId;
991
+ const position = Number.parseInt(target, 10);
992
+ const byPosition = beats[position - 1];
993
+ if (Number.isFinite(position) && byPosition)
994
+ return byPosition;
995
+ throw new Error(`No beat matches "${target}". Use a beat id or its 1-based position. ` +
996
+ (beats.length > 0
997
+ ? `This plan has ${beats.length} beats.`
998
+ : "This sequence has no beat plan yet; run ui_timeline_plan_beats first."));
999
+ }
1000
+ const tools = [
1001
+ sharedTool("ui_timeline_get_state", async () => {
1002
+ const durationMs = clips.reduce((m, c) => Math.max(m, c.startMs + c.durationMs), 0);
1003
+ return {
1004
+ ok: true,
1005
+ sequenceId,
1006
+ fps,
1007
+ width,
1008
+ height,
1009
+ durationMs,
1010
+ playheadMs,
1011
+ selectedClipIds: [...selectedClipIds],
1012
+ // Absent when the document carries none: a reader that needs a
1013
+ // number reads DEFAULT_TEMPO, and saying so beats printing a tempo
1014
+ // the document does not store.
1015
+ tempo,
1016
+ tracks: tracks.map(serializeTrack),
1017
+ clips: clips.map(serializeClip),
1018
+ mediaTracks: mediaTracks.map((track) => ({
1019
+ id: track.id,
1020
+ clipId: track.clipId,
1021
+ sourceAssetId: track.sourceAssetId,
1022
+ name: track.name,
1023
+ kind: track.kind,
1024
+ sourceStartMs: track.sourceStartMs,
1025
+ sourceEndMs: track.sourceEndMs,
1026
+ sampleCount: track.samples.length,
1027
+ confidence: track.confidence,
1028
+ status: track.status,
1029
+ provenance: track.provenance
1030
+ })),
1031
+ markers: markers.map((m) => ({ ...m }))
1032
+ };
1033
+ }),
1034
+ sharedTool("ui_timeline_add_track", async ({ type, name }) => {
1035
+ const track = addTrackInternal(type, name);
1036
+ return { ok: true, track: serializeTrack(track) };
1037
+ }),
1038
+ sharedTool("ui_timeline_move_track", async (args) => {
1039
+ const { target, toIndex, before, after } = resolveMoveTrackArgs(args);
1040
+ const track = resolveTrack(target);
1041
+ const destination = {};
1042
+ if (toIndex !== undefined)
1043
+ destination.toIndex = toIndex;
1044
+ if (before !== undefined) {
1045
+ destination.beforeId = resolveTrack(before).id;
1046
+ }
1047
+ if (after !== undefined) {
1048
+ destination.afterId = resolveTrack(after).id;
1049
+ }
1050
+ const orderedIds = moveTrackOrder(tracks, track.id, destination);
1051
+ const byId = new Map(tracks.map((t) => [t.id, t]));
1052
+ // The array order is what `get_state` prints, so keep it and the
1053
+ // indices saying the same thing.
1054
+ tracks.length = 0;
1055
+ orderedIds.forEach((id, i) => {
1056
+ const moved = byId.get(id);
1057
+ moved.index = i;
1058
+ tracks.push(moved);
1059
+ });
1060
+ return {
1061
+ ok: true,
1062
+ track: serializeTrack(track),
1063
+ tracks: tracks.map(serializeTrack)
1064
+ };
1065
+ }),
1066
+ sharedTool("ui_timeline_add_text_clip", async ({ text, name, transform, trackId, startMs, durationMs, opacity, style, ...loose }) => {
1067
+ const track = trackId
1068
+ ? resolveTrack(trackId)
1069
+ : findOrCreateTrack("overlay");
1070
+ // `style` wins over a top-level twin: a caller that sent both meant the
1071
+ // bag it named.
1072
+ const s = {
1073
+ ...loose,
1074
+ ...(style ??
1075
+ {})
1076
+ };
1077
+ const clip = makeClip({
1078
+ id: nextClipId(),
1079
+ trackId: track.id,
1080
+ name: name ?? text,
1081
+ startMs: startMs ?? trackEndMs(track.id),
1082
+ durationMs: durationMs ?? DEFAULT_TEXT_CLIP_DURATION_MS,
1083
+ mediaType: "text",
1084
+ sourceType: "imported",
1085
+ status: "generated",
1086
+ textStyle: textStyleWithDefaults(text, s)
1087
+ });
1088
+ if (opacity !== undefined)
1089
+ clip.opacity = opacity;
1090
+ if (transform)
1091
+ clip.transform = mergeClipTransform(clip.transform, transform);
1092
+ clips.push(clip);
1093
+ selectedClipIds = [clip.id];
1094
+ return { ok: true, clip: serializeClip(clip) };
1095
+ }),
1096
+ sharedTool("ui_timeline_delete_track", async (args) => {
1097
+ const { target, deleteClips } = resolveDeleteTrackArgs(args);
1098
+ const track = resolveTrack(target);
1099
+ const onIt = clips.filter((c) => c.trackId === track.id);
1100
+ if (onIt.length > 0 && !deleteClips) {
1101
+ throw new Error(`Track "${track.name}" still holds ${onIt.length} clip(s): ` +
1102
+ `${onIt.map((c) => c.id).join(", ")}. Move them first, or pass ` +
1103
+ "deleteClips: true to delete them with the track.");
1104
+ }
1105
+ const removedClipIds = onIt.map((c) => c.id);
1106
+ const kept = clips.filter((c) => c.trackId !== track.id);
1107
+ clips.length = 0;
1108
+ clips.push(...kept);
1109
+ // A parent that went with the track would leave its children pointing
1110
+ // at a clip that no longer exists, which the validator reads as a
1111
+ // broken document rather than a deletion.
1112
+ for (const clip of clips) {
1113
+ if (clip.parentId && removedClipIds.includes(clip.parentId)) {
1114
+ delete clip.parentId;
1115
+ }
1116
+ }
1117
+ selectedClipIds = selectedClipIds.filter((id) => !removedClipIds.includes(id));
1118
+ const remaining = tracks.filter((t) => t.id !== track.id);
1119
+ tracks.length = 0;
1120
+ // Index is z-order, so the stack has to close over the gap.
1121
+ remaining.forEach((t, i) => {
1122
+ t.index = i;
1123
+ tracks.push(t);
1124
+ });
1125
+ return {
1126
+ ok: true,
1127
+ deleted: { id: track.id, name: track.name, type: track.type },
1128
+ deletedClipIds: removedClipIds,
1129
+ tracks: tracks.map(serializeTrack)
1130
+ };
1131
+ }),
1132
+ sharedTool("ui_timeline_add_media_clip", async ({ asset, trackId, startMs, durationMs, name, transform }) => {
1133
+ if (!resolveAsset) {
1134
+ throw new Error("This timeline surface cannot look up assets, so an existing asset cannot be placed here.");
1135
+ }
1136
+ const ref = asset;
1137
+ const found = await resolveAsset(ref);
1138
+ if (!found) {
1139
+ throw new Error(`No asset found for "${ref}". Pass an asset id or an asset:// URI from list_assets.`);
1140
+ }
1141
+ const mediaType = mediaTypeForContentType(found.contentType);
1142
+ if (!mediaType) {
1143
+ throw new Error(`Asset "${found.name}" is ${found.contentType}, which is not video, image, or audio and cannot go on a timeline.`);
1144
+ }
1145
+ if (mediaType !== "image" && durationMs === undefined && !found.durationMs) {
1146
+ throw new Error(`Asset "${found.name}" has no known duration. Supply durationMs or import media that can be probed.`);
1147
+ }
1148
+ const track = trackId
1149
+ ? resolveTrack(trackId)
1150
+ : findOrCreateTrack(trackTypeForMediaType(mediaType));
1151
+ const init = {
1152
+ id: nextClipId(),
1153
+ trackId: track.id,
1154
+ name: name ?? found.name,
1155
+ startMs: startMs ?? trackEndMs(track.id),
1156
+ durationMs: durationMs ??
1157
+ found.durationMs ??
1158
+ DEFAULT_MEDIA_CLIP_DURATION_MS,
1159
+ mediaType,
1160
+ sourceType: "imported",
1161
+ status: "generated",
1162
+ currentAssetId: found.id
1163
+ };
1164
+ if (found.thumbnailAssetId) {
1165
+ init.thumbnailAssetId = found.thumbnailAssetId;
1166
+ }
1167
+ const clip = makeClip(init);
1168
+ if (transform)
1169
+ clip.transform = mergeClipTransform(clip.transform, transform);
1170
+ clips.push(clip);
1171
+ selectedClipIds = [clip.id];
1172
+ return { ok: true, clip: serializeClip(clip) };
1173
+ }),
1174
+ sharedTool("ui_timeline_add_shape_clip", async ({ shape, shapeStyle, name, transform, trackId, startMs, durationMs, opacity, ...loose }) => {
1175
+ const track = trackId
1176
+ ? resolveTrack(trackId)
1177
+ : findOrCreateTrack("overlay");
1178
+ const shapeArg = resolveShapeArg(shape, shapeStyle, loose);
1179
+ const clip = makeClip({
1180
+ id: nextClipId(),
1181
+ trackId: track.id,
1182
+ name: name ?? capitalize(shapeArg.kind),
1183
+ startMs: startMs ?? trackEndMs(track.id),
1184
+ durationMs: durationMs ?? DEFAULT_TEXT_CLIP_DURATION_MS,
1185
+ mediaType: "shape",
1186
+ sourceType: "imported",
1187
+ status: "generated",
1188
+ shapeStyle: shapeStyleWithDefaults(shapeArg)
1189
+ });
1190
+ if (opacity !== undefined)
1191
+ clip.opacity = opacity;
1192
+ if (transform)
1193
+ clip.transform = mergeClipTransform(clip.transform, transform);
1194
+ clips.push(clip);
1195
+ selectedClipIds = [clip.id];
1196
+ return { ok: true, clip: serializeClip(clip) };
1197
+ }),
1198
+ sharedTool("ui_timeline_add_model3d_clip", async ({ assetId, trackId, startMs, durationMs, style, transform }) => {
1199
+ // 3D is picture (D1), so it lands where a title lands.
1200
+ const track = trackId
1201
+ ? resolveTrack(trackId)
1202
+ : findOrCreateTrack("overlay");
1203
+ const clip = makeClip({
1204
+ id: nextClipId(),
1205
+ trackId: track.id,
1206
+ name: DEFAULT_MODEL3D_CLIP_NAME,
1207
+ startMs: startMs ?? trackEndMs(track.id),
1208
+ durationMs: durationMs ??
1209
+ DEFAULT_MODEL3D_CLIP_DURATION_MS,
1210
+ mediaType: "model3d",
1211
+ sourceType: "imported",
1212
+ status: "generated",
1213
+ currentAssetId: assetId.trim(),
1214
+ model3dStyle: model3dStyleWithPatch(undefined, style)
1215
+ });
1216
+ if (transform)
1217
+ clip.transform = mergeClipTransform(clip.transform, transform);
1218
+ clips.push(clip);
1219
+ selectedClipIds = [clip.id];
1220
+ return { ok: true, clip: serializeClip(clip) };
1221
+ }),
1222
+ sharedTool("ui_timeline_set_model3d_style", async ({ target, patch }) => {
1223
+ const clip = resolveClip(target);
1224
+ if (clip.mediaType !== "model3d") {
1225
+ throw new Error(`Clip "${clip.name}" is a ${clip.mediaType} clip, not a 3D clip — ` +
1226
+ "model3dStyle names a camera, an animation and lighting for a " +
1227
+ "glTF, and nothing else reads it.");
1228
+ }
1229
+ clip.model3dStyle = model3dStyleWithPatch(clip.model3dStyle, patch);
1230
+ return { ok: true, clip: serializeClip(clip) };
1231
+ }),
1232
+ sharedTool("ui_timeline_bake_model3d_clip", async ({ target }) => {
1233
+ const clip = resolveClip(target);
1234
+ if (clip.mediaType !== "model3d") {
1235
+ throw new Error(`Clip "${clip.name}" is a ${clip.mediaType} clip, not a 3D clip — ` +
1236
+ "only a 3D clip has a camera, lighting and a glTF to render.");
1237
+ }
1238
+ const style = clip.model3dStyle;
1239
+ if (!style) {
1240
+ throw new Error(`Clip "${clip.name}" has no model3dStyle, so nothing names the ` +
1241
+ "camera, lighting or animation a bake would render.");
1242
+ }
1243
+ if (!clip.currentAssetId) {
1244
+ throw new Error(`Clip "${clip.name}" has no glTF asset to bake — a 3D clip draws ` +
1245
+ "its asset the way an image clip draws its image.");
1246
+ }
1247
+ const sequence = { fps, width, height };
1248
+ const dependencyHash = computeModel3DBakeHash(clip, sequence);
1249
+ if (!bakeModel3DClip) {
1250
+ return {
1251
+ ok: true,
1252
+ clip: serializeClip(clip),
1253
+ bakeStarted: false,
1254
+ note: "This surface has no Blender renderer, so nothing was baked."
1255
+ };
1256
+ }
1257
+ const baked = await bakeModel3DClip({ clip, sequence, dependencyHash });
1258
+ clip.model3dStyle = {
1259
+ ...style,
1260
+ bake: { assetId: baked.assetId, dependencyHash }
1261
+ };
1262
+ const now = new Date().toISOString();
1263
+ clip.versions = [
1264
+ ...(clip.versions ?? []),
1265
+ makeClipVersion({
1266
+ id: nextVersionId(),
1267
+ createdAt: now,
1268
+ workflowUpdatedAt: now,
1269
+ jobId: baked.jobId ?? "",
1270
+ assetId: baked.assetId,
1271
+ dependencyHash
1272
+ })
1273
+ ];
1274
+ return { ok: true, clip: serializeClip(clip), bakeStarted: true };
1275
+ }),
1276
+ sharedTool("ui_timeline_generate_clip", async ({ kind, prompt, trackId, startMs, durationMs, provider, model, voice, width: clipWidth, height: clipHeight, aspectRatio, resolution, autoGenerate }) => {
1277
+ const mediaType = kind === "text-to-video"
1278
+ ? "video"
1279
+ : kind === "text-to-image"
1280
+ ? "image"
1281
+ : "audio";
1282
+ const track = trackId
1283
+ ? resolveTrack(trackId)
1284
+ : kind === "text-to-audio"
1285
+ ? findOrCreateTrack("audio")
1286
+ : kind === "text-to-video"
1287
+ ? findOrCreateTrack("video")
1288
+ : (tracks.find((t) => t.type === "video" || t.type === "overlay") ??
1289
+ findOrCreateTrack("video"));
1290
+ const generationStarted = autoGenerate !== false;
1291
+ const clip = makeClip({
1292
+ id: nextClipId(),
1293
+ trackId: track.id,
1294
+ name: prompt,
1295
+ startMs: startMs ?? trackEndMs(track.id),
1296
+ durationMs: durationMs ??
1297
+ (kind === "text-to-audio" ? 3000 : 5000),
1298
+ mediaType,
1299
+ sourceType: "generated",
1300
+ bindingKind: kind,
1301
+ status: generationStarted ? "generating" : "draft",
1302
+ prompt: prompt,
1303
+ provider: provider,
1304
+ model: model,
1305
+ voice: voice,
1306
+ width: clipWidth,
1307
+ height: clipHeight,
1308
+ aspectRatio: aspectRatio,
1309
+ resolution: resolution
1310
+ });
1311
+ clips.push(clip);
1312
+ selectedClipIds = [clip.id];
1313
+ const result = { ok: true, clip: serializeClip(clip), generationStarted };
1314
+ if (!generationStarted) {
1315
+ result.note = "Generation not started (autoGenerate=false).";
1316
+ }
1317
+ return result;
1318
+ }),
1319
+ sharedTool("ui_timeline_generatively_edit_clip", async ({ clip_id, instruction, provider, model }) => {
1320
+ // The P0 request captures a durable destination. Unlike general
1321
+ // timeline actions, this intentionally refuses names and selection.
1322
+ const clip = clips.find((candidate) => candidate.id === clip_id);
1323
+ if (!clip) {
1324
+ throw new Error(`No clip with id "${clip_id}" exists on this timeline. Call ui_timeline_get_state and pass the clip id.`);
1325
+ }
1326
+ const baseline = ensureBaselineTake(clip);
1327
+ if (baseline !== clip) {
1328
+ clips = clips.map((candidate) => candidate.id === clip.id ? baseline : candidate);
1329
+ }
1330
+ const source = captureMediaEditSourceContext(sequenceId, baseline);
1331
+ if (!source.ok)
1332
+ throw new Error(source.error);
1333
+ // Eval hosts have no selected model catalog. These deterministic
1334
+ // placeholders only stand in for a real provider's returned asset;
1335
+ // browser and server execution keep their normal model validation.
1336
+ const resolvedProvider = provider ?? "headless";
1337
+ const resolvedModel = model ?? "headless-video-to-video";
1338
+ const request = createMediaEditRequest({
1339
+ sourceContext: source.context,
1340
+ instruction: instruction,
1341
+ provider: resolvedProvider,
1342
+ model: resolvedModel
1343
+ });
1344
+ const generationId = nextVersionId();
1345
+ const now = new Date().toISOString();
1346
+ const next = composeGenerativeTakePatch(baseline, "restyle", {
1347
+ assetId: `generative://${baseline.id}/${generationId}`,
1348
+ jobId: generationId,
1349
+ createdAt: now,
1350
+ workflowUpdatedAt: now,
1351
+ provider: request.provider,
1352
+ model: request.model,
1353
+ prompt: request.instruction,
1354
+ durationMs: request.sourceContext.timelineDurationMs,
1355
+ mediaEdit: request
1356
+ });
1357
+ clips = clips.map((candidate) => candidate.id === baseline.id ? next : candidate);
1358
+ const take = next.versions?.find((candidate) => candidate.id === generationId);
1359
+ if (!take) {
1360
+ throw new Error("The AI edit candidate could not be recorded.");
1361
+ }
1362
+ return {
1363
+ ok: true,
1364
+ requestId: generationId,
1365
+ generationId,
1366
+ activeTakeId: activeTakeIdOf(next) ?? null,
1367
+ candidate: {
1368
+ id: take.id,
1369
+ status: take.status,
1370
+ source: take.source
1371
+ },
1372
+ clip: serializeClip(next)
1373
+ };
1374
+ }),
1375
+ sharedTool("ui_timeline_apply_take", async ({ clip_id, take_id }) => {
1376
+ const clip = clips.find((candidate) => candidate.id === clip_id);
1377
+ if (!clip) {
1378
+ throw new Error(`No clip with id "${clip_id}" exists on this timeline. Call ui_timeline_get_state and pass the clip id.`);
1379
+ }
1380
+ const take = (clip.versions ?? []).find((candidate) => candidate.id === take_id);
1381
+ if (!take?.mediaEdit) {
1382
+ throw new Error(`Take "${take_id}" is not an AI edit candidate for "${clip.name}". Apply only the candidate returned by ui_timeline_generatively_edit_clip.`);
1383
+ }
1384
+ const result = applyTakeToClip(clip, take_id);
1385
+ if (result.error)
1386
+ throw new Error(result.error);
1387
+ clips = clips.map((candidate) => candidate.id === clip.id ? result.clip : candidate);
1388
+ return { ok: true, clip: serializeClip(result.clip) };
1389
+ }),
1390
+ sharedTool("ui_timeline_split_clip", async ({ target, atMs }) => {
1391
+ const clip = resolveClip(target);
1392
+ const at = atMs ?? playheadMs;
1393
+ const [left, right] = splitClip(clip, at);
1394
+ left.id = nextClipId();
1395
+ right.id = nextClipId();
1396
+ const idx = clips.findIndex((c) => c.id === clip.id);
1397
+ clips.splice(idx, 1, left, right);
1398
+ selectedClipIds = selectedClipIds.filter((id) => id !== clip.id);
1399
+ mediaTracks = resliceTracksForSplitClip(mediaTracks, clip.id, left, right, nextTrackId);
1400
+ return { ok: true, clips: [serializeClip(left), serializeClip(right)] };
1401
+ }),
1402
+ sharedTool("ui_timeline_trim_clip", async ({ target, durationMs, inPointMs, outPointMs }) => {
1403
+ const trimmed = applyTrim(resolveClip(target), {
1404
+ durationMs: durationMs,
1405
+ inPointMs: inPointMs,
1406
+ outPointMs: outPointMs
1407
+ });
1408
+ mediaTracks = resliceTracksForTrimmedClip(mediaTracks, trimmed);
1409
+ return { ok: true, clip: serializeClip(trimmed) };
1410
+ }),
1411
+ sharedTool("ui_timeline_move_clip", async ({ target, startMs, trackId }) => {
1412
+ const moved = applyMove(resolveClip(target), {
1413
+ startMs: startMs,
1414
+ trackId: trackId
1415
+ });
1416
+ return { ok: true, clip: serializeClip(moved) };
1417
+ }),
1418
+ sharedTool("ui_timeline_delete_clip", async ({ target }) => {
1419
+ const clip = resolveClip(target);
1420
+ // Deleting a group deletes the parent, not the picture: its children
1421
+ // stay where they are and stop inheriting (D4). Leaving them with a
1422
+ // `parentId` nothing answers is what the validator calls a dangling
1423
+ // parent.
1424
+ const remaining = isGroupClip(clip) ? ungroup(clips, clip.id) : clips;
1425
+ clips = remaining.filter((c) => c.id !== clip.id);
1426
+ selectedClipIds = selectedClipIds.filter((id) => id !== clip.id);
1427
+ return { ok: true, deleted: serializeClip(clip) };
1428
+ }),
1429
+ sharedTool("ui_timeline_duplicate_clip", async ({ target, gapMs }) => {
1430
+ const src = resolveClip(target);
1431
+ const copy = {
1432
+ ...src,
1433
+ id: nextClipId(),
1434
+ startMs: src.startMs + src.durationMs + (gapMs ?? 0),
1435
+ versions: [],
1436
+ animations: src.animations?.map((a) => ({ ...a, id: nextAnimId() }))
1437
+ };
1438
+ clips.push(copy);
1439
+ selectedClipIds = [copy.id];
1440
+ return { ok: true, clip: serializeClip(copy) };
1441
+ }),
1442
+ sharedTool("ui_timeline_set_clip_params", async ({ target, ...wrapped }) => {
1443
+ const patch = unwrapClipParams(wrapped);
1444
+ let clip = resolveClip(target);
1445
+ rejectUnknownClipParams(patch);
1446
+ // Timing belongs to move_clip and trim_clip, but a caller sending it
1447
+ // here means one edit either way — so apply it through the same code
1448
+ // rather than dropping it or making them call twice.
1449
+ clip = applyTrim(clip, {
1450
+ durationMs: patch.durationMs,
1451
+ inPointMs: patch.inPointMs,
1452
+ outPointMs: patch.outPointMs
1453
+ });
1454
+ clip = applyMove(clip, {
1455
+ startMs: patch.startMs,
1456
+ trackId: patch.trackId
1457
+ });
1458
+ // The style is a patch over what the clip already carries: changing one
1459
+ // field used to mean re-sending the whole bag, and re-sending it is how
1460
+ // the four fields the caller did not mean to touch get overwritten.
1461
+ if (patch.textStyle !== undefined) {
1462
+ patch.textStyle = mergeTextStyle(clip, patch.textStyle);
1463
+ }
1464
+ if (patch.fontSizePx !== undefined) {
1465
+ // Shorthand for the one text field callers reach for by name.
1466
+ const size = patch.fontSizePx;
1467
+ const style = (patch.textStyle ?? clip.textStyle);
1468
+ if (!style) {
1469
+ throw new Error(`Clip "${clip.name}" carries no text to size; fontSizePx applies to a text clip's textStyle.`);
1470
+ }
1471
+ patch.textStyle = { ...style, fontSizePx: size };
1472
+ }
1473
+ if (patch.name !== undefined)
1474
+ clip.name = patch.name;
1475
+ if (patch.transform !== undefined)
1476
+ clip.transform = mergeClipTransform(clip.transform, patch.transform);
1477
+ if (patch.opacity !== undefined)
1478
+ clip.opacity = patch.opacity;
1479
+ if (patch.speedMultiplier !== undefined)
1480
+ clip.speedMultiplier = patch.speedMultiplier;
1481
+ if (patch.volumeDb !== undefined)
1482
+ clip.volumeDb = patch.volumeDb;
1483
+ if (patch.fadeInMs !== undefined)
1484
+ clip.fadeInMs = patch.fadeInMs;
1485
+ if (patch.fadeOutMs !== undefined)
1486
+ clip.fadeOutMs = patch.fadeOutMs;
1487
+ if (patch.fadeInShape !== undefined)
1488
+ clip.fadeInShape = patch.fadeInShape;
1489
+ if (patch.fadeOutShape !== undefined)
1490
+ clip.fadeOutShape = patch.fadeOutShape;
1491
+ if (patch.blendMode !== undefined)
1492
+ clip.blendMode = patch.blendMode;
1493
+ if (patch.borderRadius !== undefined)
1494
+ clip.borderRadius = patch.borderRadius;
1495
+ if (patch.hidden !== undefined)
1496
+ clip.hidden = patch.hidden;
1497
+ if (patch.muted !== undefined)
1498
+ clip.muted = patch.muted;
1499
+ if (patch.locked !== undefined)
1500
+ clip.locked = patch.locked;
1501
+ if (patch.textStyle !== undefined)
1502
+ clip.textStyle = patch.textStyle;
1503
+ if (patch.shapeStyle !== undefined)
1504
+ clip.shapeStyle = patch.shapeStyle;
1505
+ if (patch.captionStyle !== undefined) {
1506
+ // The style rides on the clip's caption, so a clip with no words to
1507
+ // draw has nowhere to put it. Say so rather than storing a look
1508
+ // nothing renders.
1509
+ if (!clip.caption) {
1510
+ throw new Error(`Clip "${clip.name}" carries no caption to style.`);
1511
+ }
1512
+ clip.caption = {
1513
+ ...clip.caption,
1514
+ style: patch.captionStyle
1515
+ };
1516
+ }
1517
+ return { ok: true, clip: serializeClip(clip) };
1518
+ }),
1519
+ sharedTool("ui_timeline_add_group", async ({ name, startMs, durationMs, trackId, children, transform }) => {
1520
+ // Resolve every child before anything is written: a half-applied group
1521
+ // leaves the caller with an empty group and no idea which of its clips
1522
+ // moved.
1523
+ const targets = (children ?? []).map((ref) => resolveClip(ref));
1524
+ const track = trackId
1525
+ ? resolveTrack(trackId)
1526
+ : findOrCreateTrack("overlay");
1527
+ const group = makeClip({
1528
+ id: nextClipId(),
1529
+ trackId: track.id,
1530
+ name: name,
1531
+ startMs: startMs,
1532
+ durationMs: durationMs,
1533
+ mediaType: "group",
1534
+ sourceType: "imported",
1535
+ status: "generated"
1536
+ });
1537
+ if (transform)
1538
+ group.transform = mergeClipTransform(group.transform, transform);
1539
+ clips.push(group);
1540
+ for (const child of targets) {
1541
+ child.parentId = group.id;
1542
+ }
1543
+ selectedClipIds = [group.id];
1544
+ return {
1545
+ ok: true,
1546
+ clip: serializeClip(group),
1547
+ children: targets.map((c) => c.id)
1548
+ };
1549
+ }),
1550
+ sharedTool("ui_timeline_set_parent", async ({ target, parentId }) => {
1551
+ const clip = resolveClip(target);
1552
+ if (parentId === null) {
1553
+ delete clip.parentId;
1554
+ return { ok: true, clip: serializeClip(clip) };
1555
+ }
1556
+ const parent = resolveClip(parentId);
1557
+ if (parent.mediaType !== "group") {
1558
+ throw new Error(`"${parent.name}" is a ${parent.mediaType} clip, not a group — parent to a clip created with add_group. ${validUnits(clips.filter((c) => c.mediaType === "group"), "group")}`);
1559
+ }
1560
+ // A cycle renders unparented and warns, so refusing it here is the
1561
+ // only place it can still be fixed.
1562
+ let cursor = parent;
1563
+ while (cursor) {
1564
+ if (cursor.id === clip.id) {
1565
+ throw new Error(`"${parent.name}" is inside "${clip.name}" — parenting them would make a cycle.`);
1566
+ }
1567
+ const next = cursor.parentId;
1568
+ cursor = next ? clips.find((c) => c.id === next) : undefined;
1569
+ }
1570
+ clip.parentId = parent.id;
1571
+ return { ok: true, clip: serializeClip(clip) };
1572
+ }),
1573
+ sharedTool("ui_timeline_set_transition", async ({ target, transition }) => {
1574
+ const clip = resolveClip(target);
1575
+ if (transition === null) {
1576
+ delete clip.transitionIn;
1577
+ }
1578
+ else {
1579
+ clip.transitionIn = buildTransition(transition);
1580
+ }
1581
+ return { ok: true, clip: serializeClip(clip) };
1582
+ }),
1583
+ tool("ui_timeline_generate_transition_at_cut", "Generate an inactive cut-level transition candidate for two explicitly adjacent clips. This does not change either clip or create a clip take. Preview the returned candidate before applying it.", generatedTransitionCandidateParams, async (args) => {
1584
+ const candidate = await createTransitionCandidate(args);
1585
+ return {
1586
+ ok: true,
1587
+ requestId: candidate.generationId,
1588
+ generationId: candidate.generationId,
1589
+ candidate,
1590
+ source: candidate.source
1591
+ };
1592
+ }),
1593
+ tool("ui_timeline_preview_transition_candidate", "Audition one generated cut-level transition candidate without changing the timeline document. Refuses candidates whose selected clips, source takes, or cut are stale.", transitionCandidateParams, async ({ candidate_id }) => {
1594
+ const candidate = transitionCandidates.find((entry) => entry.id === candidate_id);
1595
+ if (!candidate) {
1596
+ throw new Error(`No generated transition candidate named "${candidate_id}" exists.`);
1597
+ }
1598
+ assertFreshTransitionCandidate(candidate);
1599
+ auditionedTransitionCandidateId = candidate.id;
1600
+ return { ok: true, previewOnly: true, candidate };
1601
+ }),
1602
+ tool("ui_timeline_apply_transition_at_cut", `${APPLY_TRANSITION_AT_CUT_DESCRIPTION} With clip ids and timing, this creates an inactive cut-level candidate. Apply a returned candidate_id explicitly to commit it as one undoable operation.`, transitionCandidateLifecycleParams, async (args) => {
1603
+ if (args.candidate_id !== undefined) {
1604
+ const applied = applyTransitionCandidate(args.candidate_id);
1605
+ return { ok: true, applied: true, ...applied };
1606
+ }
1607
+ const candidate = await createTransitionCandidate(args);
1608
+ return {
1609
+ ok: true,
1610
+ applied: false,
1611
+ requestId: candidate.generationId,
1612
+ generationId: candidate.generationId,
1613
+ candidate,
1614
+ source: candidate.source,
1615
+ next: "Preview with ui_timeline_preview_transition_candidate, then call this tool with candidate_id to apply."
1616
+ };
1617
+ }),
1618
+ sharedTool("ui_timeline_set_mask", async ({ target, mask }) => {
1619
+ const clip = resolveClip(target);
1620
+ if (mask === null) {
1621
+ delete clip.mask;
1622
+ }
1623
+ else {
1624
+ clip.mask = buildMask(mask, parseSvgPath);
1625
+ }
1626
+ return { ok: true, clip: serializeClip(clip) };
1627
+ }),
1628
+ sharedTool("ui_timeline_set_matte", async ({ target, matte }) => {
1629
+ const clip = resolveClip(target);
1630
+ if (matte === null) {
1631
+ delete clip.matte;
1632
+ return { ok: true, clip: serializeClip(clip) };
1633
+ }
1634
+ const input = matte;
1635
+ const source = resolveClip(input.source);
1636
+ if (source.id === clip.id) {
1637
+ throw new Error(`"${clip.name}" cannot be its own matte source — name another clip.`);
1638
+ }
1639
+ const matteOut = {
1640
+ sourceClipId: source.id,
1641
+ mode: input.mode
1642
+ };
1643
+ if (input.invert !== undefined)
1644
+ matteOut.invert = input.invert;
1645
+ clip.matte = matteOut;
1646
+ return { ok: true, clip: serializeClip(clip) };
1647
+ }),
1648
+ // Headless-only: the inspector's own controls write these knobs into the
1649
+ // store. Both this and the ops module's `set_generated_matte` reach
1650
+ // `selectGeneratedMatteVersion`, so the version list cannot fork.
1651
+ tool("ui_timeline_set_generated_matte", SET_GENERATED_MATTE_DESCRIPTION, setGeneratedMatteParams, async (args) => {
1652
+ const input = args;
1653
+ const clip = resolveClip(input.target);
1654
+ if (input.clear === true) {
1655
+ delete clip.generatedMatte;
1656
+ return {
1657
+ ok: true,
1658
+ clip: serializeClip(clip),
1659
+ generatedMatte: null
1660
+ };
1661
+ }
1662
+ if (!clip.generatedMatte) {
1663
+ throw new Error(`"${clip.name}" carries no generated matte. Run isolate_subject ` +
1664
+ "on it first; this op only adjusts one that exists.");
1665
+ }
1666
+ if (input.selectVersionAssetId !== undefined) {
1667
+ clip.generatedMatte = selectGeneratedMatteVersion(clip, input.selectVersionAssetId).generatedMatte;
1668
+ }
1669
+ const matte = clip.generatedMatte;
1670
+ if (input.invert !== undefined)
1671
+ matte.invert = input.invert;
1672
+ if (input.strength !== undefined) {
1673
+ matte.strength = Math.min(1, Math.max(0, input.strength));
1674
+ }
1675
+ if (input.featherPx !== undefined) {
1676
+ matte.featherPx = Math.max(0, input.featherPx);
1677
+ }
1678
+ return { ok: true, clip: serializeClip(clip), generatedMatte: matte };
1679
+ }),
1680
+ sharedTool("ui_timeline_set_time_remap", async ({ target, timeRemap }) => {
1681
+ const clip = resolveClip(target);
1682
+ if (timeRemap === null) {
1683
+ delete clip.timeRemap;
1684
+ }
1685
+ else {
1686
+ clip.timeRemap = buildTimeRemap(timeRemap);
1687
+ }
1688
+ return { ok: true, clip: serializeClip(clip) };
1689
+ }),
1690
+ sharedTool("ui_timeline_set_effects", async ({ target, effects }) => {
1691
+ const clip = resolveClip(target);
1692
+ const list = effects.map(buildEffect);
1693
+ if (list.length === 0) {
1694
+ delete clip.effects;
1695
+ }
1696
+ else {
1697
+ clip.effects = list;
1698
+ }
1699
+ return { ok: true, clip: serializeClip(clip) };
1700
+ }),
1701
+ sharedTool("ui_timeline_set_clip_binding", async ({ target, ...patch }) => {
1702
+ const clip = resolveClip(target);
1703
+ if (clip.sourceType !== "generated") {
1704
+ throw new Error(`"${clip.name}" is not a generated clip — ui_timeline_set_clip_binding only applies to clips created with ui_timeline_generate_clip.`);
1705
+ }
1706
+ if (patch.prompt !== undefined)
1707
+ clip.prompt = patch.prompt;
1708
+ if (patch.negativePrompt !== undefined)
1709
+ clip.negativePrompt = patch.negativePrompt;
1710
+ if (patch.provider !== undefined)
1711
+ clip.provider = patch.provider;
1712
+ if (patch.model !== undefined)
1713
+ clip.model = patch.model;
1714
+ if (patch.voice !== undefined)
1715
+ clip.voice = patch.voice;
1716
+ if (patch.width !== undefined)
1717
+ clip.width = patch.width;
1718
+ if (patch.height !== undefined)
1719
+ clip.height = patch.height;
1720
+ if (patch.aspectRatio !== undefined)
1721
+ clip.aspectRatio = patch.aspectRatio;
1722
+ if (patch.resolution !== undefined)
1723
+ clip.resolution = patch.resolution;
1724
+ if (patch.strength !== undefined)
1725
+ clip.strength = patch.strength;
1726
+ if (patch.numInferenceSteps !== undefined)
1727
+ clip.numInferenceSteps = patch.numInferenceSteps;
1728
+ if (patch.regenerate)
1729
+ clip.status = "queued";
1730
+ return { ok: true, clip: serializeClip(clip) };
1731
+ }),
1732
+ sharedTool("ui_timeline_animate_clip", async ({ target, mode, animations }) => {
1733
+ const clip = resolveClip(target);
1734
+ const inputs = animations;
1735
+ const built = [];
1736
+ for (const input of inputs) {
1737
+ if (input.preset === CUSTOM_ANIMATION_PRESET_ID) {
1738
+ // `{preset: "custom", custom: {curves}}` reads as naturally as the
1739
+ // flat form, so lift it rather than refusing it.
1740
+ built.push(await buildCustomAnimation(clip, liftCustomAnimation(input)));
1741
+ continue;
1742
+ }
1743
+ const preset = ANIMATION_PRESETS.find((p) => p.id === input.preset);
1744
+ if (!preset) {
1745
+ const ids = ANIMATION_PRESETS.map((p) => p.id).join(", ");
1746
+ throw new Error(`Unknown animation preset "${input.preset}". Valid presets: ${ids}, ${CUSTOM_ANIMATION_PRESET_ID}.`);
1747
+ }
1748
+ if (!preset.roles.includes(input.role)) {
1749
+ throw new Error(`Preset "${input.preset}" does not support role "${input.role}". Valid roles for "${input.preset}": ${preset.roles.join(", ")}.`);
1750
+ }
1751
+ if (input.preset === "typewriter" && clip.mediaType !== "text") {
1752
+ throw new Error('Preset "typewriter" requires a text clip.');
1753
+ }
1754
+ const timing = input.preset === "typewriter"
1755
+ ? typewriterTiming(clip.textStyle?.text ?? "", clip.durationMs - (input.delayMs ?? 0), input.durationMs, input.stagger)
1756
+ : null;
1757
+ built.push({
1758
+ id: nextAnimId(),
1759
+ role: input.role,
1760
+ preset: input.preset,
1761
+ durationMs: timing?.durationMs ?? input.durationMs ?? preset.defaultDurationMs,
1762
+ delayMs: input.delayMs,
1763
+ easing: input.easing,
1764
+ params: input.params,
1765
+ stagger: timing?.stagger ?? input.stagger,
1766
+ caret: input.preset === "typewriter" ? input.caret : undefined
1767
+ });
1768
+ }
1769
+ clip.animations =
1770
+ mode === "add"
1771
+ ? [...(clip.animations ?? []), ...built]
1772
+ : built;
1773
+ return { ok: true, clip: serializeClip(clip) };
1774
+ }),
1775
+ // Headless-only: the browser writes curves through the inspector, not
1776
+ // through an agent call. Both this and the ops module's `set_baked_animation`
1777
+ // go through `buildBakedAnimation`, so the replace rule cannot fork.
1778
+ tool("ui_timeline_set_baked_animation", SET_BAKED_ANIMATION_DESCRIPTION, setBakedAnimationParams, async ({ target, animation }) => {
1779
+ const clip = resolveClip(target);
1780
+ const outcome = buildBakedAnimation(clip, animation, nextAnimId);
1781
+ clip.animations = outcome.animations;
1782
+ return {
1783
+ ok: true,
1784
+ clip: serializeClip(clip),
1785
+ animationId: outcome.animationId,
1786
+ keyframeCount: outcome.animation.custom?.curves[0]?.keyframes.length ?? 0,
1787
+ replaced: outcome.replaced
1788
+ };
1789
+ }),
1790
+ sharedTool("ui_timeline_stagger_animations", async ({ clip_ids, offset_ms }) => {
1791
+ const selected = clip_ids.map(resolveClip);
1792
+ const ids = selected.map((clip) => clip.id);
1793
+ if (new Set(ids).size !== ids.length) {
1794
+ throw new Error("clip_ids must contain distinct clip IDs.");
1795
+ }
1796
+ for (const clip of selected) {
1797
+ if (!clip.animations?.length)
1798
+ throw new Error(`Clip "${clip.name}" has no animations to stagger.`);
1799
+ }
1800
+ const staggered = staggerClipAnimations(clips, ids, offset_ms);
1801
+ const byId = new Map(staggered.map((clip) => [clip.id, clip]));
1802
+ clips.splice(0, clips.length, ...staggered);
1803
+ return { ok: true, clips: ids.map((id) => serializeClip(byId.get(id))) };
1804
+ }),
1805
+ sharedTool("ui_timeline_clear_animations", async ({ target, role }) => {
1806
+ const clip = resolveClip(target);
1807
+ clip.animations = role
1808
+ ? (clip.animations ?? []).filter((a) => a.role !== role)
1809
+ : [];
1810
+ return { ok: true, clip: serializeClip(clip) };
1811
+ }),
1812
+ sharedTool("ui_timeline_list_animation_presets", async () => {
1813
+ const presets = ANIMATION_PRESETS.map((p) => ({
1814
+ id: p.id,
1815
+ roles: p.roles,
1816
+ defaultDurationMs: p.defaultDurationMs,
1817
+ defaultEasing: p.defaultEasing,
1818
+ params: p.params,
1819
+ describe: p.describe
1820
+ }));
1821
+ return {
1822
+ ok: true,
1823
+ presets,
1824
+ custom: CUSTOM_ANIMATION_CONTRACT,
1825
+ properties: CUSTOM_ANIMATION_CONTRACT.properties
1826
+ };
1827
+ }),
1828
+ sharedTool("ui_timeline_select_clip", async ({ target }) => {
1829
+ const t = target;
1830
+ if (!t) {
1831
+ selectedClipIds = [];
1832
+ return { ok: true, selected: null };
1833
+ }
1834
+ const clip = resolveClip(t);
1835
+ selectedClipIds = [clip.id];
1836
+ return { ok: true, selected: serializeClip(clip) };
1837
+ }),
1838
+ sharedTool("ui_timeline_seek", async ({ timeMs }) => {
1839
+ playheadMs = Math.max(0, timeMs);
1840
+ return { ok: true, playheadMs };
1841
+ }),
1842
+ sharedTool("ui_timeline_add_marker", async ({ timeMs, label, color, note }) => {
1843
+ const at = timeMs;
1844
+ if (at < 0) {
1845
+ throw new Error(`A marker cannot sit before zero; got ${at}ms.`);
1846
+ }
1847
+ const marker = {
1848
+ id: nextMarkerId(),
1849
+ timeMs: Math.round(at),
1850
+ label: label ?? ""
1851
+ };
1852
+ if (color !== undefined)
1853
+ marker.color = color;
1854
+ if (note !== undefined)
1855
+ marker.note = note;
1856
+ markers.push(marker);
1857
+ return { ok: true, marker: { ...marker } };
1858
+ }),
1859
+ // ── Guided video flow (PRD § 8.6) ───────────────────────────────────
1860
+ sharedTool("ui_timeline_set_setup", async ({ stage, brief, format, voiceover }) => {
1861
+ setup = {
1862
+ stage: stage ?? setup?.stage ?? "idea",
1863
+ brief: brief ?? setup?.brief ?? "",
1864
+ format: format ?? setup?.format,
1865
+ // Absent leaves the choice unsaid; `false` is a deliberate silence,
1866
+ // so it is written rather than read as "nothing passed".
1867
+ voiceover: voiceover ?? setup?.voiceover,
1868
+ beats: setup?.beats
1869
+ };
1870
+ return { ok: true, setup: structuredClone(setup) };
1871
+ }),
1872
+ sharedTool("ui_timeline_plan_beats", async ({ beats }) => {
1873
+ // There is no Director on this surface — the caller *is* the model, so
1874
+ // it writes the plan rather than asking for one. Refusing without
1875
+ // `beats` says which half of the shared contract this host implements.
1876
+ const written = beats;
1877
+ if (!written || written.length === 0) {
1878
+ throw new Error("This surface has no Director: pass `beats` with the plan you want. Each beat needs a prompt and a durationMs.");
1879
+ }
1880
+ const planned = written.map((beat) => ({
1881
+ id: `beat_${++beatSeq}`,
1882
+ prompt: beat.prompt,
1883
+ duration_ms: Math.max(1, Math.round(beat.durationMs)),
1884
+ transition: beat.transition,
1885
+ voiceover: beat.voiceover,
1886
+ music: beat.music
1887
+ }));
1888
+ setup = {
1889
+ stage: "review",
1890
+ brief: setup?.brief ?? "",
1891
+ format: setup?.format,
1892
+ voiceover: setup?.voiceover,
1893
+ beats: planned
1894
+ };
1895
+ // Named in the answer because it is the contract: the plan is text,
1896
+ // and costs nothing until generate_from_beats runs (D4).
1897
+ return {
1898
+ ok: true,
1899
+ beats: planned.map((beat) => ({ ...beat })),
1900
+ clipsCreated: 0,
1901
+ jobsStarted: 0
1902
+ };
1903
+ }),
1904
+ sharedTool("ui_timeline_update_beat", async ({ beat, prompt, durationMs, transition, voiceover, music }) => {
1905
+ const found = resolveBeat(beat);
1906
+ const next = { ...found };
1907
+ if (prompt !== undefined)
1908
+ next.prompt = prompt;
1909
+ if (durationMs !== undefined) {
1910
+ next.duration_ms = Math.max(1, Math.round(durationMs));
1911
+ }
1912
+ if (transition !== undefined) {
1913
+ next.transition = transition ?? undefined;
1914
+ }
1915
+ if (voiceover !== undefined)
1916
+ next.voiceover = voiceover;
1917
+ if (music !== undefined)
1918
+ next.music = music;
1919
+ setup = {
1920
+ stage: setup?.stage ?? "review",
1921
+ brief: setup?.brief ?? "",
1922
+ format: setup?.format,
1923
+ voiceover: setup?.voiceover,
1924
+ beats: (setup?.beats ?? []).map((candidate) => candidate.id === found.id ? next : candidate)
1925
+ };
1926
+ return { ok: true, beat: { ...next } };
1927
+ }),
1928
+ sharedTool("ui_timeline_remove_beat", async ({ beat }) => {
1929
+ // Resolved before it is dropped, so the answer names what went and an
1930
+ // unresolvable target says so rather than reporting a silent success.
1931
+ const found = resolveBeat(beat);
1932
+ setup = {
1933
+ stage: setup?.stage ?? "review",
1934
+ brief: setup?.brief ?? "",
1935
+ format: setup?.format,
1936
+ voiceover: setup?.voiceover,
1937
+ beats: (setup?.beats ?? []).filter((candidate) => candidate.id !== found.id)
1938
+ };
1939
+ return { ok: true, removed: { ...found } };
1940
+ }),
1941
+ sharedTool("ui_timeline_generate_from_beats", async ({ model, provider, voice, music }) => {
1942
+ const plan = setup?.beats ?? [];
1943
+ if (plan.length === 0) {
1944
+ throw new Error("There is no beat plan to generate from; call ui_timeline_plan_beats first.");
1945
+ }
1946
+ const videoTrack = findOrCreateTrack("video");
1947
+ const wantsMusic = music ??
1948
+ plan.some((beat) => beat.music === true);
1949
+ const voiceTrack = voice !== undefined && plan.some((beat) => (beat.voiceover ?? "") !== "")
1950
+ ? findOrCreateTrack("audio")
1951
+ : null;
1952
+ const videoClipIds = [];
1953
+ const voiceoverClipIds = [];
1954
+ const planned = [];
1955
+ let startMs = 0;
1956
+ for (const [index, beat] of plan.entries()) {
1957
+ const durationMs = Math.max(1, Math.round(beat.duration_ms));
1958
+ const clip = makeClip({
1959
+ id: nextClipId(),
1960
+ trackId: videoTrack.id,
1961
+ name: `Beat ${index + 1}`,
1962
+ startMs,
1963
+ durationMs,
1964
+ mediaType: "video",
1965
+ sourceType: "generated",
1966
+ bindingKind: "text-to-video",
1967
+ prompt: beat.prompt,
1968
+ provider: provider,
1969
+ model: model,
1970
+ beatId: beat.id,
1971
+ status: "draft"
1972
+ });
1973
+ if (beat.transition) {
1974
+ clip.transitionIn = buildTransition({
1975
+ type: beat.transition,
1976
+ durationMs: 500
1977
+ });
1978
+ }
1979
+ clips.push(clip);
1980
+ videoClipIds.push(clip.id);
1981
+ const line = (beat.voiceover ?? "").trim();
1982
+ if (voiceTrack && line.length > 0) {
1983
+ const voiceClip = makeClip({
1984
+ id: nextClipId(),
1985
+ trackId: voiceTrack.id,
1986
+ name: `Beat ${index + 1} voiceover`,
1987
+ startMs,
1988
+ durationMs,
1989
+ mediaType: "audio",
1990
+ sourceType: "generated",
1991
+ bindingKind: "text-to-audio",
1992
+ prompt: line,
1993
+ voice: voice,
1994
+ beatId: beat.id,
1995
+ status: "draft"
1996
+ });
1997
+ clips.push(voiceClip);
1998
+ voiceoverClipIds.push(voiceClip.id);
1999
+ }
2000
+ planned.push({ ...beat, clip_id: clip.id });
2001
+ startMs += durationMs;
2002
+ }
2003
+ let musicClipId = null;
2004
+ if (wantsMusic) {
2005
+ // One bed under the whole cut, never one per beat: overlapping music
2006
+ // clips would play at once.
2007
+ const musicTrack = findOrCreateTrack("audio");
2008
+ const musicClip = makeClip({
2009
+ id: nextClipId(),
2010
+ trackId: musicTrack.id,
2011
+ name: "Music",
2012
+ startMs: 0,
2013
+ durationMs: startMs,
2014
+ mediaType: "audio",
2015
+ sourceType: "generated",
2016
+ bindingKind: "text-to-audio",
2017
+ prompt: `Instrumental score under: ${setup?.brief ?? ""}`,
2018
+ status: "draft"
2019
+ });
2020
+ clips.push(musicClip);
2021
+ musicClipId = musicClip.id;
2022
+ }
2023
+ setup = {
2024
+ stage: "done",
2025
+ brief: setup?.brief ?? "",
2026
+ format: setup?.format,
2027
+ voiceover: setup?.voiceover,
2028
+ beats: planned
2029
+ };
2030
+ return {
2031
+ ok: true,
2032
+ videoClipIds,
2033
+ voiceoverClipIds,
2034
+ musicClipId,
2035
+ // Nothing renders headlessly: this authors the cut, and the clips are
2036
+ // drafts until a renderer runs them.
2037
+ startedClipIds: [],
2038
+ note: "Clips created as drafts — this surface renders nothing."
2039
+ };
2040
+ }),
2041
+ sharedTool("ui_timeline_delete_marker", async ({ target }) => {
2042
+ const marker = resolveMarker(target);
2043
+ markers = markers.filter((m) => m.id !== marker.id);
2044
+ return { ok: true, deleted: { ...marker } };
2045
+ }),
2046
+ sharedTool("ui_timeline_set_markers_from_beats", async ({ onsets_ms, bpm, offset_ms, count, label }) => {
2047
+ const grid = buildBeatGrid({
2048
+ onsetsMs: onsets_ms,
2049
+ bpm: bpm,
2050
+ offsetMs: offset_ms,
2051
+ count: count
2052
+ });
2053
+ const stem = (label ?? "Beat").trim() || "Beat";
2054
+ const taken = new Set(markers.map((m) => m.timeMs));
2055
+ const added = [];
2056
+ const skipped = [];
2057
+ for (const [index, timeMs] of grid.entries()) {
2058
+ if (taken.has(timeMs)) {
2059
+ skipped.push(timeMs);
2060
+ continue;
2061
+ }
2062
+ const marker = {
2063
+ id: nextMarkerId(),
2064
+ timeMs,
2065
+ label: `${stem} ${index + 1}`
2066
+ };
2067
+ markers.push(marker);
2068
+ taken.add(timeMs);
2069
+ added.push(marker);
2070
+ }
2071
+ return {
2072
+ ok: true,
2073
+ grid: { count: grid.length, firstMs: grid[0], lastMs: grid[grid.length - 1] },
2074
+ added: added.map((m) => ({ ...m })),
2075
+ skipped_times_ms: skipped,
2076
+ markers: markers.length
2077
+ };
2078
+ }),
2079
+ sharedTool("ui_timeline_snap_to_beats", async ({ targets, onsets_ms, bpm, offset_ms, tolerance_ms, mode, action }) => {
2080
+ const named = targets === undefined || targets === "all"
2081
+ ? undefined
2082
+ : targets;
2083
+ const { clips: targeted, missing } = resolveSnapTargets(named);
2084
+ const offsetMs = offset_ms ?? 0;
2085
+ // A tempo grid has to reach the last boundary being snapped, so its
2086
+ // length comes from the targets rather than from the caller.
2087
+ const reachMs = targeted.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs), 0);
2088
+ const grid = buildBeatGrid({
2089
+ onsetsMs: onsets_ms,
2090
+ bpm: bpm,
2091
+ offsetMs: offset_ms,
2092
+ count: bpm === undefined
2093
+ ? undefined
2094
+ : beatCountToCover(bpm, offsetMs, reachMs)
2095
+ });
2096
+ const options = {};
2097
+ if (tolerance_ms !== undefined) {
2098
+ options.toleranceMs = tolerance_ms;
2099
+ }
2100
+ if (mode !== undefined)
2101
+ options.mode = mode;
2102
+ if (action !== undefined)
2103
+ options.action = action;
2104
+ const result = snapClipsToGrid(targeted.map((clip) => ({
2105
+ id: clip.id,
2106
+ startMs: clip.startMs,
2107
+ durationMs: clip.durationMs
2108
+ })), grid, options);
2109
+ const byId = new Map(targeted.map((clip) => [clip.id, clip]));
2110
+ const reported = result.clips.map((entry) => {
2111
+ const clip = byId.get(entry.clipId);
2112
+ if (entry.snapped && clip) {
2113
+ // Through the same ops the caller would use: a group carries its
2114
+ // children (D4) and a trim carries the source points, neither of
2115
+ // which a raw startMs/durationMs write does.
2116
+ try {
2117
+ if (entry.after.durationMs === entry.before.durationMs) {
2118
+ applyMove(clip, { startMs: entry.after.startMs });
2119
+ }
2120
+ else {
2121
+ let trimmed = clip;
2122
+ if (entry.after.startMs !== entry.before.startMs) {
2123
+ trimmed = applyTrimStart(clip, entry.after.startMs);
2124
+ }
2125
+ applyTrim(trimmed, { durationMs: entry.after.durationMs });
2126
+ }
2127
+ }
2128
+ catch (error) {
2129
+ return {
2130
+ ...entry,
2131
+ snapped: false,
2132
+ after: entry.before,
2133
+ delta: { startMs: 0, endMs: 0 },
2134
+ reason: error instanceof Error ? error.message : String(error),
2135
+ clipName: clip.name
2136
+ };
2137
+ }
2138
+ }
2139
+ return { ...entry, clipName: clip?.name ?? null };
2140
+ });
2141
+ // A name nothing matched is a skip like any other: the caller has to
2142
+ // see it in the same list, not infer it from a shorter one.
2143
+ for (const target of missing) {
2144
+ reported.push({
2145
+ clipId: target,
2146
+ clipName: null,
2147
+ snapped: false,
2148
+ before: { startMs: 0, endMs: 0, durationMs: 0 },
2149
+ after: { startMs: 0, endMs: 0, durationMs: 0 },
2150
+ delta: { startMs: 0, endMs: 0 },
2151
+ reason: `no clip matches "${target}"`
2152
+ });
2153
+ }
2154
+ return {
2155
+ ok: true,
2156
+ grid: {
2157
+ count: grid.length,
2158
+ firstMs: grid[0],
2159
+ lastMs: grid[grid.length - 1]
2160
+ },
2161
+ toleranceMs: result.toleranceMs,
2162
+ mode: result.mode,
2163
+ action: result.action,
2164
+ snapped: result.snapped,
2165
+ skipped: result.skipped + missing.length,
2166
+ clips: reported
2167
+ };
2168
+ }),
2169
+ // ── MIDI ────────────────────────────────────────────────────────────
2170
+ // Notes live on the clip, the instrument on the track, and the tempo on
2171
+ // the document, so each gets its own op.
2172
+ sharedTool("ui_timeline_add_midi_clip", async ({ track, start_ms, duration_ms, name, notes }) => {
2173
+ const midiTrack = resolveMidiTrack(track);
2174
+ const clipName = name ?? "Phrase";
2175
+ const clip = makeClip({
2176
+ id: nextClipId(),
2177
+ trackId: midiTrack.id,
2178
+ name: clipName,
2179
+ startMs: start_ms,
2180
+ durationMs: duration_ms,
2181
+ mediaType: "midi",
2182
+ sourceType: "imported",
2183
+ status: "generated",
2184
+ notes: buildNotes(notes, clipName)
2185
+ });
2186
+ clips.push(clip);
2187
+ selectedClipIds = [clip.id];
2188
+ return { ok: true, clip: serializeClip(clip) };
2189
+ }),
2190
+ sharedTool("ui_timeline_set_notes", async ({ clip: target, notes }) => {
2191
+ const clip = resolveMidiClip(target);
2192
+ // Built before anything is written: a refused list must leave the clip
2193
+ // exactly as it was, not half replaced.
2194
+ clip.notes = buildNotes(notes, clip.name);
2195
+ return { ok: true, clip: serializeClip(clip) };
2196
+ }),
2197
+ sharedTool("ui_timeline_set_tempo", async ({ bpm, offset_ms, beats_per_bar, beat_unit }) => {
2198
+ const previous = resolveTempo({ tempo });
2199
+ const next = {
2200
+ bpm: bpm,
2201
+ offsetMs: offset_ms ?? previous.offsetMs,
2202
+ timeSignature: {
2203
+ beatsPerBar: beats_per_bar ??
2204
+ previous.timeSignature.beatsPerBar,
2205
+ beatUnit: beat_unit ?? previous.timeSignature.beatUnit
2206
+ }
2207
+ };
2208
+ // Milliseconds are the master clock, so the stored ms of every midi
2209
+ // clip move and nothing else on the timeline does.
2210
+ clips = rescaleClipsForTempo(clips, tracks, previous, next);
2211
+ tempo = next;
2212
+ return {
2213
+ ok: true,
2214
+ tempo: next,
2215
+ previousTempo: previous,
2216
+ rescaledClipIds: clips
2217
+ .filter((c) => c.mediaType === "midi")
2218
+ .map((c) => c.id)
2219
+ };
2220
+ }),
2221
+ sharedTool("ui_timeline_set_track_instrument", async ({ track, instrument }) => {
2222
+ const midiTrack = resolveMidiTrack(track);
2223
+ // Resolved before the track is touched, so an unknown preset id leaves
2224
+ // the voice the track already had.
2225
+ midiTrack.instrument = resolveInstrumentArg(instrument);
2226
+ return { ok: true, track: serializeTrack(midiTrack) };
2227
+ }),
2228
+ // The note edits. Each runs the whole list through one pure function from
2229
+ // `@nodetool-ai/timeline` and replaces it — the ids survive, so a
2230
+ // selection and an undo in the editor still point at the same notes.
2231
+ sharedTool("ui_timeline_transpose_clip", async ({ clip: target, semitones }) => {
2232
+ const clip = resolveMidiClip(target);
2233
+ clip.notes = transposeNotes(clip.notes ?? [], semitones);
2234
+ return {
2235
+ ok: true,
2236
+ clip: serializeClip(clip),
2237
+ noteCount: clip.notes.length
2238
+ };
2239
+ }),
2240
+ sharedTool("ui_timeline_quantize_notes", async ({ clip: clipRef, division, strength, target }) => {
2241
+ const clip = resolveMidiClip(clipRef);
2242
+ const before = clip.notes ?? [];
2243
+ const options = { division: division };
2244
+ if (strength !== undefined)
2245
+ options.strength = strength;
2246
+ if (target !== undefined)
2247
+ options.target = target;
2248
+ const quantized = quantizeNotes(before, options);
2249
+ // The notes come back in the order they went in, so this pairs each
2250
+ // one with itself. A phrase already on the grid reports 0 moved
2251
+ // instead of reading as an edit that changed something.
2252
+ const movedNoteCount = quantized.filter((note, i) => note.startTick !== before[i].startTick ||
2253
+ note.durationTick !== before[i].durationTick).length;
2254
+ clip.notes = quantized;
2255
+ return {
2256
+ ok: true,
2257
+ clip: serializeClip(clip),
2258
+ noteCount: quantized.length,
2259
+ movedNoteCount
2260
+ };
2261
+ }),
2262
+ sharedTool("ui_timeline_scale_velocity", async ({ clip: target, factor }) => {
2263
+ const clip = resolveMidiClip(target);
2264
+ clip.notes = scaleVelocity(clip.notes ?? [], factor);
2265
+ return {
2266
+ ok: true,
2267
+ clip: serializeClip(clip),
2268
+ noteCount: clip.notes.length
2269
+ };
2270
+ }),
2271
+ tool("ui_timeline_insert_composition", "Insert a stored composition — a group of clips with named parameters (a lower third, a title card, a callout) — at a timecode. Its children keep the layering the template declares: each template track becomes an overlay track of its own, front-most on top, reused across insertions. `params` overrides the template's defaults by name; anything omitted keeps its default. Call list_compositions for the ids and their parameters.", z.object({
2272
+ composition_id: z
2273
+ .string()
2274
+ .describe("Composition id, from list_compositions."),
2275
+ startMs: z
2276
+ .number()
2277
+ .describe("Where the group starts on the timeline, in ms."),
2278
+ trackId: z
2279
+ .string()
2280
+ .optional()
2281
+ .describe("Track for the group clip itself. Its children still get their own overlay tracks. Defaults to an overlay track."),
2282
+ params: z
2283
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
2284
+ .optional()
2285
+ .describe("Parameter overrides by name, e.g. {name: \"Ada Lovelace\"}. A name the template does not declare, or a value of the wrong type, is refused.")
2286
+ }), async ({ composition_id, startMs, trackId, params }) => {
2287
+ if (!loadComposition) {
2288
+ throw new Error("This surface has no composition library, so insert_composition cannot resolve a template.");
2289
+ }
2290
+ const id = composition_id;
2291
+ const composition = await loadComposition.get(id);
2292
+ if (!composition) {
2293
+ const available = await loadComposition.listIds();
2294
+ throw new Error(`No composition with id "${id}". ` +
2295
+ (available.length > 0
2296
+ ? `Available: ${available.join(", ")}.`
2297
+ : "This install has none — save one with save_composition."));
2298
+ }
2299
+ const minted = instantiateComposition(composition, {
2300
+ startMs: startMs,
2301
+ params: params,
2302
+ newId: nextClipId
2303
+ });
2304
+ // Template track ids are names, not document ids. Two clips overlapping
2305
+ // on one track auto-dissolve into each other, so each template track
2306
+ // becomes a track of its own — created front-most first, because the
2307
+ // lowest track index draws on top (I9).
2308
+ const templateTracks = [];
2309
+ for (const child of composition.children) {
2310
+ if (!templateTracks.includes(child.trackId)) {
2311
+ templateTracks.push(child.trackId);
2312
+ }
2313
+ }
2314
+ const mapped = new Map();
2315
+ for (const name of [...templateTracks].reverse()) {
2316
+ const existing = tracks.find((t) => t.type === "overlay" && t.name === name);
2317
+ mapped.set(name, (existing ?? addTrackInternal("overlay", name)).id);
2318
+ }
2319
+ // The group draws nothing, but it still occupies its track's timeline,
2320
+ // and a group sharing a track with one of its children reads as an
2321
+ // overlap. It gets a track named after the composition instead.
2322
+ const groupTrack = trackId
2323
+ ? resolveTrack(trackId)
2324
+ : (tracks.find((t) => t.type === "overlay" && t.name === composition.name) ?? addTrackInternal("overlay", composition.name));
2325
+ const [group, ...children] = minted;
2326
+ group.trackId = groupTrack.id;
2327
+ for (const [index, child] of children.entries()) {
2328
+ child.trackId =
2329
+ mapped.get(composition.children[index].trackId) ?? groupTrack.id;
2330
+ }
2331
+ clips.push(group, ...children);
2332
+ selectedClipIds = [group.id];
2333
+ return {
2334
+ ok: true,
2335
+ compositionId: composition.id,
2336
+ clip: serializeClip(group),
2337
+ children: children.map((child) => serializeClip(child)),
2338
+ params: group.compositionParams ?? {}
2339
+ };
2340
+ }),
2341
+ // Headless-only: the editor's ClipVersionHistory panel writes take
2342
+ // switches, renames and deletes straight into TimelineStore via
2343
+ // restoreVersion/renameTake/deleteTake. This op reaches the same
2344
+ // `selectTake`/`renameTake`/`deleteTake` helpers, so the take list cannot
2345
+ // fork between the two hosts (P0 AI Video, PRD § 8.10).
2346
+ tool("ui_timeline_list_takes", "List a clip's take history (clip.versions): id, label, source, " +
2347
+ "provider, model, createdAt, costCredits, durationMs, status, " +
2348
+ "favorite, and which one is active.", z.object({ target: z.string().describe("Clip id or name.") }), async ({ target }) => {
2349
+ const clip = resolveClip(target);
2350
+ const versions = clip.versions ?? [];
2351
+ const activeId = activeTakeIdOf(clip);
2352
+ return {
2353
+ ok: true,
2354
+ takes: versions.map((v) => ({
2355
+ id: v.id,
2356
+ label: v.label,
2357
+ source: v.source,
2358
+ provider: v.provider,
2359
+ model: v.model,
2360
+ createdAt: v.createdAt,
2361
+ costCredits: v.costCredits,
2362
+ durationMs: v.durationMs,
2363
+ status: v.status,
2364
+ favorite: v.favorite,
2365
+ active: v.id === activeId
2366
+ })),
2367
+ activeTakeId: activeId ?? null
2368
+ };
2369
+ }),
2370
+ tool("ui_timeline_select_take", "Make a stored take current on a clip — the same switch " +
2371
+ "restoreVersion does in the editor. Refuses a take that is not " +
2372
+ '`status: "success"`.', z.object({
2373
+ target: z.string().describe("Clip id or name."),
2374
+ takeId: z.string().describe("A take id, from list_takes.")
2375
+ }), async ({ target, takeId }) => {
2376
+ const clip = resolveClip(target);
2377
+ if (clip.mediaType === "model3d") {
2378
+ throw new Error(`Clip "${clip.name}" is a 3D clip — its version history includes ` +
2379
+ "bake renders that must not replace the glTF source. Take " +
2380
+ "selection is not available for model3d clips yet.");
2381
+ }
2382
+ const version = (clip.versions ?? []).find((v) => v.id === takeId);
2383
+ if (!version) {
2384
+ throw new Error(`No take "${takeId}" on "${clip.name}". ` +
2385
+ validUnits((clip.versions ?? []).map((v) => ({
2386
+ id: v.id,
2387
+ name: v.label ?? v.id
2388
+ })), "take"));
2389
+ }
2390
+ if (version.status !== "success") {
2391
+ throw new Error(`Take "${takeId}" on "${clip.name}" did not finish successfully ` +
2392
+ `(status: ${version.status}) — only a successful take can be selected.`);
2393
+ }
2394
+ const next = selectTake(clip, takeId);
2395
+ clips = clips.map((c) => (c.id === clip.id ? next : c));
2396
+ return { ok: true, clip: serializeClip(next) };
2397
+ }),
2398
+ tool("ui_timeline_rename_take", "Set a take's display label.", z.object({
2399
+ target: z.string().describe("Clip id or name."),
2400
+ takeId: z.string().describe("A take id, from list_takes."),
2401
+ label: z.string().describe("The new display label.")
2402
+ }), async ({ target, takeId, label }) => {
2403
+ const clip = resolveClip(target);
2404
+ if (!(clip.versions ?? []).some((v) => v.id === takeId)) {
2405
+ throw new Error(`No take "${takeId}" on "${clip.name}". ` +
2406
+ validUnits((clip.versions ?? []).map((v) => ({
2407
+ id: v.id,
2408
+ name: v.label ?? v.id
2409
+ })), "take"));
2410
+ }
2411
+ const next = renameTakeOnClip(clip, takeId, label);
2412
+ clips = clips.map((c) => (c.id === clip.id ? next : c));
2413
+ return { ok: true, clip: serializeClip(next) };
2414
+ }),
2415
+ tool("ui_timeline_delete_take", "Remove a take from a clip's history. Refuses when `takeId` is the " +
2416
+ "active take — select_take a different take first, then delete the " +
2417
+ "one you no longer want; a clip must always keep at least one take.", z.object({
2418
+ target: z.string().describe("Clip id or name."),
2419
+ takeId: z.string().describe("A take id, from list_takes.")
2420
+ }), async ({ target, takeId }) => {
2421
+ const clip = resolveClip(target);
2422
+ const { clip: next, error } = deleteTakeOnClip(clip, takeId);
2423
+ if (error)
2424
+ throw new Error(error);
2425
+ clips = clips.map((c) => (c.id === clip.id ? next : c));
2426
+ return { ok: true, clip: serializeClip(next) };
2427
+ }),
2428
+ // Subject/object tracks (P0 AI Video, Phase 2). The async provider call
2429
+ // that fills a track's samples is the `track_object` capability, not an
2430
+ // op — these four are the structural half over it, and they carry the
2431
+ // same semantics as `applyTimelineOp`'s own cases so the two hosts cannot
2432
+ // fork (`packages/timeline/src/ops/apply.ts`).
2433
+ tool("ui_timeline_list_tracks", "List the document's subject/object tracks — id, clipId, name, kind, " +
2434
+ "status, source window and sample count. The samples themselves are " +
2435
+ "not returned: a track carries one per frame it covers, which is a " +
2436
+ "wall of numbers no caller reads through a listing.", z.object({
2437
+ target: z
2438
+ .string()
2439
+ .optional()
2440
+ .describe("A clip id or name to filter by. Omit for every track.")
2441
+ }), async ({ target }) => {
2442
+ const clipId = typeof target === "string" && target.trim() !== ""
2443
+ ? resolveClip(target).id
2444
+ : undefined;
2445
+ const listed = mediaTracks.filter((t) => clipId === undefined || t.clipId === clipId);
2446
+ return {
2447
+ ok: true,
2448
+ tracks: listed.map((t) => ({
2449
+ id: t.id,
2450
+ clipId: t.clipId,
2451
+ name: t.name,
2452
+ kind: t.kind,
2453
+ status: t.status,
2454
+ sourceStartMs: t.sourceStartMs,
2455
+ sourceEndMs: t.sourceEndMs,
2456
+ sampleCount: t.samples.length,
2457
+ confidence: t.confidence
2458
+ }))
2459
+ };
2460
+ }),
2461
+ tool("ui_timeline_delete_track_object", "Delete a subject/object track and unbind every clip that was " +
2462
+ "following it — a clip left pointing at a track that is gone reads " +
2463
+ "as bound while following nothing.", z.object({
2464
+ trackId: z.string().describe("A track id, from list_tracks.")
2465
+ }), async ({ trackId }) => {
2466
+ const track = mediaTracks.find((t) => t.id === trackId);
2467
+ if (!track) {
2468
+ throw new Error(`No track "${trackId}". ` + validUnits(mediaTracks, "track"));
2469
+ }
2470
+ mediaTracks = mediaTracks.filter((t) => t.id !== trackId);
2471
+ clips = clips.map((c) => {
2472
+ if (c.trackBinding?.trackId !== trackId)
2473
+ return c;
2474
+ const { trackBinding: _dropped, ...rest } = c;
2475
+ return rest;
2476
+ });
2477
+ return { ok: true, deleted: { id: track.id, name: track.name } };
2478
+ }),
2479
+ tool("ui_timeline_bind_to_track", "Make a clip follow a subject/object track, so its position moves " +
2480
+ 'with the tracked subject. Only "position" and "position_scale" are ' +
2481
+ "implemented; the other modes are named by the document format for a " +
2482
+ "later phase and are refused here rather than silently doing nothing.", z.object({
2483
+ target: z.string().describe("Clip id or name."),
2484
+ trackId: z.string().describe("A track id, from list_tracks."),
2485
+ mode: z
2486
+ .string()
2487
+ .describe('"position" or "position_scale".'),
2488
+ offset: z
2489
+ .object({ x: z.number(), y: z.number() })
2490
+ .optional()
2491
+ .describe("Canvas-pixel offset added on top of the tracked point."),
2492
+ scale: z
2493
+ .number()
2494
+ .optional()
2495
+ .describe('Scale multiplier, "position_scale" only.'),
2496
+ rotationOffset: z
2497
+ .number()
2498
+ .optional()
2499
+ .describe("Radians added to the clip's rotation."),
2500
+ smoothing: z
2501
+ .number()
2502
+ .optional()
2503
+ .describe("0..1; higher follows the track more loosely.")
2504
+ }), async ({ target, trackId, mode, offset, scale, rotationOffset, smoothing }) => {
2505
+ const clip = resolveClip(target);
2506
+ if (!mediaTracks.some((t) => t.id === trackId)) {
2507
+ throw new Error(`No track "${trackId}". ` + validUnits(mediaTracks, "track"));
2508
+ }
2509
+ if (mode !== "position" && mode !== "position_scale") {
2510
+ throw new Error(`bind_to_track mode "${String(mode)}" is not implemented yet — ` +
2511
+ `only "position" and "position_scale" affect rendering in this ` +
2512
+ "build. The field accepts the other modes so a document can " +
2513
+ "name the intent without a schema migration later, but binding " +
2514
+ "to one today would be a no-op, so the call is refused instead.");
2515
+ }
2516
+ const binding = {
2517
+ trackId: trackId,
2518
+ mode: mode
2519
+ };
2520
+ if (offset !== undefined) {
2521
+ binding.offset = offset;
2522
+ }
2523
+ if (scale !== undefined)
2524
+ binding.scale = scale;
2525
+ if (rotationOffset !== undefined) {
2526
+ binding.rotationOffset = rotationOffset;
2527
+ }
2528
+ if (smoothing !== undefined)
2529
+ binding.smoothing = smoothing;
2530
+ const next = { ...clip, trackBinding: binding };
2531
+ clips = clips.map((c) => (c.id === clip.id ? next : c));
2532
+ return { ok: true, clip: serializeClip(next) };
2533
+ }),
2534
+ tool("ui_timeline_unbind_track", "Stop a clip following a track. A clip that follows none is a no-op.", z.object({ target: z.string().describe("Clip id or name.") }), async ({ target }) => {
2535
+ const clip = resolveClip(target);
2536
+ if (!clip.trackBinding)
2537
+ return { ok: true, clip: serializeClip(clip) };
2538
+ const { trackBinding: _dropped, ...next } = clip;
2539
+ clips = clips.map((c) => (c.id === clip.id ? next : c));
2540
+ return { ok: true, clip: serializeClip(next) };
2541
+ }),
2542
+ sharedTool("ui_timeline_retarget_format", async ({ aspect_ratio, strategy, safe_margin, track_ids }) => {
2543
+ if (track_ids) {
2544
+ for (const [clipId, trackId] of Object.entries(track_ids)) {
2545
+ const clip = clips.find((candidate) => candidate.id === clipId);
2546
+ if (!clip) {
2547
+ throw new Error(`track_ids names unknown clip "${clipId}". ` +
2548
+ validUnits(clips, "clip"));
2549
+ }
2550
+ const track = mediaTracks.find((candidate) => candidate.id === trackId);
2551
+ if (!track || track.clipId !== clip.id) {
2552
+ throw new Error(`Track "${trackId}" does not belong to clip "${clip.name}". ` +
2553
+ validUnits(mediaTracks.filter((candidate) => candidate.clipId === clip.id), "track"));
2554
+ }
2555
+ if (track.status !== "ready") {
2556
+ throw new Error(`Track "${track.name}" is ${track.status}; a format adaptation can only follow a ready track.`);
2557
+ }
2558
+ }
2559
+ }
2560
+ const now = new Date().toISOString();
2561
+ const source = {
2562
+ id: sequenceId,
2563
+ projectId,
2564
+ name: sequenceName,
2565
+ fps,
2566
+ width,
2567
+ height,
2568
+ durationMs: clips.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs), 0),
2569
+ tracks: tracks.map((track) => structuredClone(track)),
2570
+ clips: clips.map((clip) => structuredClone(clip)),
2571
+ markers: markers.map((marker) => structuredClone(marker)),
2572
+ mediaTracks: mediaTracks.map((track) => structuredClone(track)),
2573
+ createdAt: now,
2574
+ updatedAt: now
2575
+ };
2576
+ if (seed?.transcript !== undefined) {
2577
+ source.transcript = structuredClone(seed.transcript);
2578
+ }
2579
+ if (seed?.scriptEnabled !== undefined) {
2580
+ source.scriptEnabled = seed.scriptEnabled;
2581
+ }
2582
+ if (seed?.templateId !== undefined)
2583
+ source.templateId = seed.templateId;
2584
+ if (tempo)
2585
+ source.tempo = structuredClone(tempo);
2586
+ source.camera2d = structuredClone(seed?.camera2d ?? null);
2587
+ if (setup)
2588
+ source.setup = structuredClone(setup);
2589
+ const adapted = adaptSequenceFormat(source, aspect_ratio, {
2590
+ strategy: strategy,
2591
+ safeMargin: safe_margin,
2592
+ trackIdByClipId: track_ids
2593
+ });
2594
+ const stored = retargetFormat
2595
+ ? await retargetFormat(structuredClone(adapted.sequence))
2596
+ : { sequenceId: adapted.sequence.id, name: adapted.sequence.name };
2597
+ derivedSequences.push(structuredClone(adapted.sequence));
2598
+ return {
2599
+ ok: true,
2600
+ sourceSequenceId: sequenceId,
2601
+ sequenceId: stored.sequenceId,
2602
+ name: stored.name ?? adapted.sequence.name,
2603
+ width: adapted.sequence.width,
2604
+ height: adapted.sequence.height,
2605
+ strategy,
2606
+ croppedClipIds: adapted.croppedClipIds
2607
+ };
2608
+ }),
2609
+ sharedTool("ui_timeline_set_reframe_subject", async ({ clip_id, track_id, safe_margin, smoothing }) => {
2610
+ const clip = resolveClip(clip_id);
2611
+ const track = mediaTracks.find((candidate) => candidate.id === track_id);
2612
+ if (!track || track.clipId !== clip.id) {
2613
+ throw new Error(`No track "${String(track_id)}" belongs to clip "${clip.name}". ` +
2614
+ validUnits(mediaTracks.filter((candidate) => candidate.clipId === clip.id), "track"));
2615
+ }
2616
+ if (track.status !== "ready") {
2617
+ throw new Error(`Track "${track.name}" is ${track.status}; Smart Reframe can only follow a ready track.`);
2618
+ }
2619
+ if (!mediaTrackCanDriveReframe(track) ||
2620
+ isMediaTrackStale(track, clip)) {
2621
+ throw new Error(`Track "${track.name}" has no current analysis for clip "${clip.name}".`);
2622
+ }
2623
+ const previous = clip.reframe;
2624
+ const reframe = {
2625
+ mode: "track",
2626
+ trackId: track.id,
2627
+ sourceAssetId: clip.currentAssetId
2628
+ };
2629
+ if (safe_margin !== undefined)
2630
+ reframe.safeMargin = safe_margin;
2631
+ else if (previous?.safeMargin !== undefined) {
2632
+ reframe.safeMargin = previous.safeMargin;
2633
+ }
2634
+ if (smoothing !== undefined)
2635
+ reframe.smoothing = smoothing;
2636
+ else if (previous?.smoothing !== undefined) {
2637
+ reframe.smoothing = previous.smoothing;
2638
+ }
2639
+ if (previous?.keyframes) {
2640
+ reframe.keyframes = structuredClone(previous.keyframes);
2641
+ }
2642
+ if (previous?.sourceWidth !== undefined) {
2643
+ reframe.sourceWidth = previous.sourceWidth;
2644
+ }
2645
+ if (previous?.sourceHeight !== undefined) {
2646
+ reframe.sourceHeight = previous.sourceHeight;
2647
+ }
2648
+ const next = { ...clip, reframe };
2649
+ clips = clips.map((candidate) => candidate.id === clip.id ? next : candidate);
2650
+ return { ok: true, clip: serializeClip(next) };
2651
+ }),
2652
+ sharedTool("ui_timeline_add_reframe_keyframe", async ({ clip_id, source_ms, x, y, zoom }) => {
2653
+ const clip = resolveClip(clip_id);
2654
+ if (!clip.reframe) {
2655
+ throw new Error(`Clip "${clip.name}" has no Smart Reframe path. Retarget the format or set_reframe_subject first.`);
2656
+ }
2657
+ const keyframe = {
2658
+ sourceMs: source_ms,
2659
+ x: x,
2660
+ y: y
2661
+ };
2662
+ if (zoom !== undefined) {
2663
+ keyframe.zoom = zoom;
2664
+ }
2665
+ const next = {
2666
+ ...clip,
2667
+ reframe: addReframeKeyframe(clip.reframe, keyframe)
2668
+ };
2669
+ clips = clips.map((candidate) => candidate.id === clip.id ? next : candidate);
2670
+ return { ok: true, clip: serializeClip(next), keyframe };
2671
+ }),
2672
+ sharedTool("ui_timeline_clear_reframe", async ({ clip_id }) => {
2673
+ const clip = resolveClip(clip_id);
2674
+ const next = clearReframe(clip);
2675
+ clips = clips.map((candidate) => candidate.id === clip.id ? next : candidate);
2676
+ return { ok: true, clip: serializeClip(next) };
2677
+ })
2678
+ ];
2679
+ // Opt-in, and outside the `ui_timeline_` prefix on purpose: `edit_timeline`
2680
+ // builds this same bridge and matches its ops by that prefix, so a preview
2681
+ // tool it can never call has no business in its op catalogue. A case that
2682
+ // grades "did it look at the frame" asks for it.
2683
+ if (initial.preview) {
2684
+ tools.push(tool("preview_timeline_frame", "Look at the sequence at one or more timecodes: what is on screen, layered top of the stack first, with each layer's opacity and track index. This surface has no rasterizer, so it reports the layer stack rather than pixels — enough to see a layer that is covered, missing, or drawing nothing. Call it after an edit, before you say you are done.", z.object({
2685
+ times_ms: z
2686
+ .array(z.number().nonnegative())
2687
+ .min(1)
2688
+ .max(8)
2689
+ .describe("Absolute timeline positions to look at, in ms.")
2690
+ }), async ({ times_ms }) => {
2691
+ const frames = times_ms.map((timeMs) => ({
2692
+ time_ms: timeMs,
2693
+ layers: computeActiveLayers(tracks, clips, timeMs)
2694
+ .map((layer) => ({
2695
+ clip_id: layer.clipId,
2696
+ clip_name: clips.find((c) => c.id === layer.clipId)?.name ?? layer.clipId,
2697
+ kind: layer.kind,
2698
+ track_index: layer.trackIndex,
2699
+ z_index: 1000 - layer.trackIndex,
2700
+ opacity: layer.opacity,
2701
+ text: layer.textStyle?.text
2702
+ }))
2703
+ // Top of the stack first, the order the skill's report describes.
2704
+ .sort((a, b) => b.z_index - a.z_index)
2705
+ }));
2706
+ previewTimesMs.push(...times_ms);
2707
+ for (const frame of frames) {
2708
+ for (const layer of frame.layers) {
2709
+ previewedLayerKinds.add(layer.kind);
2710
+ }
2711
+ }
2712
+ return { ok: true, width, height, frames };
2713
+ }));
2714
+ }
2715
+ // Recorded rather than counted: a predicate asks where the last edit sits
2716
+ // relative to a preview, which a tally cannot answer. The push happens
2717
+ // before the call, so a tool that throws is still in the transcript.
2718
+ const recorded = tools.map((entry) => ({
2719
+ ...entry,
2720
+ execute: (args) => {
2721
+ toolLog.push(entry.name);
2722
+ return entry.execute(args);
2723
+ }
2724
+ }));
2725
+ return {
2726
+ tools: recorded,
2727
+ finalState: () => ({
2728
+ fps,
2729
+ width,
2730
+ height,
2731
+ durationMs: clips.reduce((m, c) => Math.max(m, c.startMs + c.durationMs), 0),
2732
+ playheadMs,
2733
+ tracks: tracks.map((t) => ({
2734
+ id: t.id,
2735
+ name: t.name,
2736
+ type: t.type,
2737
+ index: t.index
2738
+ })),
2739
+ clips: clips.map((c) => ({
2740
+ id: c.id,
2741
+ name: c.name,
2742
+ trackId: c.trackId,
2743
+ mediaType: c.mediaType,
2744
+ startMs: c.startMs,
2745
+ durationMs: c.durationMs,
2746
+ prompt: c.prompt,
2747
+ animations: (c.animations ?? []).map((a) => ({
2748
+ role: a.role,
2749
+ preset: a.preset
2750
+ }))
2751
+ })),
2752
+ documentTracks: tracks.map((t) => structuredClone(t)),
2753
+ documentClips: clips.map((c) => structuredClone(c)),
2754
+ markers: markers.map((m) => structuredClone(m)),
2755
+ mediaTracks: mediaTracks.map((t) => structuredClone(t)),
2756
+ derivedSequences: derivedSequences.map((sequence) => structuredClone(sequence)),
2757
+ tempo: tempo ? structuredClone(tempo) : undefined,
2758
+ camera2d: structuredClone(seed?.camera2d ?? null),
2759
+ setup: setup ? structuredClone(setup) : null,
2760
+ transitionCandidates: structuredClone(transitionCandidates),
2761
+ auditionedTransitionCandidateId,
2762
+ appliedTransitionCandidates: structuredClone(appliedTransitionCandidates),
2763
+ toolLog: [...toolLog],
2764
+ previewTimesMs: [...previewTimesMs],
2765
+ previewedLayerKinds: [...previewedLayerKinds]
2766
+ })
2767
+ };
2768
+ }
2769
+ const TIMELINE_SYSTEM_PROMPT = `You are an assistant driving a timeline / video editor through UI tools.
2770
+
2771
+ Use the ui_timeline_* tools to inspect and modify the sequence:
2772
+ - Call ui_timeline_get_state first to see what's already there and get track/clip ids and names.
2773
+ - Add content with ui_timeline_add_text_clip, ui_timeline_add_shape_clip, or ui_timeline_generate_clip; add tracks with ui_timeline_add_track when needed.
2774
+ - Address existing clips by id, name, or "selected" with ui_timeline_split_clip, ui_timeline_trim_clip, ui_timeline_move_clip, ui_timeline_delete_clip, ui_timeline_duplicate_clip, ui_timeline_set_clip_params, ui_timeline_set_clip_binding, ui_timeline_set_transition, ui_timeline_set_time_remap, ui_timeline_animate_clip, ui_timeline_clear_animations, ui_timeline_select_clip.
2775
+ - Use ui_timeline_generatively_edit_clip for a nondestructive video edit. Pass the exact clip_id, an instruction, and an optional provider/model. It captures the clip's playable source window, returns an inactive candidate and generationId, and never changes the active take. After that candidate succeeds, call ui_timeline_apply_take with the same clip_id and the returned take_id to promote it in one undoable action. Do not request extend, object, range, or operation fields: the instruction is the edit target.
2776
+ - Before animating a clip, call ui_timeline_list_animation_presets to discover the exact preset ids, allowed roles, and params.
2777
+ - For motion no preset covers, animate with preset "custom" and pass curves — [{property, keyframes: [{t, value}]}], where t runs 0..1 over the animation window. list_animation_presets reports which properties a curve may drive.
2778
+ - ui_timeline_seek moves the playhead (useful before a playhead-relative split).
2779
+ - Use ui_timeline_list_tracks to inspect source-time subject tracks. ui_timeline_set_reframe_subject makes one ready track drive a clip's crop; ui_timeline_add_reframe_keyframe adds a manual source-time correction, and ui_timeline_clear_reframe removes only that framing state.
2780
+ - Generate a selected-cut transition with ui_timeline_generate_transition_at_cut. It returns an inactive cut-level candidate without changing either source clip. Audition it with ui_timeline_preview_transition_candidate, then explicitly apply it with ui_timeline_apply_transition_at_cut and the returned candidate_id. Never use a normal clip take or ordinary built-in transition as the generated result, and re-read state if the cut may have changed.
2781
+ - ui_timeline_retarget_format creates a new sequence for one target aspect ratio and leaves this sequence unchanged. Run it once per portrait, square, or other adaptation, then continue editing the returned sequence id.
2782
+ - For a played part: add a midi track, place phrases with ui_timeline_add_midi_clip (notes in ticks from the clip's content start, 960 ticks = a quarter note), rewrite them with ui_timeline_set_notes, pick the synth with ui_timeline_set_track_instrument — either a named voice ({"preset": "bass"}: saw-lead, square-lead, soft-pad, pluck, bass, bell) or every field spelled out — and set the speed once with ui_timeline_set_tempo, which rescales the midi clips and leaves picture and audio where they are.
2783
+ - Edit a phrase you already placed without resending it: ui_timeline_transpose_clip moves every note by whole semitones, ui_timeline_quantize_notes snaps onsets to a note grid (1/4, 1/8, 1/16, 1/32, 1/8T, 1/16T; strength below 1 keeps some of the feel) and reports how many notes moved, ui_timeline_scale_velocity multiplies how hard they are struck. get_state reports each midi clip's startBarsBeats, so the next phrase goes on a bar line.
2784
+ - Flag moments with ui_timeline_add_marker / ui_timeline_delete_marker. To cut to music, lay the grid down with ui_timeline_set_markers_from_beats and put clip boundaries on it with ui_timeline_snap_to_beats, then read its per-clip report — a clip further than the tolerance from every beat is left alone and says so.
2785
+
2786
+ - Look at what you made with preview_timeline_frame before you stop.
2787
+
2788
+ Call one tool at a time and use the result before the next call. When the objective is fully satisfied, STOP calling tools and give a one-line summary.
2789
+ ${motionGraphicsSection()}`;
2790
+ /**
2791
+ * The shipped `motion-graphics` skill, verbatim, plus the two lines that
2792
+ * reconcile it with this surface. The eval measures motion the skill teaches,
2793
+ * so the model gets the same document a product agent gets rather than a
2794
+ * paraphrase that drifts from it. Empty when the build ships no skills —
2795
+ * a skill file missing is not a reason to fail every timeline case.
2796
+ */
2797
+ function motionGraphicsSection() {
2798
+ const skill = findSystemSkill("motion-graphics")?.content;
2799
+ if (!skill)
2800
+ return "";
2801
+ return `
2802
+ ---
2803
+
2804
+ The motion-graphics craft, as shipped. It names the capability tools: read \`get_timeline\` as ui_timeline_get_state and every \`edit_timeline\` op as the matching ui_timeline_* tool. \`preview_timeline_frame\` is here and reports the layer stack rather than pixels.
2805
+
2806
+ ${skill}`;
2807
+ }
2808
+ export const TIMELINE_TOOL_LOOP_CASES = [
2809
+ {
2810
+ id: "titles-with-motion",
2811
+ description: "Add a text clip and give it a fade-in entrance animation",
2812
+ objective: "Add a text clip that says 'Hello' and give it a fade-in entrance animation.",
2813
+ createBridge: () => createTimelineToolBridge(),
2814
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
2815
+ expect: {
2816
+ requiredTools: ["ui_timeline_add_text_clip", "ui_timeline_animate_clip"],
2817
+ ordering: [["ui_timeline_add_text_clip", "ui_timeline_animate_clip"]],
2818
+ noErrorResults: true,
2819
+ minToolCalls: 2,
2820
+ maxToolCalls: 12,
2821
+ finalState: [
2822
+ {
2823
+ name: "hasAnimatedTextClip",
2824
+ detail: "no text clip with an 'in' animation",
2825
+ test: (s) => s.clips.some((c) => c.mediaType === "text" &&
2826
+ c.animations.some((a) => a.role === "in"))
2827
+ }
2828
+ ]
2829
+ }
2830
+ },
2831
+ {
2832
+ id: "generate-and-arrange",
2833
+ description: "Generate a video clip and move it to a specific start time",
2834
+ objective: "Add a video track, generate a text-to-video clip on it, then move the clip to start at 2000ms on the timeline.",
2835
+ createBridge: () => createTimelineToolBridge(),
2836
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
2837
+ expect: {
2838
+ requiredTools: ["ui_timeline_generate_clip", "ui_timeline_move_clip"],
2839
+ noErrorResults: true,
2840
+ minToolCalls: 2,
2841
+ maxToolCalls: 12,
2842
+ finalState: [
2843
+ {
2844
+ name: "hasGeneratedVideoClipAt2000",
2845
+ detail: "no generated video clip at startMs=2000 with a prompt",
2846
+ test: (s) => s.clips.some((c) => c.mediaType === "video" && c.startMs === 2000 && !!c.prompt)
2847
+ }
2848
+ ]
2849
+ }
2850
+ },
2851
+ {
2852
+ id: "cut-and-trim",
2853
+ description: "Split a clip and delete the second half",
2854
+ objective: "The timeline has one video clip named 'shot' running from 0ms to 6000ms. Split it at 3000ms and delete the second half.",
2855
+ createBridge: () => createTimelineToolBridge({
2856
+ tracks: [{ type: "video" }],
2857
+ clips: [
2858
+ {
2859
+ name: "shot",
2860
+ trackIndex: 0,
2861
+ mediaType: "video",
2862
+ startMs: 0,
2863
+ durationMs: 6000
2864
+ }
2865
+ ]
2866
+ }),
2867
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
2868
+ userPrompt: "Objective: The timeline has one video clip named 'shot' running from 0ms to 6000ms. Split it at 3000ms and delete the second half.",
2869
+ expect: {
2870
+ requiredTools: ["ui_timeline_split_clip", "ui_timeline_delete_clip"],
2871
+ noErrorResults: true,
2872
+ minToolCalls: 2,
2873
+ maxToolCalls: 12,
2874
+ finalState: [
2875
+ {
2876
+ name: "oneClipLeftAt3000ms",
2877
+ detail: "expected exactly 1 clip with durationMs 3000",
2878
+ test: (s) => s.clips.length === 1 && s.clips[0].durationMs === 3000
2879
+ }
2880
+ ]
2881
+ }
2882
+ },
2883
+ {
2884
+ id: "keyframed-slide",
2885
+ description: "Keyframe an entrance with a custom animation instead of a preset",
2886
+ objective: "Add a text clip that says 'Launch' and give it a keyframed entrance: over the first 800ms it rises 120 pixels into place, from offsetY 120 down to 0. Use a custom animation with explicit keyframes, not a preset.",
2887
+ createBridge: () => createTimelineToolBridge(),
2888
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
2889
+ expect: {
2890
+ requiredTools: [
2891
+ "ui_timeline_add_text_clip",
2892
+ "ui_timeline_animate_clip"
2893
+ ],
2894
+ ordering: [["ui_timeline_add_text_clip", "ui_timeline_animate_clip"]],
2895
+ noErrorResults: true,
2896
+ minToolCalls: 2,
2897
+ maxToolCalls: 12,
2898
+ finalState: [
2899
+ {
2900
+ name: "hasCustomOffsetYCurve",
2901
+ detail: "no text clip carrying a custom 'in' animation whose offsetY curve ends at 0",
2902
+ test: (s) => s.documentClips.some((clip) => {
2903
+ if (clip.mediaType !== "text")
2904
+ return false;
2905
+ const animation = (clip.animations ?? []).find((a) => a.role === "in" && a.preset === "custom");
2906
+ const curve = animation?.custom?.curves.find((c) => c.property === "offsetY");
2907
+ if (!curve)
2908
+ return false;
2909
+ const keyframes = curve.keyframes;
2910
+ const first = keyframes[0];
2911
+ const last = keyframes[keyframes.length - 1];
2912
+ // A rise into place: starts below, lands on the layout
2913
+ // position, and the window is the 800ms that was asked for.
2914
+ return (first.t === 0 &&
2915
+ last.t === 1 &&
2916
+ first.value >= 100 &&
2917
+ last.value === 0 &&
2918
+ animation?.durationMs === 800);
2919
+ })
2920
+ }
2921
+ ]
2922
+ }
2923
+ },
2924
+ {
2925
+ id: "kinetic-title-staggered",
2926
+ description: "Stagger a title's words so the entrance still finishes inside the clip",
2927
+ objective: "Put the title 'MAKE IT MOVE' on screen for 2500ms and have the words arrive one after another instead of all at once. The whole entrance has to be over while the card is still up.",
2928
+ createBridge: () => createTimelineToolBridge({ preview: true }),
2929
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
2930
+ expect: {
2931
+ requiredTools: [
2932
+ "ui_timeline_add_text_clip",
2933
+ "ui_timeline_animate_clip"
2934
+ ],
2935
+ ordering: [["ui_timeline_add_text_clip", "ui_timeline_animate_clip"]],
2936
+ noErrorResults: true,
2937
+ minToolCalls: 2,
2938
+ maxToolCalls: 14,
2939
+ finalState: [
2940
+ {
2941
+ name: "staggerSpanFitsTheCard",
2942
+ detail: "no text clip whose entrance staggers over at least two units and finishes inside the clip",
2943
+ test: (s) => s.documentClips.some((clip) => {
2944
+ if (clip.mediaType !== "text")
2945
+ return false;
2946
+ const entrance = (clip.animations ?? []).find((a) => a.role === "in" && a.stagger);
2947
+ const stagger = entrance?.stagger;
2948
+ if (!entrance || !stagger)
2949
+ return false;
2950
+ return (staggerUnitsOf(clip, stagger.unit, s) >= 2 &&
2951
+ staggerSpanFitsClip(clip, entrance, s));
2952
+ })
2953
+ }
2954
+ ]
2955
+ }
2956
+ },
2957
+ {
2958
+ id: "lower-third-layered",
2959
+ description: "Put a scrim behind a name plate and keep both inside the shot",
2960
+ objective: "The shot named 'Host' runs from 0ms to 6000ms. While it is on screen, put the name 'Maya Chen' on the picture with a dark bar behind the words so they stay readable against the shot. Both have to sit inside that shot's window.",
2961
+ createBridge: () => createTimelineToolBridge({
2962
+ preview: true,
2963
+ tracks: [{ type: "video", name: "Picture" }],
2964
+ clips: [
2965
+ {
2966
+ name: "Host",
2967
+ trackIndex: 0,
2968
+ mediaType: "video",
2969
+ startMs: 0,
2970
+ durationMs: 6000
2971
+ }
2972
+ ]
2973
+ }),
2974
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
2975
+ expect: {
2976
+ requiredTools: [
2977
+ "ui_timeline_add_text_clip",
2978
+ "ui_timeline_add_shape_clip"
2979
+ ],
2980
+ noErrorResults: true,
2981
+ minToolCalls: 2,
2982
+ maxToolCalls: 14,
2983
+ finalState: [
2984
+ {
2985
+ name: "scrimBehindTextInsideTheShot",
2986
+ detail: "no shape clip drawn over the picture and under the text, sharing frames with it inside 0-6000ms",
2987
+ test: (s) => {
2988
+ const indexOf = (trackId) => s.tracks.find((t) => t.id === trackId)?.index ?? -1;
2989
+ const inShot = (c) => c.startMs >= 0 && c.startMs + c.durationMs <= 6000;
2990
+ const overlaps = (a, b) => a.startMs < b.startMs + b.durationMs &&
2991
+ b.startMs < a.startMs + a.durationMs;
2992
+ const texts = s.clips.filter((c) => c.mediaType === "text" && inShot(c));
2993
+ const shapes = s.clips.filter((c) => c.mediaType === "shape" && inShot(c));
2994
+ const picture = s.clips.filter((c) => c.mediaType === "video");
2995
+ // Lowest index draws on top, so the scrim sits between the two:
2996
+ // over the shot it darkens, under the words it backs. A scrim
2997
+ // that never shares a frame with the text backs nothing.
2998
+ return texts.some((text) => shapes.some((shape) => indexOf(shape.trackId) > indexOf(text.trackId) &&
2999
+ overlaps(shape, text) &&
3000
+ picture.some((shot) => indexOf(shot.trackId) > indexOf(shape.trackId))));
3001
+ }
3002
+ }
3003
+ ]
3004
+ }
3005
+ },
3006
+ {
3007
+ id: "entrance-decelerates",
3008
+ description: "Every entrance eases out or springs, never accelerates in",
3009
+ objective: "Add two title cards, 'Chapter One' and 'Chapter Two', and bring each one on so it arrives and settles rather than speeding up as it lands.",
3010
+ createBridge: () => createTimelineToolBridge({ preview: true }),
3011
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
3012
+ expect: {
3013
+ requiredTools: ["ui_timeline_animate_clip"],
3014
+ noErrorResults: true,
3015
+ minToolCalls: 2,
3016
+ maxToolCalls: 16,
3017
+ finalState: [
3018
+ {
3019
+ name: "everyEntranceDecelerates",
3020
+ detail: "an 'in' animation runs on an easing that is neither an ease-out family nor a spring",
3021
+ test: (s) => {
3022
+ const entrances = s.documentClips.flatMap((clip) => (clip.animations ?? []).filter((a) => a.role === "in"));
3023
+ return (entrances.length >= 2 &&
3024
+ entrances.every((a) => easingDecelerates(effectiveEasing(a))));
3025
+ }
3026
+ }
3027
+ ]
3028
+ }
3029
+ },
3030
+ {
3031
+ id: "beat-cut",
3032
+ description: "Move every picture boundary onto a named musical onset",
3033
+ objective: "The music hits at 0ms, 2000ms, 4000ms and 6000ms. My three shots — A, B and C — are roughly laid out and none of the cuts land on those hits. Put every cut on a hit, keeping the shots back to back with no gap.",
3034
+ createBridge: () => createTimelineToolBridge({
3035
+ preview: true,
3036
+ tracks: [{ type: "video", name: "Picture" }],
3037
+ clips: [
3038
+ {
3039
+ name: "A",
3040
+ trackIndex: 0,
3041
+ mediaType: "video",
3042
+ startMs: 0,
3043
+ durationMs: 2180
3044
+ },
3045
+ {
3046
+ name: "B",
3047
+ trackIndex: 0,
3048
+ mediaType: "video",
3049
+ startMs: 2180,
3050
+ durationMs: 2080
3051
+ },
3052
+ {
3053
+ name: "C",
3054
+ trackIndex: 0,
3055
+ mediaType: "video",
3056
+ startMs: 4260,
3057
+ durationMs: 1740
3058
+ }
3059
+ ]
3060
+ }),
3061
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
3062
+ expect: {
3063
+ noErrorResults: true,
3064
+ minToolCalls: 1,
3065
+ maxToolCalls: 16,
3066
+ finalState: [
3067
+ {
3068
+ name: "everyBoundaryOnAnOnset",
3069
+ detail: "a picture clip's start or end is further than 60ms from 0/2000/4000/6000ms",
3070
+ test: (s) => {
3071
+ const onsets = [0, 2000, 4000, 6000];
3072
+ const onBeat = (ms) => onsets.some((onset) => Math.abs(ms - onset) <= 60);
3073
+ const picture = s.clips.filter((c) => c.mediaType === "video");
3074
+ return (picture.length === 3 &&
3075
+ picture.every((c) => onBeat(c.startMs) && onBeat(c.startMs + c.durationMs)));
3076
+ }
3077
+ }
3078
+ ]
3079
+ }
3080
+ },
3081
+ {
3082
+ id: "looked-before-done",
3083
+ description: "Check the frame after the last edit, before reporting done",
3084
+ objective: "Add an end card that says 'END' starting at 4000ms, then look at what is actually on screen there before you tell me it is finished.",
3085
+ createBridge: () => createTimelineToolBridge({ preview: true }),
3086
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
3087
+ expect: {
3088
+ requiredTools: ["ui_timeline_add_text_clip", "preview_timeline_frame"],
3089
+ noErrorResults: true,
3090
+ minToolCalls: 2,
3091
+ maxToolCalls: 12,
3092
+ finalState: [
3093
+ {
3094
+ name: "previewedAfterTheLastEdit",
3095
+ detail: "the run's last edit is not followed by a preview_timeline_frame call",
3096
+ test: (s) => s.clips.some((c) => c.mediaType === "text") &&
3097
+ previewedAfterLastEdit(s.toolLog) &&
3098
+ previewedMidMotion(s.previewTimesMs, s.documentClips, s)
3099
+ }
3100
+ ]
3101
+ }
3102
+ },
3103
+ {
3104
+ id: "video-flow-plan-then-generate",
3105
+ description: "Plan a video's beats, then cut it — the guided flow through the tools",
3106
+ objective: "This is a 15-second ad for a paper boat. Write the brief onto the sequence, plan four beats — the second and fourth each carry a voiceover line — then generate the video from that plan with model 'nodetool/kling-turbo' and voice 'alloy'.",
3107
+ createBridge: () => createTimelineToolBridge(),
3108
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
3109
+ expect: {
3110
+ requiredTools: [
3111
+ "ui_timeline_plan_beats",
3112
+ "ui_timeline_generate_from_beats"
3113
+ ],
3114
+ noErrorResults: true,
3115
+ minToolCalls: 2,
3116
+ maxToolCalls: 12,
3117
+ finalState: [
3118
+ {
3119
+ name: "onePictureClipPerBeat",
3120
+ detail: "the picture track does not hold exactly one clip per planned beat",
3121
+ test: (s) => (s.setup?.beats?.length ?? 0) > 0 &&
3122
+ s.clips.filter((c) => c.mediaType === "video").length ===
3123
+ (s.setup?.beats?.length ?? 0)
3124
+ },
3125
+ {
3126
+ name: "oneVoiceoverClipPerVoicedBeat",
3127
+ detail: "the voiceover clips do not match the beats that carry a line",
3128
+ test: (s) => {
3129
+ const voiced = (s.setup?.beats ?? []).filter((beat) => (beat.voiceover ?? "").trim().length > 0).length;
3130
+ const audio = s.documentClips.filter((c) => c.bindingKind === "text-to-audio" && c.beatId).length;
3131
+ return voiced > 0 && audio === voiced;
3132
+ }
3133
+ },
3134
+ {
3135
+ name: "flowFinished",
3136
+ detail: "the flow's stage is not done after generating",
3137
+ test: (s) => s.setup?.stage === "done"
3138
+ }
3139
+ ]
3140
+ }
3141
+ },
3142
+ {
3143
+ id: "three-d-turntable",
3144
+ description: "Put a glTF on the timeline, spin its camera, then look",
3145
+ objective: "Put the 3D model asset 'asset_lantern_glb' on an overlay track for 4 seconds, make its camera go all the way round the model over that time, and then look at the frame in the middle to check the model is actually on screen.",
3146
+ createBridge: () => createTimelineToolBridge({ preview: true }),
3147
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
3148
+ expect: {
3149
+ requiredTools: [
3150
+ "ui_timeline_add_model3d_clip",
3151
+ "ui_timeline_animate_clip",
3152
+ "preview_timeline_frame"
3153
+ ],
3154
+ noErrorResults: true,
3155
+ minToolCalls: 3,
3156
+ maxToolCalls: 14,
3157
+ finalState: [
3158
+ {
3159
+ name: "modelClipCarriesAnOrbit",
3160
+ detail: "no model3d clip holding the asset with a loop animation driving its camera",
3161
+ test: (s) => s.documentClips.some((clip) => clip.mediaType === "model3d" &&
3162
+ clip.currentAssetId === "asset_lantern_glb" &&
3163
+ clip.model3dStyle !== undefined &&
3164
+ (clip.animations ?? []).some((a) => a.role === "loop"))
3165
+ },
3166
+ {
3167
+ // The document can say a 3D clip exists; only the report says one
3168
+ // was drawn — which is the whole point of looking before stopping.
3169
+ name: "previewReportedTheModel",
3170
+ detail: "no preview reported a model3d layer",
3171
+ test: (s) => s.previewedLayerKinds.includes("model3d")
3172
+ }
3173
+ ]
3174
+ }
3175
+ },
3176
+ {
3177
+ id: "trimmed-video-edit-candidate-apply",
3178
+ description: "Edit a trimmed source into an inactive candidate, inspect it, then apply it",
3179
+ objective: "The timeline contains a trimmed imported video clip. Edit that exact clip to make the station deserted at night, inspect the inactive candidate, then explicitly apply the returned take while keeping the current cut.",
3180
+ createBridge: () => createTimelineToolBridge({
3181
+ sequenceId: "sequence-trimmed-edit",
3182
+ sequence: {
3183
+ tracks: [
3184
+ {
3185
+ id: "track-video",
3186
+ name: "Video",
3187
+ type: "video",
3188
+ index: 0,
3189
+ visible: true,
3190
+ locked: false
3191
+ }
3192
+ ],
3193
+ clips: [
3194
+ {
3195
+ id: "clip-trimmed",
3196
+ trackId: "track-video",
3197
+ name: "Station",
3198
+ startMs: 1200,
3199
+ durationMs: 4000,
3200
+ inPointMs: 40000,
3201
+ outPointMs: 44000,
3202
+ mediaType: "video",
3203
+ sourceType: "imported",
3204
+ status: "generated",
3205
+ locked: false,
3206
+ currentAssetId: "asset-original",
3207
+ activeTakeId: "take-original",
3208
+ versions: [
3209
+ {
3210
+ id: "take-original",
3211
+ createdAt: "2026-01-01T00:00:00.000Z",
3212
+ workflowUpdatedAt: "2026-01-01T00:00:00.000Z",
3213
+ jobId: "original-job",
3214
+ assetId: "asset-original",
3215
+ dependencyHash: "",
3216
+ paramOverridesSnapshot: {},
3217
+ durationMs: 4000,
3218
+ status: "success",
3219
+ source: "imported",
3220
+ sourceMapping: {
3221
+ inPointMs: 40000,
3222
+ outPointMs: 44000,
3223
+ speedMultiplier: 1,
3224
+ speedBaked: false
3225
+ }
3226
+ }
3227
+ ]
3228
+ }
3229
+ ]
3230
+ }
3231
+ }),
3232
+ systemPrompt: TIMELINE_SYSTEM_PROMPT,
3233
+ expect: {
3234
+ requiredTools: [
3235
+ "ui_timeline_generatively_edit_clip",
3236
+ "ui_timeline_list_takes",
3237
+ "ui_timeline_apply_take"
3238
+ ],
3239
+ ordering: [
3240
+ ["ui_timeline_generatively_edit_clip", "ui_timeline_list_takes"],
3241
+ ["ui_timeline_list_takes", "ui_timeline_apply_take"]
3242
+ ],
3243
+ noErrorResults: true,
3244
+ minToolCalls: 3,
3245
+ maxToolCalls: 8,
3246
+ finalState: [
3247
+ {
3248
+ name: "appliedEditPreservesTheTimelineCut",
3249
+ detail: "the edit candidate was not applied to the trimmed clip with its timeline placement intact",
3250
+ test: (s) => {
3251
+ const clip = s.documentClips.find((candidate) => candidate.id === "clip-trimmed");
3252
+ const active = clip?.versions?.find((take) => take.id === clip.activeTakeId);
3253
+ return (clip?.startMs === 1200 &&
3254
+ clip.durationMs === 4000 &&
3255
+ clip.currentAssetId?.startsWith("generative://clip-trimmed/") ===
3256
+ true &&
3257
+ clip.inPointMs === 0 &&
3258
+ clip.outPointMs === 4000 &&
3259
+ active?.mediaEdit?.sourceContext.sourceStartMs === 40000 &&
3260
+ active.mediaEdit.sourceContext.sourceEndMs === 44000);
3261
+ }
3262
+ }
3263
+ ]
3264
+ }
3265
+ }
3266
+ ];
3267
+ //# sourceMappingURL=timeline.js.map