@hybridlabor-api/aos 4.4.1 → 4.4.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/.agents/skills/firecrawl/SKILL.md +149 -0
  2. package/.agents/skills/firecrawl/rules/install.md +82 -0
  3. package/.agents/skills/firecrawl/rules/security.md +26 -0
  4. package/.agents/skills/firecrawl-agent/SKILL.md +58 -0
  5. package/.agents/skills/firecrawl-build/SKILL.md +39 -0
  6. package/.agents/skills/firecrawl-build-interact/SKILL.md +68 -0
  7. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +103 -0
  8. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +39 -0
  9. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +20 -0
  10. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +17 -0
  11. package/.agents/skills/firecrawl-build-scrape/SKILL.md +69 -0
  12. package/.agents/skills/firecrawl-build-search/SKILL.md +69 -0
  13. package/.agents/skills/firecrawl-crawl/SKILL.md +59 -0
  14. package/.agents/skills/firecrawl-download/SKILL.md +70 -0
  15. package/.agents/skills/firecrawl-interact/SKILL.md +84 -0
  16. package/.agents/skills/firecrawl-map/SKILL.md +51 -0
  17. package/.agents/skills/firecrawl-scrape/SKILL.md +69 -0
  18. package/.agents/skills/firecrawl-search/SKILL.md +60 -0
  19. package/CLAUDE.md +573 -0
  20. package/README.de.md +0 -8
  21. package/README.md +0 -8
  22. package/README.pt.md +0 -8
  23. package/assets/brand/ao-ant-mark.svg +91 -0
  24. package/assets/brand/bdb-core-mark.svg +34 -0
  25. package/assets/brand/heimdall-guardian-mark.svg +16 -0
  26. package/assets/brand/memb-crystal-mark.svg +18 -0
  27. package/bin/aos-config.mjs +89 -0
  28. package/bin/aos-dashboard.mjs +608 -0
  29. package/bin/aos-uninstall.mjs +227 -0
  30. package/docs/sessions/audit-2026-09-14-silent-failures.md +114 -0
  31. package/docs/skills_table.md +0 -3
  32. package/installer.js +387 -237
  33. package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +10 -0
  34. package/mcps/after-effects-mcp/build/index.js +840 -0
  35. package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +153 -0
  36. package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +218 -0
  37. package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +71 -0
  38. package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +147 -0
  39. package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +114 -0
  40. package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +115 -0
  41. package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +192 -0
  42. package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +90 -0
  43. package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +50 -0
  44. package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +1773 -0
  45. package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +160 -0
  46. package/mcps/computer-use-mcp/dist/client.d.ts +150 -0
  47. package/mcps/computer-use-mcp/dist/client.js +136 -0
  48. package/mcps/computer-use-mcp/dist/entrypoint.d.ts +16 -0
  49. package/mcps/computer-use-mcp/dist/entrypoint.js +26 -0
  50. package/mcps/computer-use-mcp/dist/native.d.ts +212 -0
  51. package/mcps/computer-use-mcp/dist/native.js +50 -0
  52. package/mcps/computer-use-mcp/dist/server.d.ts +32 -0
  53. package/mcps/computer-use-mcp/dist/server.js +342 -0
  54. package/mcps/computer-use-mcp/dist/session.d.ts +101 -0
  55. package/mcps/computer-use-mcp/dist/session.js +2372 -0
  56. package/package.json +13 -4
  57. package/skills/global_config/aos-project-init/SKILL.md +97 -130
  58. package/skills/global_config/aos-project-init/assets/openwikiignore.template +62 -0
  59. package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +38 -1
  60. package/skills/global_config/aos-setup/SKILL.md +98 -8
  61. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +21 -2
  62. package/skills/global_config/ask-tim/SKILL.md +2 -7
  63. package/skills/global_config/openwiki-skill/scripts/openwiki_daemon.py +65 -12
  64. package/bin/setup-saas.mjs +0 -792
  65. package/mcps/blender-mcp-server/.dockerignore +0 -19
  66. package/mcps/blender-mcp-server/.github/workflows/ci.yml +0 -67
  67. package/mcps/blender-mcp-server/.github/workflows/publish-pypi.yml +0 -54
  68. package/mcps/blender-mcp-server/CHANGELOG.md +0 -64
  69. package/mcps/blender-mcp-server/CONTRIBUTING.md +0 -107
  70. package/mcps/blender-mcp-server/Dockerfile +0 -19
  71. package/mcps/blender-mcp-server/LICENSE +0 -21
  72. package/mcps/blender-mcp-server/README.md +0 -409
  73. package/mcps/blender-mcp-server/addon/__init__.py +0 -1440
  74. package/mcps/blender-mcp-server/addon/models.py +0 -223
  75. package/mcps/blender-mcp-server/pyproject.toml +0 -120
  76. package/mcps/blender-mcp-server/scripts/blender_bridge_request.py +0 -71
  77. package/mcps/blender-mcp-server/scripts/blender_create_test_cube.py +0 -49
  78. package/mcps/blender-mcp-server/scripts/blender_scene_info.py +0 -38
  79. package/mcps/blender-mcp-server/scripts/build_addon_zip.sh +0 -29
  80. package/mcps/blender-mcp-server/scripts/demos/README.md +0 -186
  81. package/mcps/blender-mcp-server/scripts/demos/dam_break_scene.py +0 -276
  82. package/mcps/blender-mcp-server/scripts/demos/open_dam_break_gui.py +0 -33
  83. package/mcps/blender-mcp-server/scripts/demos/pipe_movement_study.py +0 -518
  84. package/mcps/blender-mcp-server/scripts/demos/pipe_pressure_failure_scene.py +0 -806
  85. package/mcps/blender-mcp-server/scripts/demos/procedural_dam_break_scene.py +0 -600
  86. package/mcps/blender-mcp-server/scripts/demos/run_dam_break.py +0 -510
  87. package/mcps/blender-mcp-server/scripts/launch_blender_gui.py +0 -41
  88. package/mcps/blender-mcp-server/scripts/library/README.md +0 -64
  89. package/mcps/blender-mcp-server/scripts/library/apply_transforms.py +0 -46
  90. package/mcps/blender-mcp-server/scripts/library/camera.py +0 -51
  91. package/mcps/blender-mcp-server/scripts/library/collections.py +0 -65
  92. package/mcps/blender-mcp-server/scripts/library/create_mesh.py +0 -41
  93. package/mcps/blender-mcp-server/scripts/library/effector.py +0 -48
  94. package/mcps/blender-mcp-server/scripts/library/fluid_domain.py +0 -80
  95. package/mcps/blender-mcp-server/scripts/library/fluid_inflow.py +0 -98
  96. package/mcps/blender-mcp-server/scripts/library/frame_range.py +0 -35
  97. package/mcps/blender-mcp-server/scripts/library/keyframes.py +0 -59
  98. package/mcps/blender-mcp-server/scripts/library/rigid_body.py +0 -52
  99. package/mcps/blender-mcp-server/scripts/library/save_blend.py +0 -40
  100. package/mcps/blender-mcp-server/scripts/server_start.sh +0 -26
  101. package/mcps/blender-mcp-server/src/blender_mcp_server/__init__.py +0 -3
  102. package/mcps/blender-mcp-server/src/blender_mcp_server/headless.py +0 -330
  103. package/mcps/blender-mcp-server/src/blender_mcp_server/server.py +0 -606
  104. package/mcps/davinci-mcp-professional/.bandit +0 -35
  105. package/mcps/davinci-mcp-professional/.claude/commands/security-review.md +0 -191
  106. package/mcps/davinci-mcp-professional/.claude/settings.json +0 -1
  107. package/mcps/davinci-mcp-professional/.editorconfig +0 -27
  108. package/mcps/davinci-mcp-professional/.gitattributes +0 -207
  109. package/mcps/davinci-mcp-professional/.github/CODEOWNERS +0 -2
  110. package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/bug_report.md +0 -33
  111. package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  112. package/mcps/davinci-mcp-professional/.github/PULL_REQUEST_TEMPLATE.md +0 -20
  113. package/mcps/davinci-mcp-professional/.github/SECURITY.md +0 -97
  114. package/mcps/davinci-mcp-professional/.github/dependabot.yml +0 -27
  115. package/mcps/davinci-mcp-professional/.github/workflows/ci.yml +0 -39
  116. package/mcps/davinci-mcp-professional/ATTRIBUTION.md +0 -4
  117. package/mcps/davinci-mcp-professional/AUTHORS.md +0 -9
  118. package/mcps/davinci-mcp-professional/BUGS.md +0 -55
  119. package/mcps/davinci-mcp-professional/CLAUDE.md +0 -229
  120. package/mcps/davinci-mcp-professional/CONTRIBUTING.md +0 -51
  121. package/mcps/davinci-mcp-professional/COPYING.md +0 -228
  122. package/mcps/davinci-mcp-professional/Doxyfile +0 -379
  123. package/mcps/davinci-mcp-professional/README.md +0 -143
  124. package/mcps/davinci-mcp-professional/SECURITY.md +0 -40
  125. package/mcps/davinci-mcp-professional/USING.md +0 -301
  126. package/mcps/davinci-mcp-professional/VERSION.md +0 -13
  127. package/mcps/davinci-mcp-professional/build.py +0 -169
  128. package/mcps/davinci-mcp-professional/claude_desktop_config_template.json +0 -11
  129. package/mcps/davinci-mcp-professional/hooks/ci-check.sh +0 -29
  130. package/mcps/davinci-mcp-professional/hooks/pre-push +0 -46
  131. package/mcps/davinci-mcp-professional/main.py +0 -40
  132. package/mcps/davinci-mcp-professional/mcp_server.py +0 -30
  133. package/mcps/davinci-mcp-professional/pyproject.toml +0 -115
  134. package/mcps/davinci-mcp-professional/security_audit.py +0 -379
  135. package/mcps/davinci-mcp-professional/src/davinci_mcp/__init__.py +0 -19
  136. package/mcps/davinci-mcp-professional/src/davinci_mcp/cli.py +0 -138
  137. package/mcps/davinci-mcp-professional/src/davinci_mcp/resolve_client.py +0 -342
  138. package/mcps/davinci-mcp-professional/src/davinci_mcp/resources/__init__.py +0 -60
  139. package/mcps/davinci-mcp-professional/src/davinci_mcp/server.py +0 -185
  140. package/mcps/davinci-mcp-professional/src/davinci_mcp/tools/__init__.py +0 -146
  141. package/mcps/davinci-mcp-professional/src/davinci_mcp/types.py +0 -125
  142. package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/__init__.py +0 -19
  143. package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/platform.py +0 -239
  144. package/mcps/davinci-mcp-professional/test.py +0 -186
  145. package/mcps/davinci-mcp-professional/uv.lock +0 -2073
  146. package/mcps/davinci-resolve-mcp/.claude/skills/media-analysis.md +0 -44
  147. package/mcps/davinci-resolve-mcp/.clinerules +0 -17
  148. package/mcps/davinci-resolve-mcp/.cursorrules +0 -17
  149. package/mcps/davinci-resolve-mcp/.github/copilot-instructions.md +0 -17
  150. package/mcps/davinci-resolve-mcp/.github/workflows/npm-publish.yml +0 -109
  151. package/mcps/davinci-resolve-mcp/.windsurfrules +0 -17
  152. package/mcps/davinci-resolve-mcp/AGENTS.md +0 -114
  153. package/mcps/davinci-resolve-mcp/CHANGELOG.md +0 -2648
  154. package/mcps/davinci-resolve-mcp/CLAUDE.md +0 -15
  155. package/mcps/davinci-resolve-mcp/LICENSE +0 -21
  156. package/mcps/davinci-resolve-mcp/README.md +0 -173
  157. package/mcps/davinci-resolve-mcp/SECURITY.md +0 -53
  158. package/mcps/davinci-resolve-mcp/bin/davinci-resolve-mcp.mjs +0 -450
  159. package/mcps/davinci-resolve-mcp/examples/README.md +0 -53
  160. package/mcps/davinci-resolve-mcp/examples/markers/README.md +0 -81
  161. package/mcps/davinci-resolve-mcp/examples/media/README.md +0 -94
  162. package/mcps/davinci-resolve-mcp/examples/timeline/README.md +0 -98
  163. package/mcps/davinci-resolve-mcp/install.py +0 -1687
  164. package/mcps/davinci-resolve-mcp/package.json +0 -52
  165. package/mcps/davinci-resolve-mcp/release-notes/v2.22.0.md +0 -51
  166. package/mcps/davinci-resolve-mcp/release-notes/v2.23.0.md +0 -51
  167. package/mcps/davinci-resolve-mcp/release-notes/v2.23.1.md +0 -35
  168. package/mcps/davinci-resolve-mcp/release-notes/v2.24.1.md +0 -32
  169. package/mcps/davinci-resolve-mcp/scripts/audit_api_parity.py +0 -275
  170. package/mcps/davinci-resolve-mcp/scripts/audit_readwrite_symmetry.py +0 -80
  171. package/mcps/davinci-resolve-mcp/scripts/doctor.py +0 -227
  172. package/mcps/davinci-resolve-mcp/scripts/gen_api_limitations.py +0 -144
  173. package/mcps/davinci-resolve-mcp/scripts/live_media_analysis_polish_probe.py +0 -65
  174. package/mcps/davinci-resolve-mcp/scripts/measure_bridge_cost.py +0 -65
  175. package/mcps/davinci-resolve-mcp/scripts/regen_panel_screenshots.py +0 -76
  176. package/mcps/davinci-resolve-mcp/src/__init__.py +0 -3
  177. package/mcps/davinci-resolve-mcp/src/analysis_dashboard.py +0 -15505
  178. package/mcps/davinci-resolve-mcp/src/batch_cli.py +0 -540
  179. package/mcps/davinci-resolve-mcp/src/control_panel.py +0 -13
  180. package/mcps/davinci-resolve-mcp/src/granular/__init__.py +0 -17
  181. package/mcps/davinci-resolve-mcp/src/granular/common.py +0 -741
  182. package/mcps/davinci-resolve-mcp/src/granular/folder.py +0 -420
  183. package/mcps/davinci-resolve-mcp/src/granular/gallery.py +0 -306
  184. package/mcps/davinci-resolve-mcp/src/granular/graph.py +0 -309
  185. package/mcps/davinci-resolve-mcp/src/granular/media_pool.py +0 -679
  186. package/mcps/davinci-resolve-mcp/src/granular/media_pool_item.py +0 -1020
  187. package/mcps/davinci-resolve-mcp/src/granular/media_storage.py +0 -179
  188. package/mcps/davinci-resolve-mcp/src/granular/project.py +0 -1654
  189. package/mcps/davinci-resolve-mcp/src/granular/resolve_control.py +0 -538
  190. package/mcps/davinci-resolve-mcp/src/granular/timeline.py +0 -1075
  191. package/mcps/davinci-resolve-mcp/src/granular/timeline_item.py +0 -2251
  192. package/mcps/davinci-resolve-mcp/src/resolve_mcp_server.py +0 -43
  193. package/mcps/davinci-resolve-mcp/src/server.py +0 -24116
  194. package/mcps/davinci-resolve-mcp/src/utils/__init__.py +0 -3
  195. package/mcps/davinci-resolve-mcp/src/utils/actor_identity.py +0 -46
  196. package/mcps/davinci-resolve-mcp/src/utils/analysis_caps.py +0 -678
  197. package/mcps/davinci-resolve-mcp/src/utils/analysis_memory.py +0 -755
  198. package/mcps/davinci-resolve-mcp/src/utils/analysis_runs.py +0 -307
  199. package/mcps/davinci-resolve-mcp/src/utils/analysis_store.py +0 -945
  200. package/mcps/davinci-resolve-mcp/src/utils/api_truth.py +0 -525
  201. package/mcps/davinci-resolve-mcp/src/utils/app_control.py +0 -319
  202. package/mcps/davinci-resolve-mcp/src/utils/audio_fairlight_live_probe.py +0 -263
  203. package/mcps/davinci-resolve-mcp/src/utils/brain_edits.py +0 -387
  204. package/mcps/davinci-resolve-mcp/src/utils/bridge_metrics.py +0 -76
  205. package/mcps/davinci-resolve-mcp/src/utils/cdl.py +0 -20
  206. package/mcps/davinci-resolve-mcp/src/utils/clip_query.py +0 -85
  207. package/mcps/davinci-resolve-mcp/src/utils/cloud_operations.py +0 -192
  208. package/mcps/davinci-resolve-mcp/src/utils/color_grade_live_probe.py +0 -444
  209. package/mcps/davinci-resolve-mcp/src/utils/contracts.py +0 -102
  210. package/mcps/davinci-resolve-mcp/src/utils/cut_ir.py +0 -115
  211. package/mcps/davinci-resolve-mcp/src/utils/dctl_templates.py +0 -368
  212. package/mcps/davinci-resolve-mcp/src/utils/deep_vision.py +0 -643
  213. package/mcps/davinci-resolve-mcp/src/utils/destructive_hook.py +0 -606
  214. package/mcps/davinci-resolve-mcp/src/utils/edit_engine.py +0 -737
  215. package/mcps/davinci-resolve-mcp/src/utils/embeddings.py +0 -922
  216. package/mcps/davinci-resolve-mcp/src/utils/entities.py +0 -579
  217. package/mcps/davinci-resolve-mcp/src/utils/extension_authoring_live_probe.py +0 -292
  218. package/mcps/davinci-resolve-mcp/src/utils/failure_tracker.py +0 -119
  219. package/mcps/davinci-resolve-mcp/src/utils/fuse_templates.py +0 -1968
  220. package/mcps/davinci-resolve-mcp/src/utils/fusion_composition_live_probe.py +0 -284
  221. package/mcps/davinci-resolve-mcp/src/utils/fusion_group_settings.py +0 -334
  222. package/mcps/davinci-resolve-mcp/src/utils/layout_presets.py +0 -333
  223. package/mcps/davinci-resolve-mcp/src/utils/mcp_stdio.py +0 -32
  224. package/mcps/davinci-resolve-mcp/src/utils/mcp_transport.py +0 -128
  225. package/mcps/davinci-resolve-mcp/src/utils/media_analysis.py +0 -7690
  226. package/mcps/davinci-resolve-mcp/src/utils/media_analysis_jobs.py +0 -805
  227. package/mcps/davinci-resolve-mcp/src/utils/media_pool_changes.py +0 -121
  228. package/mcps/davinci-resolve-mcp/src/utils/media_pool_ingest_live_probe.py +0 -592
  229. package/mcps/davinci-resolve-mcp/src/utils/multicam.py +0 -393
  230. package/mcps/davinci-resolve-mcp/src/utils/object_inspection.py +0 -301
  231. package/mcps/davinci-resolve-mcp/src/utils/page_lock.py +0 -75
  232. package/mcps/davinci-resolve-mcp/src/utils/platform.py +0 -157
  233. package/mcps/davinci-resolve-mcp/src/utils/proc.py +0 -26
  234. package/mcps/davinci-resolve-mcp/src/utils/project_cleanup.py +0 -73
  235. package/mcps/davinci-resolve-mcp/src/utils/project_lifecycle_live_probe.py +0 -376
  236. package/mcps/davinci-resolve-mcp/src/utils/project_lint.py +0 -131
  237. package/mcps/davinci-resolve-mcp/src/utils/project_properties.py +0 -601
  238. package/mcps/davinci-resolve-mcp/src/utils/project_spec.py +0 -459
  239. package/mcps/davinci-resolve-mcp/src/utils/readback.py +0 -91
  240. package/mcps/davinci-resolve-mcp/src/utils/render_deliver_live_probe.py +0 -384
  241. package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_governance.py +0 -214
  242. package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_ledger.py +0 -244
  243. package/mcps/davinci-resolve-mcp/src/utils/resolve_busy.py +0 -171
  244. package/mcps/davinci-resolve-mcp/src/utils/resolve_connection.py +0 -77
  245. package/mcps/davinci-resolve-mcp/src/utils/review_annotation_live_probe.py +0 -352
  246. package/mcps/davinci-resolve-mcp/src/utils/script_templates.py +0 -1193
  247. package/mcps/davinci-resolve-mcp/src/utils/shot_relationships.py +0 -544
  248. package/mcps/davinci-resolve-mcp/src/utils/structural_diff.py +0 -175
  249. package/mcps/davinci-resolve-mcp/src/utils/sync_detection.py +0 -889
  250. package/mcps/davinci-resolve-mcp/src/utils/timeline_brain_db.py +0 -826
  251. package/mcps/davinci-resolve-mcp/src/utils/timeline_conform_live_probe.py +0 -280
  252. package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_live_probe.py +0 -1092
  253. package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_probe.py +0 -185
  254. package/mcps/davinci-resolve-mcp/src/utils/timeline_title_text.py +0 -87
  255. package/mcps/davinci-resolve-mcp/src/utils/timeline_versioning.py +0 -730
  256. package/mcps/davinci-resolve-mcp/src/utils/update_check.py +0 -685
  257. package/mcps/vectorworks-mcp/.dockerignore +0 -8
  258. package/mcps/vectorworks-mcp/Dockerfile +0 -28
  259. package/mcps/vectorworks-mcp/README.md +0 -156
  260. package/mcps/vectorworks-mcp/app/__init__.py +0 -2
  261. package/mcps/vectorworks-mcp/app/api.py +0 -52
  262. package/mcps/vectorworks-mcp/app/chunking.py +0 -56
  263. package/mcps/vectorworks-mcp/app/config.py +0 -29
  264. package/mcps/vectorworks-mcp/app/embeddings.py +0 -28
  265. package/mcps/vectorworks-mcp/app/indexer.py +0 -106
  266. package/mcps/vectorworks-mcp/app/main.py +0 -33
  267. package/mcps/vectorworks-mcp/app/mcp_server.py +0 -71
  268. package/mcps/vectorworks-mcp/app/search.py +0 -54
  269. package/mcps/vectorworks-mcp/app/utils.py +0 -14
  270. package/mcps/vectorworks-mcp/data/.gitkeep +0 -1
  271. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_PluginScript.htm +0 -95
  272. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_ScriptEditor.htm +0 -266
  273. package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_Scripting.htm +0 -106
  274. package/mcps/vectorworks-mcp/data/vw-app-help/VW2023_CreatingScriptedPlugins.htm +0 -327
  275. package/mcps/vectorworks-mcp/data/vw-app-help/VW2024_RunningScripts.htm +0 -116
  276. package/mcps/vectorworks-mcp/data/vw-devwiki/VS_Function_Reference_category.html +0 -2129
  277. package/mcps/vectorworks-mcp/data/vw-jp-ref/ObjectsSymbols.html +0 -2035
  278. package/mcps/vectorworks-mcp/data/vw-jp-ref/ScriptFunctionReference.html +0 -29
  279. package/mcps/vectorworks-mcp/design.md +0 -57
  280. package/mcps/vectorworks-mcp/docker-compose.yml +0 -18
  281. package/mcps/vectorworks-mcp/index/.gitkeep +0 -1
  282. package/mcps/vectorworks-mcp/index/meta.jsonl +0 -3956
  283. package/mcps/vectorworks-mcp/index/vw.faiss +0 -0
  284. package/mcps/vectorworks-mcp/requirements.txt +0 -10
  285. package/mcps/vectorworks-mcp/scripts/fetch_docs_minimal.sh +0 -84
  286. package/mcps/vectorworks-mcp/scripts/fetch_github_vectorworks.sh +0 -85
  287. package/mcps/vectorworks-mcp/templates/index.html +0 -40
  288. package/scripts/ecosystem-health-audit.js +0 -257
  289. package/skills/bdb-dev-os-skill/SKILL.md +0 -49
  290. package/skills/bdb-dev-os-skill/package.json +0 -11
  291. package/skills/bdbsaastraining/SKILL.md +0 -316
  292. package/skills/bdbsaastraining/references/exam-pool.md +0 -313
  293. package/skills/bdbsaastraining/references/interaction-modes.md +0 -94
  294. package/skills/bdbsaastraining/references/track-a-ai-agent.md +0 -111
  295. package/skills/bdbsaastraining/references/track-b-wordpress.md +0 -111
  296. package/skills/bdbsaastraining/references/track-c-mailserver.md +0 -107
  297. package/skills/bdbsaastraining/references/track-d-debian.md +0 -109
  298. package/skills/bdbsaastraining/references/track-e-custom.md +0 -83
  299. package/skills/bdbsaastraining/scripts/build_profile.py +0 -529
  300. package/skills/bdbsaastraining/scripts/generate_certificate.py +0 -161
  301. package/skills/bdbsaastraining/scripts/preflight_check.sh +0 -150
  302. package/skills/bdbsaastraining/templates/certificate_template.html +0 -508
  303. package/skills/global_config/bdb-ecosystem-health/SKILL.md +0 -68
  304. package/skills/global_config/bdbsaas-ops/SKILL.md +0 -16
  305. package/skills/global_config/bdbsaashost/SKILL.md +0 -125
@@ -1,826 +0,0 @@
1
- """Per-project SQLite store for timeline versioning + brain edits (C6).
2
-
3
- Lives at `<project_root>/_soul/timeline_brain.sqlite`. Tracks:
4
-
5
- - `timeline_versions` — every archived timeline duplicate (one row per archive)
6
- - `brain_edits` — every destructive edit, with target metric + before/after
7
- - `timeline_clip_usage` — which media pool items are placed where on which timeline
8
-
9
- This DB is the **source of truth** for the timeline-mutation history of a project.
10
- It coexists with the existing `jobs.sqlite` (operational job tracking) and
11
- `index.sqlite` (rebuildable cache over JSON reports). When the V2 source-of-truth
12
- migration (C1) lands, these tables move into the unified DB; until then, this is
13
- a dedicated standalone store.
14
-
15
- Concurrency: single-process access expected (Resolve is single-user). WAL mode
16
- enabled to make reads from the control panel non-blocking against writes from
17
- the MCP server.
18
- """
19
-
20
- from __future__ import annotations
21
-
22
- import logging
23
- import os
24
- import sqlite3
25
- import threading
26
- import time
27
- from contextlib import contextmanager
28
- from typing import Callable, Dict, Iterator, Optional, Tuple
29
-
30
- logger = logging.getLogger("resolve-mcp.timeline-brain-db")
31
-
32
- SCHEMA_VERSION = 12
33
- DB_FILENAME = "timeline_brain.sqlite"
34
- SOUL_DIRNAME = "_soul"
35
-
36
- # How long SQLite waits internally for a busy lock before raising
37
- # SQLITE_BUSY (in ms). Set high enough to absorb the dashboard's read traffic.
38
- BUSY_TIMEOUT_MS = 5000
39
-
40
- # Application-level retry settings on top of busy_timeout, used by transaction().
41
- # These guard against rare cases where the timeout is exhausted under load.
42
- _BUSY_RETRY_BACKOFF_SECONDS = (0.1, 0.3, 0.5)
43
-
44
- _CONNECTION_LOCK = threading.Lock()
45
- _CONNECTIONS: Dict[str, sqlite3.Connection] = {}
46
-
47
-
48
- def db_path_for_project(project_root: str) -> str:
49
- """Return the on-disk path to the timeline-brain DB for a project."""
50
- return os.path.join(project_root, SOUL_DIRNAME, DB_FILENAME)
51
-
52
-
53
- def _ensure_parent_dir(path: str) -> None:
54
- os.makedirs(os.path.dirname(path), exist_ok=True)
55
-
56
-
57
- def _init_pragmas(conn: sqlite3.Connection) -> None:
58
- conn.execute("PRAGMA journal_mode=WAL")
59
- conn.execute("PRAGMA foreign_keys=ON")
60
- conn.execute("PRAGMA synchronous=NORMAL")
61
- conn.execute(f"PRAGMA busy_timeout={BUSY_TIMEOUT_MS}")
62
-
63
-
64
- # ── Migration registry ───────────────────────────────────────────────────────
65
- #
66
- # Each entry is keyed by the *target* version. The function receives an open
67
- # connection and runs any schema changes required to advance from
68
- # (target_version - 1) to target_version. Connect() walks this dict in order
69
- # until the DB reaches SCHEMA_VERSION.
70
- #
71
- # Conventions:
72
- # - Migrations are additive when possible (ADD COLUMN, CREATE TABLE IF NOT
73
- # EXISTS, CREATE INDEX IF NOT EXISTS). Destructive changes need an explicit
74
- # data-preserving copy-and-rename sequence.
75
- # - Migration functions must be idempotent — if a column already exists (e.g.
76
- # from a partial prior run), the migration should detect that and continue.
77
- # - Bump SCHEMA_VERSION at the top of this module + register the migration
78
- # here in the same edit. The runner will detect the bump and apply it.
79
-
80
- _MIGRATIONS: Dict[int, Callable[[sqlite3.Connection], None]] = {}
81
-
82
-
83
- def register_migration(target_version: int) -> Callable[[Callable[[sqlite3.Connection], None]], Callable[[sqlite3.Connection], None]]:
84
- """Decorator: registers a migration that advances the schema to `target_version`."""
85
- def decorator(fn: Callable[[sqlite3.Connection], None]) -> Callable[[sqlite3.Connection], None]:
86
- if target_version in _MIGRATIONS:
87
- raise RuntimeError(f"Duplicate migration for v{target_version}")
88
- _MIGRATIONS[target_version] = fn
89
- return fn
90
- return decorator
91
-
92
-
93
- def _column_exists(conn: sqlite3.Connection, table: str, column: str) -> bool:
94
- row = conn.execute(f"PRAGMA table_info({table})").fetchall()
95
- return any(r[1] == column for r in row)
96
-
97
-
98
- def _read_schema_version(conn: sqlite3.Connection) -> int:
99
- try:
100
- row = conn.execute(
101
- "SELECT value FROM schema_metadata WHERE key='schema_version'"
102
- ).fetchone()
103
- except sqlite3.OperationalError:
104
- # Tables don't exist yet — treat as version 0.
105
- return 0
106
- if row is None:
107
- return 0
108
- try:
109
- return int(row["value"] if isinstance(row, sqlite3.Row) else row[0])
110
- except (TypeError, ValueError):
111
- return 0
112
-
113
-
114
- def _write_schema_version(conn: sqlite3.Connection, version: int) -> None:
115
- conn.execute(
116
- "INSERT OR REPLACE INTO schema_metadata(key, value) VALUES (?, ?)",
117
- ("schema_version", str(version)),
118
- )
119
-
120
-
121
- def _run_pending_migrations(conn: sqlite3.Connection) -> None:
122
- """Bring the DB from its current schema_version up to SCHEMA_VERSION."""
123
- current = _read_schema_version(conn)
124
- if current >= SCHEMA_VERSION:
125
- return
126
- for target in sorted(_MIGRATIONS):
127
- if target <= current:
128
- continue
129
- if target > SCHEMA_VERSION:
130
- break
131
- migration = _MIGRATIONS[target]
132
- logger.info("Running timeline_brain_db migration: v%d → v%d", target - 1, target)
133
- migration(conn)
134
- _write_schema_version(conn, target)
135
- conn.commit()
136
- current = target
137
-
138
-
139
- def _create_schema(conn: sqlite3.Connection) -> None:
140
- conn.executescript(
141
- """
142
- CREATE TABLE IF NOT EXISTS schema_metadata (
143
- key TEXT PRIMARY KEY,
144
- value TEXT NOT NULL
145
- );
146
-
147
- CREATE TABLE IF NOT EXISTS timeline_versions (
148
- id INTEGER PRIMARY KEY AUTOINCREMENT,
149
- timeline_name TEXT NOT NULL,
150
- version INTEGER NOT NULL,
151
- created_at TEXT NOT NULL,
152
- analysis_run_id TEXT,
153
- archived_timeline_name TEXT NOT NULL,
154
- archived_bin_path TEXT NOT NULL,
155
- drt_export_path TEXT,
156
- reason TEXT,
157
- UNIQUE(timeline_name, version)
158
- );
159
-
160
- CREATE INDEX IF NOT EXISTS ix_timeline_versions_name
161
- ON timeline_versions(timeline_name);
162
- CREATE INDEX IF NOT EXISTS ix_timeline_versions_run
163
- ON timeline_versions(analysis_run_id);
164
-
165
- CREATE TABLE IF NOT EXISTS brain_edits (
166
- id INTEGER PRIMARY KEY AUTOINCREMENT,
167
- analysis_run_id TEXT NOT NULL,
168
- timeline_before TEXT,
169
- timeline_after TEXT,
170
- edit_type TEXT NOT NULL,
171
- tool_name TEXT,
172
- action_name TEXT,
173
- target_metric TEXT,
174
- metric_direction TEXT,
175
- before_value REAL,
176
- after_value REAL,
177
- rationale TEXT,
178
- params_json TEXT,
179
- result_summary_json TEXT,
180
- created_at TEXT NOT NULL,
181
- rolled_back_at TEXT
182
- );
183
-
184
- CREATE INDEX IF NOT EXISTS ix_brain_edits_run
185
- ON brain_edits(analysis_run_id);
186
- CREATE INDEX IF NOT EXISTS ix_brain_edits_timeline
187
- ON brain_edits(timeline_after);
188
- CREATE INDEX IF NOT EXISTS ix_brain_edits_type
189
- ON brain_edits(edit_type);
190
-
191
- CREATE TABLE IF NOT EXISTS timeline_clip_usage (
192
- id INTEGER PRIMARY KEY AUTOINCREMENT,
193
- media_pool_item_id TEXT NOT NULL,
194
- timeline_name TEXT NOT NULL,
195
- timeline_version INTEGER,
196
- track_type TEXT NOT NULL,
197
- track_index INTEGER NOT NULL,
198
- in_frame INTEGER NOT NULL,
199
- out_frame INTEGER NOT NULL,
200
- analysis_run_id_at_placement TEXT,
201
- observed_at TEXT NOT NULL
202
- );
203
-
204
- CREATE INDEX IF NOT EXISTS ix_clip_usage_media
205
- ON timeline_clip_usage(media_pool_item_id);
206
- CREATE INDEX IF NOT EXISTS ix_clip_usage_timeline
207
- ON timeline_clip_usage(timeline_name, timeline_version);
208
- """
209
- )
210
- # Only stamp the version on a *fresh* DB. On an existing DB the recorded
211
- # version must be preserved so the migration runner can advance it from
212
- # whatever schema actually exists on disk. Overwriting unconditionally
213
- # would block every migration by making the DB always look up-to-date.
214
- if _read_schema_version(conn) == 0:
215
- _write_schema_version(conn, 1)
216
- conn.commit()
217
-
218
-
219
- def connect(project_root: str) -> sqlite3.Connection:
220
- """Return a cached, schema-initialised connection for `project_root`.
221
-
222
- On first open: creates the tables if missing, then runs any pending
223
- migrations to bring the schema up to SCHEMA_VERSION.
224
- """
225
- if not project_root:
226
- raise ValueError("project_root is required")
227
- path = db_path_for_project(project_root)
228
- with _CONNECTION_LOCK:
229
- existing = _CONNECTIONS.get(path)
230
- if existing is not None:
231
- return existing
232
- _ensure_parent_dir(path)
233
- conn = sqlite3.connect(path, check_same_thread=False, isolation_level=None)
234
- conn.row_factory = sqlite3.Row
235
- _init_pragmas(conn)
236
- _create_schema(conn)
237
- _run_pending_migrations(conn)
238
- _CONNECTIONS[path] = conn
239
- return conn
240
-
241
-
242
- def close_all() -> None:
243
- """Close every cached connection. Intended for tests + shutdown."""
244
- with _CONNECTION_LOCK:
245
- for conn in _CONNECTIONS.values():
246
- try:
247
- conn.close()
248
- except sqlite3.Error:
249
- pass
250
- _CONNECTIONS.clear()
251
-
252
-
253
- @contextmanager
254
- def transaction(project_root: str) -> Iterator[sqlite3.Connection]:
255
- """Context manager wrapping a write transaction.
256
-
257
- Uses BEGIN IMMEDIATE so concurrent dashboard readers don't trigger SQLITE_BUSY
258
- when a write is in flight. Retries on `database is locked` with exponential
259
- backoff so that contention between the MCP server and the dashboard doesn't
260
- surface as a user-visible error.
261
- """
262
- conn = connect(project_root)
263
- last_exc: Optional[sqlite3.OperationalError] = None
264
- began = False
265
- for delay in (0.0, *_BUSY_RETRY_BACKOFF_SECONDS):
266
- if delay > 0:
267
- time.sleep(delay)
268
- try:
269
- conn.execute("BEGIN IMMEDIATE")
270
- began = True
271
- break
272
- except sqlite3.OperationalError as exc:
273
- if "locked" not in str(exc).lower():
274
- raise
275
- last_exc = exc
276
- logger.debug("BEGIN IMMEDIATE saw busy lock (%s), retrying after %.2fs", exc, delay)
277
- if not began:
278
- assert last_exc is not None
279
- raise last_exc
280
- try:
281
- yield conn
282
- except Exception:
283
- try:
284
- conn.execute("ROLLBACK")
285
- except sqlite3.OperationalError:
286
- pass
287
- raise
288
- else:
289
- conn.execute("COMMIT")
290
-
291
-
292
- def reset_for_test(project_root: str) -> None:
293
- """Drop + recreate every table. Tests only."""
294
- path = db_path_for_project(project_root)
295
- with _CONNECTION_LOCK:
296
- conn = _CONNECTIONS.pop(path, None)
297
- if conn is not None:
298
- try:
299
- conn.close()
300
- except sqlite3.Error:
301
- pass
302
- for suffix in ("", "-wal", "-shm"):
303
- try:
304
- os.remove(path + suffix)
305
- except OSError:
306
- pass
307
-
308
-
309
- # ── v2 migration: analysis_runs table + initiator columns ───────────────────
310
-
311
-
312
- @register_migration(2)
313
- def _migrate_v2_analysis_runs(conn: sqlite3.Connection) -> None:
314
- """Add analysis_runs table + initiator columns on existing tables.
315
-
316
- `analysis_runs` groups a sequence of brain_edits / timeline_versions under
317
- a single user- or brain-initiated operation. begin_run inserts a row,
318
- end_run updates ended_at + summary_json with the cumulative metric deltas.
319
- """
320
- conn.executescript(
321
- """
322
- CREATE TABLE IF NOT EXISTS analysis_runs (
323
- id TEXT PRIMARY KEY,
324
- label TEXT,
325
- initiator TEXT,
326
- started_at TEXT NOT NULL,
327
- ended_at TEXT,
328
- summary_json TEXT
329
- );
330
-
331
- CREATE INDEX IF NOT EXISTS ix_analysis_runs_started
332
- ON analysis_runs(started_at);
333
- """
334
- )
335
- # Add initiator column on brain_edits + timeline_versions for provenance.
336
- if not _column_exists(conn, "brain_edits", "initiator"):
337
- conn.execute("ALTER TABLE brain_edits ADD COLUMN initiator TEXT")
338
- if not _column_exists(conn, "timeline_versions", "initiator"):
339
- conn.execute("ALTER TABLE timeline_versions ADD COLUMN initiator TEXT")
340
-
341
-
342
- # ── v3 migration: media_pool_changes table ───────────────────────────────────
343
-
344
-
345
- @register_migration(3)
346
- def _migrate_v3_media_pool_changes(conn: sqlite3.Connection) -> None:
347
- """Track destructive media-pool ops (delete clips/folders, replace, relink).
348
-
349
- These don't mutate a timeline directly but can offline clips referenced by
350
- timelines. Separate table from brain_edits because the addressable entity
351
- is a media_pool_item, not a timeline.
352
- """
353
- conn.executescript(
354
- """
355
- CREATE TABLE IF NOT EXISTS media_pool_changes (
356
- id INTEGER PRIMARY KEY AUTOINCREMENT,
357
- analysis_run_id TEXT,
358
- action TEXT NOT NULL,
359
- target_id TEXT,
360
- target_name TEXT,
361
- before_state_json TEXT,
362
- after_state_json TEXT,
363
- params_json TEXT,
364
- initiator TEXT,
365
- created_at TEXT NOT NULL
366
- );
367
-
368
- CREATE INDEX IF NOT EXISTS ix_media_pool_changes_run
369
- ON media_pool_changes(analysis_run_id);
370
- CREATE INDEX IF NOT EXISTS ix_media_pool_changes_target
371
- ON media_pool_changes(target_id);
372
- CREATE INDEX IF NOT EXISTS ix_media_pool_changes_action
373
- ON media_pool_changes(action);
374
- """
375
- )
376
-
377
-
378
- # ── v4 migration: thumbnail_path on timeline_versions ───────────────────────
379
-
380
-
381
- @register_migration(4)
382
- def _migrate_v4_thumbnail_path(conn: sqlite3.Connection) -> None:
383
- """Add thumbnail_path column so the History view can render per-version stills."""
384
- if not _column_exists(conn, "timeline_versions", "thumbnail_path"):
385
- conn.execute("ALTER TABLE timeline_versions ADD COLUMN thumbnail_path TEXT")
386
-
387
-
388
- # ── v5 migration: analysis_token_usage table for caps enforcement ───────────
389
-
390
-
391
- # ── v6 migration: caps_events for refusal logging ───────────────────────────
392
-
393
-
394
- @register_migration(6)
395
- def _migrate_v6_caps_events(conn: sqlite3.Connection) -> None:
396
- """Log every caps refusal so the dashboard can show recent denials.
397
-
398
- Distinct from `analysis_token_usage` because refusals never spend tokens —
399
- we record the *intent* and the cap that blocked it, for debugging "why
400
- didn't my analysis run".
401
- """
402
- conn.executescript(
403
- """
404
- CREATE TABLE IF NOT EXISTS caps_events (
405
- id INTEGER PRIMARY KEY AUTOINCREMENT,
406
- event_type TEXT NOT NULL, -- 'refusal' | 'timeout' | other
407
- reason TEXT, -- 'over_clip_cap' | 'over_day_cap' | ...
408
- preset TEXT,
409
- estimated_vision_tokens INTEGER,
410
- current_usage_json TEXT,
411
- cap_json TEXT,
412
- headroom_json TEXT,
413
- clip_id TEXT,
414
- job_id TEXT,
415
- occurred_at TEXT NOT NULL,
416
- day_bucket TEXT NOT NULL
417
- );
418
-
419
- CREATE INDEX IF NOT EXISTS ix_caps_events_day ON caps_events(day_bucket);
420
- CREATE INDEX IF NOT EXISTS ix_caps_events_type ON caps_events(event_type);
421
- """
422
- )
423
-
424
-
425
- # ── v7 migration: resolve_ai_op_usage ledger for Resolve 21 GPU/AI ops ──────
426
-
427
-
428
- @register_migration(7)
429
- def _migrate_v7_resolve_ai_op_usage(conn: sqlite3.Connection) -> None:
430
- """Ledger for Resolve-local AI ops (audio classification, IntelliSearch,
431
- slate, motion-deblur, speech generation).
432
-
433
- These run on Resolve's own GPU/AI engine and do NOT consume the Claude-side
434
- analysis token budget tracked in `analysis_token_usage`, so they get their
435
- own ledger. The value is the wall-clock + file/byte accounting for the two
436
- media-creating ops (remove_motion_blur, generate_speech). `op_class` is
437
- 'analysis' (no media produced) or 'render' (new media file written).
438
- """
439
- conn.executescript(
440
- """
441
- CREATE TABLE IF NOT EXISTS resolve_ai_op_usage (
442
- id INTEGER PRIMARY KEY AUTOINCREMENT,
443
- op TEXT NOT NULL,
444
- op_class TEXT NOT NULL DEFAULT 'analysis',
445
- clip_id TEXT,
446
- session_id TEXT,
447
- success INTEGER NOT NULL DEFAULT 0,
448
- wall_clock_ms INTEGER NOT NULL DEFAULT 0,
449
- output_path TEXT,
450
- output_bytes INTEGER,
451
- extra_required TEXT,
452
- error TEXT,
453
- occurred_at TEXT NOT NULL,
454
- day_bucket TEXT NOT NULL
455
- );
456
-
457
- CREATE INDEX IF NOT EXISTS ix_resolve_ai_op_usage_op
458
- ON resolve_ai_op_usage(op);
459
- CREATE INDEX IF NOT EXISTS ix_resolve_ai_op_usage_session
460
- ON resolve_ai_op_usage(session_id);
461
- CREATE INDEX IF NOT EXISTS ix_resolve_ai_op_usage_day
462
- ON resolve_ai_op_usage(day_bucket);
463
- """
464
- )
465
-
466
-
467
- @register_migration(8)
468
- def _migrate_v8_actor_identity(conn: sqlite3.Connection) -> None:
469
- """Instance-level actor provenance (design decision 2026-06-09).
470
-
471
- Stamps which process kind performed an op — "<instance>:<pid>" where
472
- instance is stdio / network-sse / network-http / control-panel /
473
- batch-cli. Complements `initiator` (auto vs manual) on the versioning
474
- tables: initiator says WHY a row exists, actor says WHO wrote it.
475
- """
476
- for table in ("resolve_ai_op_usage", "brain_edits", "timeline_versions"):
477
- if not _column_exists(conn, table, "actor"):
478
- conn.execute(f"ALTER TABLE {table} ADD COLUMN actor TEXT")
479
-
480
-
481
- @register_migration(5)
482
- def _migrate_v5_analysis_token_usage(conn: sqlite3.Connection) -> None:
483
- """Track real vendor token + frame upload usage so caps can enforce budgets.
484
-
485
- `scope` is 'clip' | 'job' | 'session' | 'global'. `scope_key` is the entity
486
- id within that scope (clip_id, job_id, session_id, or empty for global).
487
- `day_bucket` (YYYY-MM-DD) is indexed for fast per-day rollup queries.
488
- """
489
- conn.executescript(
490
- """
491
- CREATE TABLE IF NOT EXISTS analysis_token_usage (
492
- id INTEGER PRIMARY KEY AUTOINCREMENT,
493
- scope TEXT NOT NULL,
494
- scope_key TEXT,
495
- vision_tokens INTEGER NOT NULL DEFAULT 0,
496
- transcription_tokens INTEGER NOT NULL DEFAULT 0,
497
- frames_uploaded INTEGER NOT NULL DEFAULT 0,
498
- wall_clock_ms INTEGER NOT NULL DEFAULT 0,
499
- preset_at_call TEXT,
500
- occurred_at TEXT NOT NULL,
501
- day_bucket TEXT NOT NULL
502
- );
503
-
504
- CREATE INDEX IF NOT EXISTS ix_analysis_token_usage_scope
505
- ON analysis_token_usage(scope, scope_key);
506
- CREATE INDEX IF NOT EXISTS ix_analysis_token_usage_day
507
- ON analysis_token_usage(day_bucket);
508
- """
509
- )
510
-
511
-
512
- @register_migration(9)
513
- def _migrate_v9_analysis_core(conn: sqlite3.Connection) -> None:
514
- """C1 — DB-canonical clip analysis (Phase A of the analysis/edit-engine program).
515
-
516
- The DB becomes the source of truth for clip analysis; analysis.json becomes
517
- a derived export written in lockstep. Shape is hybrid: a canonical
518
- full-report blob per clip (`analysis_reports`) plus normalized tables for
519
- what downstream phases query (shots, subjective fields with per-field
520
- provenance, transcript segments, sampled frames, QC observations).
521
- Computed layers (technical/motion/cuts/audio) stay inside the blob — they
522
- re-derive cleanly from source media and nothing queries them row-wise.
523
- """
524
- conn.executescript(
525
- """
526
- CREATE TABLE IF NOT EXISTS clips (
527
- clip_uuid TEXT PRIMARY KEY, -- rename-stable canonical hash (12-hex)
528
- clip_dir TEXT, -- report folder name under clips/
529
- resolve_clip_id TEXT,
530
- media_id TEXT,
531
- clip_name TEXT,
532
- file_path TEXT,
533
- bin_path TEXT,
534
- duration_seconds REAL,
535
- fps REAL,
536
- resolution TEXT,
537
- media_type TEXT,
538
- summary TEXT,
539
- overall_motion_level TEXT,
540
- cut_count INTEGER,
541
- shot_count INTEGER,
542
- analysis_version TEXT,
543
- depth TEXT,
544
- signature_hash TEXT,
545
- analyzed_at TEXT,
546
- vision_status TEXT,
547
- vision_committed_at TEXT,
548
- created_at TEXT NOT NULL,
549
- updated_at TEXT NOT NULL
550
- );
551
-
552
- CREATE INDEX IF NOT EXISTS ix_clips_clip_name ON clips(clip_name);
553
- CREATE INDEX IF NOT EXISTS ix_clips_file_path ON clips(file_path);
554
-
555
- -- Every stable id a clip is known by (legacy hashes, clip_id, media_id,
556
- -- folder hash, normalized file path) → clip_uuid. Lookup table only.
557
- CREATE TABLE IF NOT EXISTS clip_aliases (
558
- alias TEXT NOT NULL,
559
- clip_uuid TEXT NOT NULL,
560
- kind TEXT,
561
- PRIMARY KEY (alias, clip_uuid)
562
- );
563
-
564
- CREATE INDEX IF NOT EXISTS ix_clip_aliases_clip ON clip_aliases(clip_uuid);
565
-
566
- -- Canonical full analysis payload. analysis.json is exported FROM this.
567
- CREATE TABLE IF NOT EXISTS analysis_reports (
568
- clip_uuid TEXT PRIMARY KEY,
569
- report_json TEXT NOT NULL,
570
- signature_hash TEXT,
571
- analyzed_at TEXT,
572
- written_at TEXT NOT NULL,
573
- FOREIGN KEY (clip_uuid) REFERENCES clips(clip_uuid) ON DELETE CASCADE
574
- );
575
-
576
- CREATE TABLE IF NOT EXISTS shots (
577
- shot_uuid TEXT PRIMARY KEY, -- short_hash(clip_uuid + rounded time region)
578
- clip_uuid TEXT NOT NULL,
579
- shot_index INTEGER NOT NULL,
580
- time_seconds_start REAL,
581
- time_seconds_end REAL,
582
- description TEXT,
583
- qc_flags_json TEXT,
584
- frame_indices_json TEXT,
585
- extra_json TEXT, -- forward-compat: any other shot keys
586
- created_at TEXT NOT NULL,
587
- updated_at TEXT NOT NULL,
588
- UNIQUE(clip_uuid, shot_index),
589
- FOREIGN KEY (clip_uuid) REFERENCES clips(clip_uuid) ON DELETE CASCADE
590
- );
591
-
592
- CREATE INDEX IF NOT EXISTS ix_shots_clip ON shots(clip_uuid, shot_index);
593
-
594
- -- Per-field provenance for subjective (vision/human) values.
595
- -- Current value = the row with superseded_at IS NULL.
596
- CREATE TABLE IF NOT EXISTS subjective_fields (
597
- id INTEGER PRIMARY KEY AUTOINCREMENT,
598
- entity_type TEXT NOT NULL CHECK(entity_type IN ('clip', 'shot')),
599
- entity_uuid TEXT NOT NULL,
600
- field_path TEXT NOT NULL,
601
- value_json TEXT NOT NULL,
602
- confidence TEXT,
603
- source TEXT NOT NULL, -- 'vision_v0.2' | 'human' | ...
604
- source_model TEXT,
605
- author TEXT NOT NULL,
606
- timestamp TEXT NOT NULL,
607
- superseded_at TEXT
608
- );
609
-
610
- CREATE INDEX IF NOT EXISTS ix_sf_entity
611
- ON subjective_fields(entity_type, entity_uuid, field_path);
612
- CREATE INDEX IF NOT EXISTS ix_sf_current
613
- ON subjective_fields(entity_type, entity_uuid, field_path)
614
- WHERE superseded_at IS NULL;
615
-
616
- CREATE TABLE IF NOT EXISTS field_changelog (
617
- id INTEGER PRIMARY KEY AUTOINCREMENT,
618
- entity_type TEXT NOT NULL,
619
- entity_uuid TEXT NOT NULL,
620
- field_path TEXT NOT NULL,
621
- previous_value_json TEXT,
622
- new_value_json TEXT NOT NULL,
623
- previous_source TEXT,
624
- new_source TEXT NOT NULL,
625
- previous_author TEXT,
626
- new_author TEXT NOT NULL,
627
- change_reason TEXT,
628
- timestamp TEXT NOT NULL
629
- );
630
-
631
- CREATE INDEX IF NOT EXISTS ix_changelog_entity
632
- ON field_changelog(entity_type, entity_uuid, timestamp);
633
-
634
- CREATE TABLE IF NOT EXISTS transcript_segments (
635
- id INTEGER PRIMARY KEY AUTOINCREMENT,
636
- clip_uuid TEXT NOT NULL,
637
- segment_index INTEGER NOT NULL,
638
- start_seconds REAL,
639
- end_seconds REAL,
640
- text TEXT,
641
- speaker_id TEXT,
642
- UNIQUE(clip_uuid, segment_index),
643
- FOREIGN KEY (clip_uuid) REFERENCES clips(clip_uuid) ON DELETE CASCADE
644
- );
645
-
646
- CREATE INDEX IF NOT EXISTS ix_transcript_segments_clip
647
- ON transcript_segments(clip_uuid, start_seconds);
648
-
649
- CREATE TABLE IF NOT EXISTS frames (
650
- id INTEGER PRIMARY KEY AUTOINCREMENT,
651
- clip_uuid TEXT NOT NULL,
652
- shot_uuid TEXT,
653
- frame_index INTEGER NOT NULL,
654
- time_seconds REAL,
655
- frame_path TEXT,
656
- selection_reason TEXT,
657
- motion_peak INTEGER NOT NULL DEFAULT 0,
658
- UNIQUE(clip_uuid, frame_index),
659
- FOREIGN KEY (clip_uuid) REFERENCES clips(clip_uuid) ON DELETE CASCADE
660
- );
661
-
662
- CREATE INDEX IF NOT EXISTS ix_frames_clip ON frames(clip_uuid, frame_index);
663
- CREATE INDEX IF NOT EXISTS ix_frames_shot ON frames(shot_uuid);
664
-
665
- CREATE TABLE IF NOT EXISTS qc_observations (
666
- id INTEGER PRIMARY KEY AUTOINCREMENT,
667
- clip_uuid TEXT NOT NULL,
668
- shot_uuid TEXT,
669
- observation_type TEXT NOT NULL,
670
- severity TEXT NOT NULL DEFAULT 'info',
671
- message TEXT NOT NULL,
672
- related_shot_indices_json TEXT,
673
- confidence TEXT,
674
- source TEXT NOT NULL,
675
- resolved INTEGER NOT NULL DEFAULT 0,
676
- resolved_by TEXT,
677
- resolved_at TEXT,
678
- resolution_note TEXT,
679
- created_at TEXT NOT NULL,
680
- FOREIGN KEY (clip_uuid) REFERENCES clips(clip_uuid) ON DELETE CASCADE
681
- );
682
-
683
- CREATE INDEX IF NOT EXISTS ix_qc_clip ON qc_observations(clip_uuid);
684
- CREATE INDEX IF NOT EXISTS ix_qc_unresolved
685
- ON qc_observations(clip_uuid, resolved) WHERE resolved = 0;
686
- """
687
- )
688
-
689
-
690
- @register_migration(10)
691
- def _migrate_v10_embeddings(conn: sqlite3.Connection) -> None:
692
- """C3 — embeddings for similarity search (Phase C of the analysis program).
693
-
694
- One row per (entity, kind, model). Vectors are float32 BLOBs; similarity
695
- is brute-force cosine in the app layer (thousands of rows, not millions).
696
- `content_hash` fingerprints the embedded content so re-runs only re-embed
697
- entities whose text/frames changed.
698
- """
699
- conn.executescript(
700
- """
701
- CREATE TABLE IF NOT EXISTS embeddings (
702
- id INTEGER PRIMARY KEY AUTOINCREMENT,
703
- entity_type TEXT NOT NULL CHECK(entity_type IN ('clip', 'shot', 'frame', 'segment')),
704
- entity_uuid TEXT NOT NULL,
705
- embedding_kind TEXT NOT NULL, -- 'text' | 'visual'
706
- model_name TEXT NOT NULL,
707
- dimension INTEGER NOT NULL,
708
- vector BLOB NOT NULL,
709
- content_hash TEXT,
710
- computed_at TEXT NOT NULL,
711
- UNIQUE(entity_type, entity_uuid, embedding_kind, model_name)
712
- );
713
-
714
- CREATE INDEX IF NOT EXISTS ix_embeddings_kind
715
- ON embeddings(embedding_kind, model_name);
716
- CREATE INDEX IF NOT EXISTS ix_embeddings_entity
717
- ON embeddings(entity_type, entity_uuid);
718
- """
719
- )
720
-
721
-
722
- @register_migration(11)
723
- def _migrate_v11_entities(conn: sqlite3.Connection) -> None:
724
- """C5 — cross-clip entities (Phase D of the analysis program).
725
-
726
- `entities` rows start as provisional clusters over the v10 visual
727
- embeddings (source='clustering', label NULL) and are enriched by one
728
- host-vision call per cluster representative (source='vision_entity_v1')
729
- or by humans. `entity_appearances` records every frame an entity was
730
- seen in, with the clip/shot derivation and the match similarity.
731
- """
732
- conn.executescript(
733
- """
734
- CREATE TABLE IF NOT EXISTS entities (
735
- entity_uuid TEXT PRIMARY KEY,
736
- kind TEXT, -- person|place|object|unknown
737
- label TEXT,
738
- description TEXT,
739
- confidence TEXT,
740
- source TEXT NOT NULL,
741
- representative_frame_ref TEXT, -- 'clip_uuid:frame_index'
742
- representative_frame_path TEXT,
743
- cluster_size INTEGER,
744
- created_at TEXT NOT NULL,
745
- updated_at TEXT NOT NULL
746
- );
747
-
748
- CREATE INDEX IF NOT EXISTS ix_entities_kind ON entities(kind);
749
-
750
- CREATE TABLE IF NOT EXISTS entity_appearances (
751
- id INTEGER PRIMARY KEY AUTOINCREMENT,
752
- entity_uuid TEXT NOT NULL,
753
- clip_uuid TEXT NOT NULL,
754
- shot_uuid TEXT,
755
- frame_ref TEXT NOT NULL, -- 'clip_uuid:frame_index'
756
- similarity REAL,
757
- UNIQUE(entity_uuid, frame_ref),
758
- FOREIGN KEY (entity_uuid) REFERENCES entities(entity_uuid) ON DELETE CASCADE
759
- );
760
-
761
- CREATE INDEX IF NOT EXISTS ix_entity_appearances_entity
762
- ON entity_appearances(entity_uuid);
763
- CREATE INDEX IF NOT EXISTS ix_entity_appearances_clip
764
- ON entity_appearances(clip_uuid);
765
- """
766
- )
767
-
768
-
769
- @register_migration(12)
770
- def _migrate_v12_shot_relationships(conn: sqlite3.Connection) -> None:
771
- """Cross-shot relationships (spec §4 — pattern recognition only).
772
-
773
- Three types: same_setup_as / alt_take_of (symmetric — stored once with
774
- the canonically-ordered pair) and continues_from (directional — the
775
- SOURCE shot continues from the TARGET shot, i.e. target precedes source).
776
- Rows are append-only with supersede semantics: current rows have
777
- superseded_at IS NULL.
778
- """
779
- conn.executescript(
780
- """
781
- CREATE TABLE IF NOT EXISTS shot_relationships (
782
- id INTEGER PRIMARY KEY AUTOINCREMENT,
783
- source_shot_uuid TEXT NOT NULL,
784
- target_shot_uuid TEXT NOT NULL,
785
- relationship_type TEXT NOT NULL CHECK(relationship_type IN
786
- ('same_setup_as', 'continues_from', 'alt_take_of')),
787
- confidence TEXT,
788
- source TEXT NOT NULL,
789
- author TEXT NOT NULL,
790
- timestamp TEXT NOT NULL,
791
- superseded_at TEXT,
792
- UNIQUE(source_shot_uuid, target_shot_uuid, relationship_type, timestamp),
793
- FOREIGN KEY (source_shot_uuid) REFERENCES shots(shot_uuid) ON DELETE CASCADE,
794
- FOREIGN KEY (target_shot_uuid) REFERENCES shots(shot_uuid) ON DELETE CASCADE
795
- );
796
-
797
- CREATE INDEX IF NOT EXISTS idx_relationships_source
798
- ON shot_relationships(source_shot_uuid) WHERE superseded_at IS NULL;
799
- CREATE INDEX IF NOT EXISTS idx_relationships_target
800
- ON shot_relationships(target_shot_uuid) WHERE superseded_at IS NULL;
801
- CREATE INDEX IF NOT EXISTS idx_relationships_type
802
- ON shot_relationships(relationship_type) WHERE superseded_at IS NULL;
803
- """
804
- )
805
-
806
-
807
- def latest_version(conn: sqlite3.Connection, timeline_name: str) -> Optional[int]:
808
- """Return the highest archived version number for `timeline_name`, or None."""
809
- row = conn.execute(
810
- "SELECT MAX(version) AS v FROM timeline_versions WHERE timeline_name = ?",
811
- (timeline_name,),
812
- ).fetchone()
813
- if row is None or row["v"] is None:
814
- return None
815
- return int(row["v"])
816
-
817
-
818
- def run_archived_for_run(conn: sqlite3.Connection, timeline_name: str, analysis_run_id: str) -> bool:
819
- """Has an archive already happened for this (timeline, run) pair?"""
820
- if not analysis_run_id:
821
- return False
822
- row = conn.execute(
823
- "SELECT 1 FROM timeline_versions WHERE timeline_name = ? AND analysis_run_id = ? LIMIT 1",
824
- (timeline_name, analysis_run_id),
825
- ).fetchone()
826
- return row is not None