stillmade-sdk 0.1.41 → 0.1.43

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 (285) hide show
  1. package/.stillmade/sdk-baseline.json +19 -21
  2. package/AGENTS.md +17 -12
  3. package/FRAME_VIEWS.md +113 -0
  4. package/INTERFACE_DESIGN.md +67 -22
  5. package/MOBILE_UI.md +4 -2
  6. package/MODULES.md +152 -6
  7. package/PASTE_TO_LLM.md +42 -32
  8. package/README.md +3 -2
  9. package/SDK.md +1028 -563
  10. package/UI_AESTHETIC.md +30 -3
  11. package/blocks/generate/package.js +1 -0
  12. package/blocks/generate/versions/1.0.0/package.js +463 -0
  13. package/docs/build/audio-to-editor.md +36 -0
  14. package/docs/build/block-builder-contract.md +150 -0
  15. package/docs/build/comfyui.md +227 -0
  16. package/docs/build/concepts.md +40 -0
  17. package/docs/build/context.md +159 -0
  18. package/docs/build/cookbook.md +57 -0
  19. package/docs/build/custom-interface.md +617 -0
  20. package/docs/build/data-types.md +83 -0
  21. package/docs/build/frame-views.md +259 -0
  22. package/docs/build/generate-media.md +59 -0
  23. package/docs/build/github-applications.md +314 -0
  24. package/docs/build/hosted-images.md +214 -0
  25. package/docs/build/hosted-music.md +39 -0
  26. package/docs/build/hosted-speech.md +157 -0
  27. package/docs/build/hosted-text.md +246 -0
  28. package/docs/build/image-description.md +49 -0
  29. package/docs/build/interface-design.md +343 -0
  30. package/docs/build/javascript.md +63 -0
  31. package/docs/build/local-preview.md +61 -0
  32. package/docs/build/media-analysis.md +27 -0
  33. package/docs/build/media.md +400 -0
  34. package/docs/build/mobile-ui.md +106 -0
  35. package/docs/build/modules.md +487 -0
  36. package/docs/build/original-blocks.md +70 -0
  37. package/docs/build/outside-apis.md +128 -0
  38. package/docs/build/production-context-templates.md +12 -0
  39. package/docs/build/project-storage.md +94 -0
  40. package/docs/build/quickstart.md +48 -0
  41. package/docs/build/recipes.md +103 -0
  42. package/docs/build/script-handoff.md +45 -0
  43. package/docs/build/script-writer.md +34 -0
  44. package/docs/build/state.md +223 -0
  45. package/docs/build/testing.md +112 -0
  46. package/docs/build/timeline-edits.md +116 -0
  47. package/docs/build/timeline-proposals.md +76 -0
  48. package/docs/build/transcription.md +59 -0
  49. package/docs/build/triggers.md +59 -0
  50. package/docs/build/tutorial.md +52 -0
  51. package/docs/build/typescript.md +34 -0
  52. package/docs/build/video-generation.md +61 -0
  53. package/docs/build/web-reading.md +37 -0
  54. package/docs/build/with-your-ai.md +27 -0
  55. package/docs/changelog.md +9 -0
  56. package/docs/creators.md +21 -0
  57. package/docs/distribute/discovery.md +43 -0
  58. package/docs/distribute/imports.md +58 -0
  59. package/docs/distribute/publishing.md +29 -0
  60. package/docs/distribute/sdk-versions.md +36 -0
  61. package/docs/distribute/selling.md +14 -0
  62. package/docs/distribute/teams.md +60 -0
  63. package/docs/distribute/versioning.md +28 -0
  64. package/docs/examples.md +23 -0
  65. package/docs/gallery.md +26 -0
  66. package/docs/index.md +50 -0
  67. package/docs/native-mcp.md +307 -0
  68. package/docs/project-types/automation.md +43 -0
  69. package/docs/project-types/batches.md +50 -0
  70. package/docs/project-types/build.md +42 -0
  71. package/docs/project-types/completion.md +15 -0
  72. package/docs/project-types/conditions.md +21 -0
  73. package/docs/project-types/hosted-review.md +115 -0
  74. package/docs/project-types/onboarding.md +75 -0
  75. package/docs/project-types/running.md +309 -0
  76. package/docs/project-types/testing.md +72 -0
  77. package/docs/reference/application-artifacts.md +87 -0
  78. package/docs/reference/authoring-guide.md +134 -0
  79. package/docs/reference/browser-applications.md +267 -0
  80. package/docs/reference/capabilities.md +45 -0
  81. package/docs/reference/cli.md +71 -0
  82. package/docs/reference/connection-api.md +59 -0
  83. package/docs/reference/cooperative-jobs.md +13 -0
  84. package/docs/reference/desktop-capabilities.md +172 -0
  85. package/docs/reference/desktop-execution.md +77 -0
  86. package/docs/reference/errors.md +233 -0
  87. package/docs/reference/execution.md +800 -0
  88. package/docs/reference/first-party-blocks.md +40 -0
  89. package/docs/reference/generation-models.md +59 -0
  90. package/docs/reference/limits.md +306 -0
  91. package/docs/reference/manifest-kinds-controls.md +36 -0
  92. package/docs/reference/manifest.md +181 -0
  93. package/docs/reference/operations.md +36 -0
  94. package/docs/reference/package-jobs.md +35 -0
  95. package/docs/reference/parity.md +55 -0
  96. package/docs/reference/permissions.md +86 -0
  97. package/docs/reference/ports.md +26 -0
  98. package/docs/reference/types.md +268 -0
  99. package/docs/reference/universal-resolver.md +303 -0
  100. package/docs/reference/view-bridge.md +60 -0
  101. package/docs/reference/view-rules.md +15 -0
  102. package/docs/reference/workspace-sync.md +25 -0
  103. package/docs/sdk.md +34 -0
  104. package/docs/tools/block-remix.md +211 -0
  105. package/docs/tools/llm.md +2213 -0
  106. package/docs/tools/repository-adaptation.md +613 -0
  107. package/examples/check-clip.stillmade.json +8 -1
  108. package/examples/clean-text.stillmade.json +4 -0
  109. package/examples/custom-interface.stillmade.json +4 -0
  110. package/examples/describe-image.stillmade.json +4 -0
  111. package/examples/foley-cue.stillmade.json +4 -0
  112. package/examples/generate-image.stillmade.json +4 -0
  113. package/examples/generate-video.stillmade.json +10 -2
  114. package/examples/module-word-stats/stillmade.block.json +4 -0
  115. package/examples/narrate-script.stillmade.json +4 -0
  116. package/examples/numbered-takes.stillmade.json +4 -0
  117. package/examples/research-topic.stillmade.json +9 -1
  118. package/examples/score-a-scene.stillmade.json +4 -0
  119. package/examples/script-draft.stillmade.json +4 -0
  120. package/examples/summarize-page.stillmade.json +4 -0
  121. package/examples/text-workflow.stillmade.json +7 -32
  122. package/examples/thicken-lines.stillmade.json +4 -0
  123. package/examples/timeline-polish.stillmade.json +15 -3
  124. package/examples/transcribe-audio.stillmade.json +15 -3
  125. package/examples/word-count.stillmade.json +4 -0
  126. package/package.json +1 -1
  127. package/packages/block-cli/cli.js +13 -6
  128. package/packages/block-cli/dev-host/assets/host.js +81 -38
  129. package/packages/block-cli/dev-host/assets/index.css +1 -1
  130. package/packages/block-cli/format.js +1 -1
  131. package/packages/block-cli/ready-gate.js +4 -0
  132. package/packages/block-cli/templates.js +37 -19
  133. package/packages/block-cli/type-packages.js +119 -0
  134. package/packages/block-platform/block-readiness.js +8 -1
  135. package/packages/block-platform/canvas.js +2 -1
  136. package/packages/block-platform/catalog.js +27 -48
  137. package/packages/block-platform/native-host-inventory.js +0 -226
  138. package/packages/block-platform/plain-descriptions.js +1 -25
  139. package/packages/block-platform/project-type-starters.js +13 -53
  140. package/packages/block-platform/script-handoff.js +2 -4
  141. package/packages/block-platform/type-workspace.js +2 -0
  142. package/packages/block-sdk/appearance.js +3 -1
  143. package/packages/block-sdk/audio-generation-example.js +2 -2
  144. package/packages/block-sdk/block-tier.js +1 -1
  145. package/packages/block-sdk/builder-contract.js +13 -11
  146. package/packages/block-sdk/capabilities.js +1 -1
  147. package/packages/block-sdk/capability-example.js +1 -1
  148. package/packages/block-sdk/contracts.js +6 -2
  149. package/packages/block-sdk/examples.js +3 -3
  150. package/packages/block-sdk/first-party-identities.js +1 -84
  151. package/packages/block-sdk/first-party-packages.js +3 -85
  152. package/packages/block-sdk/generation-catalog.js +15 -3
  153. package/packages/block-sdk/image-description-example.js +1 -1
  154. package/packages/block-sdk/image-generation-example.js +1 -1
  155. package/packages/block-sdk/index.d.ts +2 -1
  156. package/packages/block-sdk/media-analysis-example.js +2 -2
  157. package/packages/block-sdk/mock-host.js +7 -1
  158. package/packages/block-sdk/module-example.js +1 -1
  159. package/packages/block-sdk/module-host-calls.js +35 -10
  160. package/packages/block-sdk/module-hosted.js +15 -2
  161. package/packages/block-sdk/module-media-access.js +71 -0
  162. package/packages/block-sdk/module-node.js +1 -1
  163. package/packages/block-sdk/portable-media-actions.js +1 -1
  164. package/packages/block-sdk/runtime.js +1 -1
  165. package/packages/block-sdk/speech-example.js +1 -1
  166. package/packages/block-sdk/state-example.js +1 -1
  167. package/packages/block-sdk/timeline-proposal-example.js +1 -1
  168. package/packages/block-sdk/transcription-example.js +1 -1
  169. package/packages/block-sdk/trigger-examples.js +2 -2
  170. package/packages/block-sdk/video-generation-example.js +1 -1
  171. package/packages/block-sdk/view-bridge.js +4 -1
  172. package/packages/block-sdk/view-design.js +44 -15
  173. package/packages/block-sdk/web-example.js +2 -2
  174. package/packages/design-system/block-kit.css +53 -12
  175. package/packages/design-system/block-kit.html +3 -2
  176. package/blocks/mcp-motion-planner/versions/1.1.0/package.js +0 -97
  177. package/blocks/motion-designer/versions/1.0.1/package.js +0 -173318
  178. package/blocks/motion-designer/versions/1.0.2/package.js +0 -173318
  179. package/blocks/motion-frame-review/versions/1.1.1/package.js +0 -465
  180. package/blocks/motion-frame-review/versions/1.1.2/package.snapshot.js +0 -572
  181. package/blocks/narration/package.js +0 -1
  182. package/blocks/narration/versions/1.0.0/package.js +0 -221
  183. package/blocks/narration/versions/1.0.1/package.js +0 -221
  184. package/blocks/native/sm.animated-scene/versions/2.0.0/package.js +0 -475
  185. package/blocks/native/sm.animated-scene/versions/2.1.0/package.js +0 -498
  186. package/blocks/native/sm.animated-scene/versions/2.2.0/package.js +0 -524
  187. package/blocks/native/sm.animated-scene/versions/2.3.0/package.js +0 -618
  188. package/blocks/native/sm.animated-scene/versions/2.3.1/package.js +0 -618
  189. package/blocks/native/sm.animated-scene/versions/2.3.2/package.js +0 -723
  190. package/blocks/native/sm.animated-scene/versions/2.3.3/package.js +0 -723
  191. package/blocks/native/sm.animated-scene/versions/2.3.4/package.js +0 -676
  192. package/blocks/native/sm.animated-scene/versions/2.3.5/package.js +0 -693
  193. package/blocks/native/sm.animated-scene/versions/2.3.6/package.js +0 -700
  194. package/blocks/native/sm.animated-scene/versions/2.3.7/package.js +0 -747
  195. package/blocks/native/sm.animated-scene/versions/2.3.8/package.js +0 -769
  196. package/blocks/native/sm.animated-scene/versions/2.3.9/package.js +0 -769
  197. package/blocks/native/sm.animated-scene-assistant/versions/1.0.0/package.js +0 -183
  198. package/blocks/native/sm.blueprint/versions/2.0.0/package.js +0 -358
  199. package/blocks/native/sm.blueprint/versions/2.1.0/package.js +0 -358
  200. package/blocks/native/sm.blueprint/versions/2.1.1/package.js +0 -358
  201. package/blocks/native/sm.blueprint/versions/2.1.2/package.js +0 -358
  202. package/blocks/native/sm.blueprint/versions/2.1.3/package.js +0 -381
  203. package/blocks/native/sm.blueprint/versions/2.1.4/package.js +0 -381
  204. package/blocks/native/sm.blueprint/versions/2.1.5/package.js +0 -339
  205. package/blocks/native/sm.blueprint/versions/2.1.6/package.js +0 -356
  206. package/blocks/native/sm.blueprint/versions/2.1.7/package.js +0 -363
  207. package/blocks/native/sm.blueprint/versions/2.1.8/package.js +0 -410
  208. package/blocks/native/sm.blueprint/versions/2.1.9/package.js +0 -422
  209. package/blocks/native/sm.blueprint-assistant/versions/1.0.0/package.js +0 -172
  210. package/blocks/native/sm.chat/versions/2.0.0/package.js +0 -144
  211. package/blocks/native/sm.chat/versions/2.0.1/package.js +0 -97
  212. package/blocks/native/sm.chat/versions/2.0.2/package.js +0 -100
  213. package/blocks/native/sm.chat/versions/2.0.3/package.js +0 -100
  214. package/blocks/native/sm.continue-if/versions/1.0.0/package.js +0 -168
  215. package/blocks/native/sm.describe-media/versions/2.0.0/package.js +0 -153
  216. package/blocks/native/sm.describe-media/versions/2.0.1/package.js +0 -156
  217. package/blocks/native/sm.describe-media/versions/2.1.0/package.js +0 -140
  218. package/blocks/native/sm.image-editor/versions/2.0.0/package.js +0 -183
  219. package/blocks/native/sm.image-editor/versions/2.0.1/package.js +0 -190
  220. package/blocks/native/sm.new-items/versions/1.0.0/package.js +0 -191
  221. package/blocks/native/sm.pick-photos/versions/1.0.0/package.js +0 -334
  222. package/blocks/native/sm.quality-audit/versions/1.0.0/package.js +0 -192
  223. package/blocks/native/sm.quality-audit/versions/1.0.1/package.js +0 -192
  224. package/blocks/native/sm.quality-audit/versions/1.0.2/package.js +0 -195
  225. package/blocks/native/sm.research/versions/2.0.0/package.js +0 -175
  226. package/blocks/native/sm.research/versions/2.0.1/package.js +0 -184
  227. package/blocks/native/sm.research/versions/2.0.2/package.js +0 -187
  228. package/blocks/native/sm.script/versions/2.0.0/package.js +0 -144
  229. package/blocks/native/sm.script/versions/2.1.0/package.js +0 -144
  230. package/blocks/native/sm.script/versions/2.1.1/package.js +0 -144
  231. package/blocks/native/sm.script/versions/2.1.2/package.js +0 -105
  232. package/blocks/native/sm.script/versions/2.1.3/package.js +0 -152
  233. package/blocks/native/sm.script/versions/2.1.4/package.js +0 -156
  234. package/blocks/native/sm.script/versions/2.1.5/package.js +0 -156
  235. package/blocks/native/sm.shot-plan/versions/2.0.0/package.js +0 -301
  236. package/blocks/native/sm.shot-plan/versions/2.1.0/package.js +0 -528
  237. package/blocks/native/sm.shot-plan/versions/2.1.1/package.js +0 -528
  238. package/blocks/native/sm.shot-plan/versions/2.1.2/package.js +0 -528
  239. package/blocks/native/sm.shot-plan/versions/2.1.3/package.js +0 -536
  240. package/blocks/native/sm.shot-plan/versions/2.2.0/package.js +0 -800
  241. package/blocks/native/sm.shot-plan/versions/2.2.1/package.js +0 -920
  242. package/blocks/native/sm.shot-plan-assistant/versions/1.0.0/package.js +0 -173
  243. package/blocks/native/sm.stock-search/versions/2.0.0/package.js +0 -174
  244. package/blocks/native/sm.thumbnail/versions/2.0.0/package.js +0 -137
  245. package/blocks/native/sm.thumbnail/versions/2.0.1/package.js +0 -140
  246. package/blocks/native/sm.transcribe/versions/2.0.0/package.js +0 -163
  247. package/blocks/native/sm.transcribe/versions/2.0.1/package.js +0 -168
  248. package/blocks/native/sm.whiteboard-assistant/versions/1.0.0/package.js +0 -175
  249. package/blocks/native/sm.youtube/versions/2.0.0/package.js +0 -217
  250. package/blocks/occasion-story/package.js +0 -1
  251. package/blocks/occasion-story/versions/1.0.0/package.js +0 -1658
  252. package/blocks/occasion-story/versions/1.0.1/package.js +0 -1699
  253. package/blocks/occasion-story/versions/1.0.2/package.js +0 -1682
  254. package/blocks/photo-reel-composer/versions/1.0.0/package.js +0 -3043
  255. package/blocks/photo-reel-composer/versions/1.0.1/package.js +0 -3026
  256. package/blocks/photo-reel-composer/versions/1.0.2/package.js +0 -4149
  257. package/blocks/product-ad-variants/versions/1.0.0/package.js +0 -2911
  258. package/blocks/product-launch-director/versions/1.0.0/package.js +0 -103
  259. package/blocks/release-visuals/package.js +0 -1
  260. package/blocks/release-visuals/versions/1.0.0/package.js +0 -1492
  261. package/blocks/release-visuals/versions/1.0.1/package.js +0 -1517
  262. package/blocks/release-visuals/versions/1.0.2/package.js +0 -1500
  263. package/blocks/screen-recorder/package.js +0 -1
  264. package/blocks/screen-recorder/versions/1.0.0/package.js +0 -223
  265. package/blocks/screen-recorder/versions/1.0.1/package.js +0 -223
  266. package/blocks/short-form-finish/package.js +0 -1
  267. package/blocks/short-form-finish/versions/1.0.0/package.js +0 -1043
  268. package/blocks/short-form-finish/versions/1.0.1/package.js +0 -1043
  269. package/blocks/short-form-finish/versions/1.0.2/package.js +0 -1043
  270. package/blocks/short-form-finish/versions/1.0.3/package.js +0 -1026
  271. package/examples/saas-product-launch-reviewed-source.stillmade.json +0 -383
  272. package/examples/saas-product-launch.stillmade.json +0 -304
  273. package/packages/block-platform/dealer-inventory-video-project-type.js +0 -38
  274. package/packages/block-platform/first-revenue-project-types.js +0 -46
  275. package/packages/block-platform/fixture-starters.js +0 -13
  276. package/packages/block-platform/launch-project-source.js +0 -10
  277. package/packages/block-platform/launch-project.js +0 -30
  278. package/packages/block-platform/motion-project-types.js +0 -55
  279. package/packages/block-platform/motion-starters.js +0 -14
  280. package/packages/block-platform/product-ad-variants-project-type.js +0 -30
  281. package/packages/block-platform/product-launch-updated.js +0 -90
  282. package/packages/block-platform/revenue-review-project-types.js +0 -36
  283. package/packages/block-platform/revenue-review-starters.js +0 -13
  284. package/packages/block-platform/script-writer.js +0 -29
  285. package/packages/block-platform/website-source-block.js +0 -14
@@ -9,7 +9,7 @@
9
9
  import {HOSTED_OPERATION_NAMES} from './contracts.js';
10
10
 
11
11
  export const BUILDER_CONTRACT_ID='stillmade.block-builder';
12
- export const BUILDER_CONTRACT_VERSION='1.16.0';
12
+ export const BUILDER_CONTRACT_VERSION='1.17.0';
13
13
  export const BUILDER_CONTRACT_STAMP=`${BUILDER_CONTRACT_ID}@${BUILDER_CONTRACT_VERSION}`;
14
14
  export const BUILDER_CONTRACT_DOCS_PATH='/docs/build/block-builder-contract';
15
15
  // Block names appear in step bars, cards and pickers; new releases keep them short.
@@ -47,9 +47,11 @@ const SECTIONS=Object.freeze([
47
47
  // on module Blocks and refused a job that needs one).
48
48
  rule('runtime-choice','block',null,
49
49
  'Pick the runtime from the job. No hosted AI: a recipe or isolated `javascript`, both able to run unattended. One text, script or brief prompt returning text, script or JSON: a `capability` Block. Hosted operations on other values (a transcript, media, structured data), several hosted operations in a row, or a result shaped in code: one `module` Block, `module/main.js` calling `stillmade.hosted.run(operation, request)` for each operation in `permissions.capabilities` (MODULES.md), never one Block per operation; it runs in the person\'s browser while StillMade is open. Build a new Block in a folder with `stillmade-block create`, `test`, `ready` and `pack`, and import the file `pack` writes (a module Block writes `<id>-<version>.stillmade-module.json`) through Create, then Import Block. A `.stillmade-block` archive is only for adapting a GitHub repository.'),
50
+ // November 3, 2026 (owner, "Yes, one page"): a guided job is one Block on one page, its cheap check before the
51
+ // expensive run on that page too; a workspace (CAD, the Editor) stays its own Step, and using what it made is the next.
50
52
  rule('block-scope','both',
51
- 'One Step per job: start one only where a person works with the result, where finding new items turns into handling each, or where the result leaves StillMade; all else is a part.',
52
- 'A Step is one job: the work between two moments where a person or the outside world must see the result. Start a new Step only at one of three points; everything else is a part of the neighboring Step.\n\n1. **A person works with the result**: reads, edits, picks or approves it (a workspace Block such as a script, a Canvas or an Editor, or an approval). So Bible, Canvas and Editor are separate Steps after Chat.\n2. **Finding new things turns into handling each one**: the trigger (a schedule or an app event), the app read, its filter and "only new" are the first job; what is done with each item is the next job. A trigger is a setting of the first job, never a Step of its own.\n3. **The result leaves StillMade**: it is sent, posted, published, paid or written into another app. Consecutive writes to the same app, with the glue that shapes data for them, are one job.\n\nIn a production the Steps are the workspaces a person opens: work feeding the next workspace is its part, else it joins the one before. A Block with its own interface (a workspace, editor or picker) is always its own Step; two such Steps are never one. A video ends in the Editor (1.0), which has captions and export built in, so Export video is never a Step beside it.\n\nA part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of the three reasons makes it separate; if none applies, make it a part of a neighbor. A Step nobody would ever open must not exist on its own. Prefer an app task\'s own filter (a Gmail query such as `has:attachment filename:png`) to an AI check, and use AI decide only for judgement.\n\nBuild one Block for a job one Block can do: a module Block calls every hosted operation the job needs (it transcribes a sermon, then finds the verses said in it), a JavaScript Block does several local transforms, a workspace owns its whole document. A Block being built never takes an ingredient\'s result only so the ingredient can be its own part or Step (Transcribe in front of a verse finder): it calls that operation itself. Parts in one Step (`groups` in the Project Type) are for jobs made of Blocks that already exist and app tasks, each keeping its own permission, approval and credit rules. Never invent a combined runtime.\n\nExamples: "every new email with an image becomes a generated image" is two Steps, "Gmail · New emails with images" (When, fetch with an image filter, only new) and "Image from email" (AI write, Generate image); listing When, Fetch, Only new, AI decide, Continue only if, AI write and Generate image as seven Steps splits one job into its parts. "A new Meta lead goes to HubSpot" is three Steps: "Meta Ads · New leads", "Qualify lead" and "HubSpot · Add contact".'),
53
+ 'One Step per job: a guided job is one Block whose page holds the inputs, a cheap check and the result. New Steps only for a workspace (CAD, Editor; using its work is the next Step), a one-time setup, a later render, a hand-off, handling each new item, or leaving StillMade.',
54
+ 'A Step is one job: the work between two moments where a person or the outside world must see the result. A guided job, one the person fills in and then gets a result from, is one Step with one Block whose own screen holds everything: the inputs and settings, then a cheap check before the expensive run (the script and a voice sample before the video), then the result. On a wide screen the inputs sit on the left and the result\'s place on the right; on a phone they stack, the result\'s place under the button. A cheap check or a review the person does on that screen never starts a new Step. Start a new Step only at one of these points; everything else is a part of the neighboring Step.\n\n1. **A person works with the result**: in a workspace they spend a while in (the Editor, a storyboard, a Canvas, a CAD or drawing tool), or someone else approves or takes it over (a hand-off). Turning what a workspace made into something else is the next Step: a CAD tool is its own Step, and the Block that takes pictures of its model and makes a video is the next one. So Bible, Canvas and Editor are separate Steps after Chat.\n2. **Finding new things turns into handling each one**: the trigger (a schedule or an app event), the app read, its filter and "only new" are the first job; what is done with each item is the next job. A trigger is a setting of the first job, never a Step of its own.\n3. **The result leaves StillMade**: it is sent, posted, published, paid or written into another app. Consecutive writes to the same app, with the glue that shapes data for them, are one job.\n4. **Setup or a wait**: a one-time setup that is not part of each job (a brand kit, capturing a voice or a likeness), or a render that takes minutes and is delivered later.\n\nIn a production the Steps are the workspaces a person opens: work feeding the next workspace is its part, else it joins the one before. A Block with its own interface (a workspace, editor or picker) is always its own Step; two such Steps are never one. A Type whose own Block renders the finished video (`timeline.render`) needs no Editor Step; the Editor (1.0, captions and export built in) is a Step only when the person edits the cut by hand, and Export video is never a Step beside it.\n\nA part that only runs in the desktop app cannot share a Step with cloud parts. Before adding a Step, name which of these reasons makes it separate; if none applies, make it a part of a neighbor. A Step nobody would ever open must not exist on its own. Prefer an app task\'s own filter (a Gmail query such as `has:attachment filename:png`) to an AI check, and use AI decide only for judgement.\n\nBuild one Block for a job one Block can do: a module Block calls every hosted operation the job needs (it transcribes a sermon, then finds the verses said in it), a JavaScript Block does several local transforms, a workspace owns its whole document. A Block being built never takes an ingredient\'s result only so the ingredient can be its own part or Step (Transcribe in front of a verse finder): it calls that operation itself. Parts in one Step (`groups` in the Project Type) are for jobs made of Blocks that already exist and app tasks, each keeping its own permission, approval and credit rules. Never invent a combined runtime.\n\nExamples: "a listing video from my photos and the home\'s facts" is one Step: one Block whose page takes the photos and facts, shows the script and a voice sample to check, then makes the video. "every new email with an image becomes a generated image" is two Steps, "Gmail · New emails with images" (When, fetch with an image filter, only new) and "Image from email" (AI write, Generate image); listing When, Fetch, Only new, AI decide, Continue only if, AI write and Generate image as seven Steps splits one job into its parts. "A new Meta lead goes to HubSpot" is three Steps: "Meta Ads · New leads", "Qualify lead" and "HubSpot · Add contact".'),
53
55
  rule('capability-contract','block',
54
56
  'Define the contract first: typed inputs (optional with safe defaults where possible), outputs, state, permissions, UI, failures and cancellation. Keep the interface small; keep the job whole.',
55
57
  'Translate the request into an explicit capability contract before implementation: what the Block does; each input with its type, meaning and whether it is required (prefer optional settings with safe defaults over extra required questions); each output with its type and meaning; persistent state; project context it reads; permissions; UI; long-running behavior; expected failures; cancellation; determinism; and whether it needs hosted models, media, desktop tools or shared editing. Keep the public interface as small and general as possible (few required inputs, one primary output) and keep implementation details out of it; small means a small interface, never a fragment of a job. A Block that generates images, video or speech declares only its prompt and output: every Step that runs it gets the host’s generation settings (model, quality, length and resolution, voice, speed, how many), saved with the Step, changed on its page or by Chat, and shown with their cost before each run.'),
@@ -71,30 +73,30 @@ const SECTIONS=Object.freeze([
71
73
  // November 2, 2026 (owner): a sermon Block's video input read "Video"; it should read "Drop your sermon video here".
72
74
  rule('input-words','block',
73
75
  'Name each input in the job\'s words (`sermonVideo`, not `video`): StillMade words its drop area from the name.',
74
- 'Name each input for what the person brings, in the job\'s words: `sermonVideo` for a sermon clip Block, `listingPhotos` for a listing, `lessonSlides` for a lesson podcast. StillMade titles the input from that name ("Sermon video") and words its drop area from it ("Drop your sermon video here", the formats, "Choose a video"), in StillMade and the SDK preview, so never draw your own upload box for an input. Keep a bare type name (`video`, `file`) only when any file of that kind does the job. The `description` says what makes a good one.'),
76
+ 'Name each input for what the person brings, in the job\'s words: `sermonVideo` for a sermon clip Block, `listingPhotos` for a listing, `lessonSlides` for a lesson podcast. StillMade titles the input from that name ("Sermon video") and words its drop area from it ("Drop your sermon video here", the formats, "Choose a video"), in StillMade and the SDK preview, so never draw your own upload box or `<input type="file">`: a Block\'s screen shows the files as tiles and adds more with `<button class="sm-add" data-stillmade-add="listingPhotos" aria-label="Add photos">`, which opens StillMade\'s picker and upload, and a list\'s tiles drop or reorder a file with `data-stillmade-remove="listingPhotos" data-index="2"` and `data-stillmade-move="listingPhotos" data-index="2" data-to="1"` buttons; the files arrive through `onInput`. Keep a bare type name (`video`, `file`) only when any file of that kind does the job. The `description` says what makes a good one.'),
75
77
  // November 2, 2026 (owner): every screen speaks the job, as a product made only for it would, and stays quiet. The same
76
78
  // day (owner, of four tiles each saying "Only on the device it was added from"): "if it's a notification make it a
77
79
  // notification": state is one StillMade notification or nothing, never a sentence on the page or one per item.
78
80
  rule('job-words','block',
79
- 'Speak the job unless the person or creator asks otherwise: `manifest.action` names the button ("Plan the tour"; AI pictures, video or sound start with Generate, never "Make it"), one solid `sm-primary`, progress in the job\'s units, one line per problem, no notes on how the Block works or its state (one StillMade notification at most, never one per item), `date` inputs for days and times, plain input names so "Use a reference" fills them, posting as its own connector Step. Clear every Wording note `gate` lists.',
80
- 'These are StillMade\'s defaults: unless the person or the creator asked for something else, every screen speaks the job in its own words and adds nothing else. `manifest.action` names the Step\'s button, {label:"Plan the tour", running:"Planning the tour…"}, and StillMade adds the price ("Plan the tour · 1 credit"). When the Block makes a picture, video or sound with AI the label starts with Generate and says what ("Generate tour", "Generate voiceover"); one that cuts or renders the person\'s own media says Make ("Make 6 clips"); never "Make it", "Run", "Submit", "Process" or a bare "Generate". The main button stands out: one solid `sm-primary` in the kit\'s ink, never restyled to the background; everything else is `sm-quiet` or an `sm-icon`, and close is an X icon. Report progress in the job\'s units with `stillmade.progress(fraction, label)` ("Moving through the kitchen, 4 of 9 rooms"), never "Processing". Head the result in the job\'s words ("9 rooms", "Your tour · 44 s"), never "Output". Say a problem in one line with its fix beside it ("No bathroom photo. Guests look for one." and Add photo). Stay quiet: no welcome text, tips under fields, tours or banners, and at most one line of guidance on a screen, only where the next move is not obvious; anything more goes in the input\'s `description`, which StillMade shows behind an (i). Say nothing about how the Block works (where it runs, saving, syncing, recovery, accounts, credits) or where a file is kept: StillMade tells the person what they need as one short notification, or nothing; never a sentence on the page, and never the same line under each item. Every drop area offers "Use a reference", which fills the Step from the person\'s saved photos, videos and notes: media inputs fill by kind, so take `video` or `asset` where a clip works as well as a photo, and text inputs fill by plain names and labels (`productName`, `website`, `brandColors`, `fonts`, `voice`, and `details` or `notes` for the reference\'s note); never build your own reference picker. Days and times are picked, never typed: use the `date` type, which StillMade shows as a calendar, with a list of times when the input is about a time (`postAt`, `startsAt`); a custom view uses `<input type="date">` or `type="datetime-local"`. Posting is a connector Step of its own, and StillMade words its button from the app and action ("Post to Instagram"). Before handing the Block back, clear every Wording note that `gate` and readiness list, unless the person asked for those words.'),
81
+ 'Speak the job unless the person or creator asks otherwise: `manifest.action` names the button, required whenever StillMade draws it ("Plan the tour"; AI pictures, video or sound start with Generate, never "Make it"), one solid `sm-primary`, progress in the job\'s units, one line per problem, no notes on how the Block works or its state (one StillMade notification at most, never one per item), `date` inputs for days and times, plain input names so "Use a reference" fills them, posting as its own connector Step. Clear every Wording note `gate` lists.',
82
+ 'These are StillMade\'s defaults: unless the person or the creator asked for something else, every screen speaks the job in its own words and adds nothing else. `manifest.action` names the Step\'s button, {label:"Plan the tour", running:"Planning the tour…"}, and StillMade adds the price ("Plan the tour · 1 credit"). Every Block whose Step shows StillMade\'s button declares it: any Block without its own screen, and any that calls operations. Without it StillMade can only guess from the Block\'s name or output ("Make clips"), and the readiness gate and StillMade\'s own builder send the Block back until it names the job. When the Block makes a picture, video or sound with AI the label starts with Generate and says what ("Generate tour", "Generate voiceover"); one that cuts or renders the person\'s own media says Make ("Make 6 clips"); never "Make it", "Run", "Submit", "Process" or a bare "Generate". The main button stands out: one solid `sm-primary` in the kit\'s ink, never restyled to the background; everything else is `sm-quiet` or an `sm-icon`, and close is an X icon. Report progress in the job\'s units with `stillmade.progress(fraction, label)` ("Moving through the kitchen, 4 of 9 rooms"), never "Processing". Head the result in the job\'s words ("9 rooms", "Your tour · 44 s"), never "Output". Say a problem in one line with its fix beside it ("No bathroom photo. Guests look for one." and Add photo). Stay quiet: no welcome text, tips under fields, tours or banners, and at most one line of guidance on a screen, only where the next move is not obvious; anything more goes in the input\'s `description`, which StillMade shows behind an (i). Say nothing about how the Block works (where it runs, saving, syncing, recovery, accounts, credits) or where a file is kept: StillMade tells the person what they need as one short notification, or nothing; never a sentence on the page, and never the same line under each item. Every drop area offers "Use a reference", which fills the Step from the person\'s saved photos, videos and notes: media inputs fill by kind, so take `video` or `asset` where a clip works as well as a photo, and text inputs fill by plain names and labels (`productName`, `website`, `brandColors`, `fonts`, `voice`, and `details` or `notes` for the reference\'s note); never build your own reference picker. Days and times are picked, never typed: use the `date` type, which StillMade shows as a calendar, with a list of times when the input is about a time (`postAt`, `startsAt`); a custom view uses `<input type="date">` or `type="datetime-local"`. Posting is a connector Step of its own, and StillMade words its button from the app and action ("Post to Instagram"). Before handing the Block back, clear every Wording note that `gate` and readiness list, unless the person asked for those words.'),
81
83
  // November 2, 2026 (owner): StillMade centers and zooms a screen; an app that lays itself out (a CAD tool) opts out with
82
84
  // data-stillmade-layout="own" and keeps its own layout and Ctrl/Cmd +/- keys. The same day (owner: "implement A absolutely
83
85
  // universally, like in app, platform, sdk, just everywhere"): every <video controls> gets StillMade's player and result
84
86
  // pictures its full-screen button; a video without controls stays the Block's own. Also that day (owner: "there is no
85
87
  // separation and I cannot tell what is going on"): a pick is the kit's ink fill, and media that cannot show is a quiet tile.
86
88
  rule('ui','block',
87
- 'Prefer `manifest.ui`; custom views use the Block kit (`sm-*`) unless a look is named. Pictures over words: preview each option before choosing; few words, fold the rest; buttons carry `data-stillmade-action`; the start button names the action (never "Run" or "Make it") and shows no price (StillMade adds it). A job fits one screen unscrolled and a 320px phone; an app (editor, configurator, CAD tool) works like that app: full canvas, panels, tools, undo, never a form. An app puts `data-stillmade-layout="own"` on its root: StillMade never centers or zooms it. `<video controls>` gets StillMade\'s player; leave out `controls` when the Block drives playback. A pick keeps the kit\'s ink fill; media that cannot show is a wordless `sm-media-off` tile.',
88
- 'If the capability needs UI, use the Block UI SDK: generated `manifest.ui` controls first; a sandboxed custom view (`view.html`/`view.css`/`view.js`) only when the core interaction needs one. Build custom views from the Block kit the host injects (`block-kit.css`): the result in `sm-stage`, picks as pictures in `sm-choices`, the picked one in the kit\'s ink fill (never restyled to an outline, a tint or the page\'s background) so it reads at a glance, one solid `sm-primary` naming the action in the job\'s words ("Plan the tour", "Generate voiceover"; never "Run" or "Make it") and no price (StillMade\'s run button adds it), extras in a closed `sm-more`; a look the person names wins. Fit one laptop screen without scrolling: label beside control, status in a few words, files as tiles, rare tools as icons, (i) on unclear controls. These compact rules are for a job or an ingredient. Size the view to the Block\'s tier (block-tier.js): an app, a workspace a person works in for a while (video editor, 3D configurator, CAD or drawing tool), looks and works like that kind of app instead of a compact page: its canvas or stage fills the view, with the panels, toolbars, inspector or timeline such an app has, direct manipulation (select, drag, zoom), keyboard shortcuts and undo; it keeps the host appearance and Block kit controls, its panels may scroll, and on a phone its tools fold into sheets while the canvas stays usable. Never shrink an app into a form of fields and one button. StillMade centers a standard view narrower than its Step and zooms a view to fill its space; an app that lays itself out (full-size canvas or WebGL, its own scroll, pan or zoom, its own Ctrl/Cmd +/- keys) puts `data-stillmade-layout="own"` on its outermost element, and StillMade then never centers, zooms or takes those keys. StillMade draws one player for every `<video controls>` (play, timeline, time, sound and full screen, shown while the pointer is on the video, with keys and touch) and a full-screen button on result pictures in `sm-stage` or `sm-gallery` and on any element marked `data-stillmade-expand` (the same value makes one set of pictures, `off` removes it); `data-stillmade-player="native"` keeps the browser\'s controls, and a `<video>` without `controls` is the Block\'s own and is never touched. A picture or clip that cannot show here (kept on another device, or it did not open) keeps its place as a quiet `sm-media-off` tile in its own shape with the `image-off` glyph and the reason in its label, never words in its place. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Mark each button `data-stillmade-action="what it does"` and label each field: Chat presses and fills them. Expose the capability, not the implementation: sensible defaults, previews, progress, clear errors, visible outputs. Show no raw JSON, internal IDs, filesystem details, provider terms or developer settings unless the Block is a developer tool. When a person chooses among options they need to hear or see (voices, music, fonts, looks, templates, images), let them preview each option before choosing: a sample or thumbnail from the Block\'s own files or the host\'s free samples, never a paid generation. Skip previews only when there is nothing to preview. Choices StillMade owns (voices, models in the run review) already show samples. Follow the host appearance (Light, Dark and White) and one elastic layout: stacked on a 320px phone with no sideways scroll (result first, action pinned), and from 860px the controls left (≤380px), the result filling the right. Short settings sit side by side, each label beside its control (`sm-field`, `sm-settings`; three or four short options as one `data-segmented` row); many items are one line each (`sm-list`) that open in place; input files are small tiles (`sm-gallery data-size="small"`); on a phone chip rows scroll sideways and long lists show three. Compacting moves controls, never deletes them.'),
89
+ 'Prefer `manifest.ui`; custom views use the Block kit (`sm-*`) unless a look is named. Pictures over words: preview each option before choosing; few words, fold the rest; buttons carry `data-stillmade-action`; the start button names the action (never "Run" or "Make it") and shows no price (StillMade adds it). A job fits one screen unscrolled and a 320px phone; an app (editor, configurator, CAD tool) works like that app: full canvas, panels, tools, undo, never a form. An app puts `data-stillmade-layout="own"` on its root: StillMade never centers or zooms it. `<video controls>` gets StillMade\'s player; leave out `controls` when the Block drives playback. A pick keeps the kit\'s ink fill; media that cannot show is a wordless `sm-media-off` tile. Every part shows at once: the result\'s place is drawn before the first run in its own shape and size, so nothing pops in; on wide screens inputs sit left and the result right; on phones they stack, the result under the button.',
90
+ 'If the capability needs UI, use the Block UI SDK: generated `manifest.ui` controls first; a sandboxed custom view (`view.html`/`view.css`/`view.js`) only when the core interaction needs one. Build custom views from the Block kit the host injects (`block-kit.css`): the result in `sm-stage`, drawn before the first run in the shape and size of what is coming (`data-ratio="9:16"` for a vertical clip, lines for text, a player bar for sound) so every part of the screen is in place at once and the result fills a place the person already sees instead of popping in somewhere unexpected; StillMade draws it the same way on the Steps it draws; on a wide screen what goes in sits on the left and the result on the right, side by side, and they stack only on narrow screens and phones, the result\'s place under the button, picks as pictures in `sm-choices`, the picked one in the kit\'s ink fill (never restyled to an outline, a tint or the page\'s background) so it reads at a glance, one solid `sm-primary` naming the action in the job\'s words ("Plan the tour", "Generate voiceover"; never "Run" or "Make it") and no price (StillMade\'s run button adds it), extras in a closed `sm-more`; a look the person names wins. Fit one laptop screen without scrolling: label beside control, status in a few words, files as tiles, rare tools as icons, (i) on unclear controls. These compact rules are for a job or an ingredient. Size the view to the Block\'s tier (block-tier.js): an app, a workspace a person works in for a while (video editor, 3D configurator, CAD or drawing tool), looks and works like that kind of app instead of a compact page: its canvas or stage fills the view, with the panels, toolbars, inspector or timeline such an app has, direct manipulation (select, drag, zoom), keyboard shortcuts and undo; it keeps the host appearance and Block kit controls, its panels may scroll, and on a phone its tools fold into sheets while the canvas stays usable. Never shrink an app into a form of fields and one button. StillMade centers a standard view narrower than its Step and zooms a view to fill its space; an app that lays itself out (full-size canvas or WebGL, its own scroll, pan or zoom, its own Ctrl/Cmd +/- keys) puts `data-stillmade-layout="own"` on its outermost element, and StillMade then never centers, zooms or takes those keys. StillMade draws one player for every `<video controls>` (play, timeline, time, sound and full screen, shown while the pointer is on the video, with keys and touch) and a full-screen button on result pictures in `sm-stage` or `sm-gallery` and on any element marked `data-stillmade-expand` (the same value makes one set of pictures, `off` removes it); `data-stillmade-player="native"` keeps the browser\'s controls, and a `<video>` without `controls` is the Block\'s own and is never touched. A picture or clip that cannot show here (kept on another device, or it did not open) keeps its place as a quiet `sm-media-off` tile in its own shape with the `image-off` glyph and the reason in its label, never words in its place. Drop words nobody needs; put helpful ones in a closed `sm-hint`. Mark each button `data-stillmade-action` (`run` on the one that runs the Block, which the device checks press once; a page that checks first keeps `run` on that button for both presses; what it does on the others, `"play sample"`) and label each field: Chat presses and fills them. Expose the capability, not the implementation: sensible defaults, previews, progress, clear errors, visible outputs. Show no raw JSON, internal IDs, filesystem details, provider terms or developer settings unless the Block is a developer tool. When a person chooses among options they need to hear or see (voices, music, fonts, looks, templates, images), let them preview each option before choosing: a sample or thumbnail from the Block\'s own files or the host\'s free samples, never a paid generation. Skip previews only when there is nothing to preview. Choices StillMade owns (voices, models in the run review) already show samples. Follow the host appearance (Light, Dark and White) and one elastic layout: stacked on a 320px phone with no sideways scroll (the inputs, the button in normal flow, never pinned, then the result\'s place under it), and from 860px the controls left (≤380px), the result filling the right. Short settings sit side by side, each label beside its control (`sm-field`, `sm-settings`; three or four short options as one `data-segmented` row); many items are one line each (`sm-list`) that open in place; input files are small tiles (`sm-gallery data-size="small"`); on a phone chip rows scroll sideways and long lists show three. Compacting moves controls, never deletes them.'),
89
91
  rule('multiplayer','block',
90
92
  'Custom views share edits: `StillMade.onShared`, publish every editable field (`data-stillmade-share`, `bindShared`, `updateShared`), respect `canEdit`, never re-run on remote updates.',
91
93
  'Projects are multiplayer. A custom view subscribes through `StillMade.onShared(callback)` and publishes every editable field: give static controls a unique `id` and `data-stillmade-share="field"` (the host owns their synchronization), use `StillMade.bindShared(field, elementId)` for controls created dynamically, and `StillMade.updateShared(values, before, options)` for structured values; private variables and DOM values are not shared. Mark a truly device-only control (playback, hover, device selection) with `data-stillmade-local`. Observe shared outputs as well as state, honor `canEdit` in every mutating callback (read-only participants cannot write), never run, click or call a paid integration in response to a remote update, and preserve conflicting drafts. Shared background work uses `sharedWork` with the `work.shared` permission.'),
92
94
  rule('external-services','block',
93
95
  'External services only via hosted operations or `connection.execute`; the host owns providers, keys, BYOK, credits and consent.',
94
- 'Reach external services only through StillMade\'s hosted operations, the connections service (`connection.execute`), or, in a module Block, `stillmade.net.fetch` to origins declared in `permissions.network` (with the key each needs), `stillmade.net.connect` for a live WebSocket to one of them, and `stillmade.connections.call` to adapters or catalog apps in `permissions.connections`; never put keys in code, inputs or headers: StillMade adds each person\'s own key on its servers. The host owns the model and provider choice, BYOK keys, StillMade credits, spending consent, authorization, token refresh, rate limits, retries and provider errors; a Block never receives a secret and never names a provider unless it must. Treat provider unavailability, timeout, cancellation and partial failure as typed outcomes the Block reports, not crashes. Generated images, video, voices, music and sound effects come from hosted operations or the `media.generate` action, paid with the user\'s StillMade credits at StillMade\'s prices; for structured model output declare a `json` output with `capability.schema` instead of parsing prose. When a Block needs npm libraries, WebAssembly (threaded: `"threads": true` in module.json) or heavy compute, use `runtime: "module"` and reach hosted operations through `stillmade.hosted.run` with the operations listed in `permissions.capabilities`. Decode, cut, join, re-encode, extract or normalise audio and video with `stillmade.media.transform` on the device; never ask for a server render. For a rich interactive interface (React or another framework, SVG, canvas, drag), use a frame view: `src/view.config.json` with `{"runtime":"frame"}` and `src/view.js` bundled as one IIFE, colored with StillMade\'s theme variables. For files and structured data use the `file`, `document`, `table`, `url`, `date` and `color` types and their generated controls and presentations, not JSON text.'),
96
+ 'Reach external services only through StillMade\'s hosted operations, the connections service (`connection.execute`), or, in a module Block, `stillmade.net.fetch` to origins declared in `permissions.network` (with the key each needs), `stillmade.net.connect` for a live WebSocket to one of them, and `stillmade.connections.call` to adapters or catalog apps in `permissions.connections`; never put keys in code, inputs or headers: StillMade adds each person\'s own key on its servers. The host owns the model and provider choice, BYOK keys, StillMade credits, spending consent, authorization, token refresh, rate limits, retries and provider errors; a Block never receives a secret and never names a provider unless it must. Treat provider unavailability, timeout, cancellation and partial failure as outcomes the Block reports, not crashes: a call the result needs throws and fails the run (see Useful failures); an optional part\'s failure is caught, given its fallback and named in an optional `notes` text output. Generated images, video, voices, music and sound effects come from hosted operations or the `media.generate` action, paid with the user\'s StillMade credits at StillMade\'s prices; for structured model output declare a `json` output with `capability.schema` instead of parsing prose. When a Block needs npm libraries, WebAssembly (threaded: `"threads": true` in module.json) or heavy compute, use `runtime: "module"` and reach hosted operations through `stillmade.hosted.run` with the operations listed in `permissions.capabilities`. Decode, cut, join, re-encode, extract or normalise audio and video with `stillmade.media.transform` on the device; never ask for a server render. For a rich interactive interface (React or another framework, SVG, canvas, drag), use a frame view: `src/view.config.json` with `{"runtime":"frame"}` and `src/view.js` bundled as one IIFE, colored with StillMade\'s theme variables. For files and structured data use the `file`, `document`, `table`, `url`, `date` and `color` types and their generated controls and presentations, not JSON text.'),
95
97
  rule('dependencies','block',
96
- 'No npm or system dependencies; inline small licensed code and record its license and notice in the manifest.',
97
- 'A Block must declare everything it requires and must not assume anything is installed. Portable packages carry no package dependencies (`portable.dependencies` is empty): inline small, license-compatible code and record its license and notice in `manifest.license` and `manifest.provenance`. Desktop-only tools are reached solely through declared desktop permissions and the host desktop bridge. If a dependency cannot be delivered through the Block runtime, report the incompatibility instead of assuming the user\'s machine has it.'),
98
+ 'No npm or system dependencies; inline small licensed code and record its license and notice in the manifest; the person is the copyright holder.',
99
+ 'A Block must declare everything it requires and must not assume anything is installed. Portable packages carry no package dependencies (`portable.dependencies` is empty): inline small, license-compatible code and record its license and notice in `manifest.license` and `manifest.provenance`. The copyright holder in the Block\'s own `provenance.notice` is the person it is made for: replace the placeholder `create` writes with their name, never StillMade, the SDK or yourself. Desktop-only tools are reached solely through declared desktop permissions and the host desktop bridge. If a dependency cannot be delivered through the Block runtime, report the incompatibility instead of assuming the user\'s machine has it.'),
98
100
  rule('security','both',
99
101
  'Treat inputs, files and responses as untrusted; request minimum permissions; never leak secrets in outputs, state, errors or UI.',
100
102
  'Treat Block code, external data, files, repositories, model output and network responses as untrusted. Request the minimum permissions and capabilities; `stillmade-block permissions --fix` declares exactly what the source uses. Do not weaken the sandbox (QuickJS has no fetch, DOM, timers, modules or Node APIs). Validate and bound every value crossing a boundary. Never expose secrets through logs, outputs, project context, UI, error messages or serialized state.'),
@@ -187,7 +187,7 @@ export function validateCapability(capability,manifest){
187
187
  assert(frames.length<2||capability.firstFrame.$input!==capability.lastFrame.$input,'CAPABILITY_BINDING','Use different image inputs for the first and last frame');
188
188
  assert(!own(capability,'lastFrame')||own(capability,'firstFrame'),'CAPABILITY_BINDING','A last frame needs a first frame');
189
189
  assert(Object.values(manifest.inputs).filter(port=>port.type==='image').length===frames.length,'CAPABILITY_BINDING','Bind every image input as the first or last frame');
190
- if(own(capability,'settings')){const settings=videoSettings(capability.settings,{strict:true,firstFrame:own(capability,'firstFrame')});const entry=VIDEO_GENERATION_MODELS[settings.model];assert(!own(capability,'lastFrame')||entry.lastFrame,'CAPABILITY_SETTINGS',`${entry.label} does not accept a last frame`);}
190
+ if(own(capability,'settings')){const settings=videoSettings(capability.settings,{strict:true,firstFrame:own(capability,'firstFrame'),lastFrame:own(capability,'lastFrame')});const entry=VIDEO_GENERATION_MODELS[settings.model];assert(!own(capability,'lastFrame')||entry.lastFrame,'CAPABILITY_SETTINGS',`${entry.label} does not accept a last frame`);}
191
191
  generationText(capability);
192
192
  assert(typeof capability.output==='string'&&own(manifest.outputs,capability.output),'CAPABILITY_BINDING','Use the declared video output port');return true;
193
193
  }
@@ -2,7 +2,7 @@ import {EXAMPLE_PROVENANCE} from './example-license.js';
2
2
  // Portable request; provider/model/payment and real execution approval are
3
3
  // selected in StillMade. These fixtures are expectations, not recorded results.
4
4
  export const textGenerate={
5
- manifest:{schemaVersion:1,sdkVersion:'0.1.0',id:'example.script-draft',version:'1.0.0',name:'Draft a short script',description:'Turn a production brief into a short narration draft using an approved text-generation call.',kind:'task',runtime:'capability',entry:'src/capability.json',license:'MIT',provenance:EXAMPLE_PROVENANCE,inputs:{prompt:{type:'text',primary:true,semantic:'production_brief',required:true,description:'Production brief that guides the narration draft.'}},outputs:{script:{type:'script',primary:true,semantic:'narration_script',description:'A concise narration script based on the supplied brief.'}},permissions:{project:[],capabilities:['text.generate'],network:[],filesystem:[],secrets:[]},ui:[{control:'text',port:'prompt'}]},
5
+ manifest:{schemaVersion:1,sdkVersion:'0.1.0',id:'example.script-draft',action:{label:'Write script',running:'Writing script…'},version:'1.0.0',name:'Draft a short script',description:'Turn a production brief into a short narration draft using an approved text-generation call.',kind:'task',runtime:'capability',entry:'src/capability.json',license:'MIT',provenance:EXAMPLE_PROVENANCE,inputs:{prompt:{type:'text',primary:true,semantic:'production_brief',required:true,description:'Production brief that guides the narration draft.'}},outputs:{script:{type:'script',primary:true,semantic:'narration_script',description:'A concise narration script based on the supplied brief.'}},permissions:{project:[],capabilities:['text.generate'],network:[],filesystem:[],secrets:[]},ui:[{control:'text',port:'prompt'}]},
6
6
  capability:{schemaVersion:1,operation:'text.generate',prompt:{$input:'prompt'},system:'Write a concise narration script. Return only the script text.',maxTokens:1200,output:'script'},
7
7
  tests:[{name:'A short opening narration',input:{prompt:'Write two narration sentences about a quiet forest at sunrise.'},expectations:{script:{kind:'script',minLength:20,maxLength:6000}}}],
8
8
  };
@@ -235,9 +235,11 @@ export function validateManifest(manifest, { builtIn = false } = {}) {
235
235
  check(plain(permissions), 'permissions', 'Declare permissions explicitly');
236
236
  for (const [key, scopes] of Object.entries(plain(permissions) ? permissions : {})) {
237
237
  check(['project', 'network', 'connections', 'filesystem', 'secrets', 'capabilities', 'actions', 'desktop'].includes(key), `permissions.${key}`, 'Unknown permission family');
238
- // Network entries may be {origin, auth} objects; every other family is a string list.
238
+ // Network entries may be {origin, auth} objects; every other family is a string list. A module Block's empty
239
+ // capabilities list is the same as none (November 3, 2026: a module that only calls actions.run was refused for
240
+ // `capabilities: []` with a message about capability and ComfyUI Blocks).
239
241
  check(Array.isArray(scopes) && scopes.every(s => typeof s === 'string' || key === 'network' && plain(s)), `permissions.${key}`, 'Expected string array');
240
- if (Array.isArray(scopes)) check(key === 'desktop' ? scopes.length<=DESKTOP_PERMISSIONS.length && new Set(scopes).size===scopes.length && scopes.every(s=>DESKTOP_PERMISSIONS.includes(s)) : key === 'project' ? scopes.every(s => SCOPES.includes(s)) : key === 'actions' ? ['javascript','module'].includes(manifest.runtime)&&scopes.length<=PORTABLE_ACTIONS.length&&new Set(scopes).size===scopes.length&&scopes.every(s=>PORTABLE_ACTIONS.includes(s)) : key === 'capabilities' ? (manifest.runtime === 'module' ? scopes.length >= 1 && scopes.length <= CAPABILITY_RUN_OPERATIONS.length && new Set(scopes).size === scopes.length && scopes.every(s => CAPABILITY_RUN_OPERATIONS.includes(s)) : scopes.length === 1 && (manifest.runtime === 'comfyui' && scopes[0] === 'comfyui.execute' || manifest.runtime === 'capability' && HOSTED_OPERATION_NAMES.includes(scopes[0]))) : key === 'network' && manifest.runtime === 'module' ? networkValid(scopes) : key === 'connections' ? manifest.runtime === 'module' && connectionsValid(scopes) : scopes.length === 0, `permissions.${key}`, key === 'connections' ? 'Unsupported permission: only module Blocks declare connections, up to 8 published adapters by app ID such as app:openapi:notion' : key === 'network' ? (manifest.runtime === 'module' ? `Declare network origins as https origins, optionally with {origin, auth}: ${networkProblem(scopes)}` : 'Unsupported permission: only module Blocks declare network origins; other runtimes reach services through hosted operations and connections') : key === 'capabilities' ? (['module','capability','comfyui'].includes(manifest.runtime) ? 'Declare each hosted operation once, from the supported list (a capability or ComfyUI Block declares exactly one)' : 'Unsupported permission: only capability, ComfyUI and module Blocks declare hosted operations. A javascript or recipe Block declares none: remove permissions.capabilities, or make the AI work its own capability Block') : 'Unsupported permission; Blocks have no ambient filesystem or secrets');
242
+ if (Array.isArray(scopes)) check(key === 'desktop' ? scopes.length<=DESKTOP_PERMISSIONS.length && new Set(scopes).size===scopes.length && scopes.every(s=>DESKTOP_PERMISSIONS.includes(s)) : key === 'project' ? scopes.every(s => SCOPES.includes(s)) : key === 'actions' ? ['javascript','module'].includes(manifest.runtime)&&scopes.length<=PORTABLE_ACTIONS.length&&new Set(scopes).size===scopes.length&&scopes.every(s=>PORTABLE_ACTIONS.includes(s)) : key === 'capabilities' ? (manifest.runtime === 'module' ? scopes.length <= CAPABILITY_RUN_OPERATIONS.length && new Set(scopes).size === scopes.length && scopes.every(s => CAPABILITY_RUN_OPERATIONS.includes(s)) : scopes.length === 1 && (manifest.runtime === 'comfyui' && scopes[0] === 'comfyui.execute' || manifest.runtime === 'capability' && HOSTED_OPERATION_NAMES.includes(scopes[0]))) : key === 'network' && manifest.runtime === 'module' ? networkValid(scopes) : key === 'connections' ? manifest.runtime === 'module' && connectionsValid(scopes) : scopes.length === 0, `permissions.${key}`, key === 'connections' ? 'Unsupported permission: only module Blocks declare connections, up to 8 published adapters by app ID such as app:openapi:notion' : key === 'network' ? (manifest.runtime === 'module' ? `Declare network origins as https origins, optionally with {origin, auth}: ${networkProblem(scopes)}` : 'Unsupported permission: only module Blocks declare network origins; other runtimes reach services through hosted operations and connections') : key === 'capabilities' ? (['module','capability','comfyui'].includes(manifest.runtime) ? 'Declare each hosted operation once, from the supported list (a capability or ComfyUI Block declares exactly one)' : 'Unsupported permission: only capability, ComfyUI and module Blocks declare hosted operations. A javascript or recipe Block declares none: remove permissions.capabilities, or make the AI work its own capability Block') : 'Unsupported permission; Blocks have no ambient filesystem or secrets');
241
243
  }
242
244
  if(Object.values(plain(manifest.outputs)?manifest.outputs:{}).some(port=>['workspace-edit','workspace-edit[]'].includes(port?.type))) {
243
245
  check(WORKSPACE_EDIT_FIELDS.some(field=>canProposeWorkspace(manifest,field)), 'permissions.project', 'Workspace edits require matching context.FIELD.read and workspace.FIELD.propose permissions');
@@ -257,6 +259,8 @@ export function validateManifest(manifest, { builtIn = false } = {}) {
257
259
  continue;
258
260
  }
259
261
  check(plain(c) && UI_INPUT_CONTROLS.includes(c.control) && Object.hasOwn(manifest.inputs || {}, c.port), 'ui', 'Control must bind to a declared input');
262
+ // A text input of several lines (one fact per line, a list) declares it, so every host draws a box that keeps its line breaks.
263
+ if (plain(c) && c.multiline !== undefined) check(c.multiline === true && c.control === 'text' && manifest.inputs?.[c.port]?.type === 'text', 'ui', 'multiline: true goes on a text control bound to a text input');
260
264
  if (plain(c) && Object.hasOwn(UI_CONTROL_TYPES, c.control)) check(UI_CONTROL_TYPES[c.control].includes(manifest.inputs?.[c.port]?.type), 'ui', `The ${c.control} control binds a ${UI_CONTROL_TYPES[c.control].join(', ')} input`);
261
265
  if (plain(c) && ['select','multiselect'].includes(c.control)) {
262
266
  check(manifest.inputs?.[c.port]?.type === (c.control === 'select' ? 'text' : 'text[]'), 'ui', 'Select binds text; multiselect binds text[]');
@@ -1,19 +1,19 @@
1
1
  import {EXAMPLE_PROVENANCE} from './example-license.js';
2
2
  export const textCleanup = {
3
- manifest: { schemaVersion: 1, license: 'MIT', provenance: EXAMPLE_PROVENANCE, sdkVersion: '0.1.0', id: 'example.clean-text', version: '1.0.0', name: 'Clean text', description: 'Trims extra space from text and returns a clean copy for the next step.', kind: 'task', runtime: 'recipe', inputs: { text: { type: 'text', semantic: 'plain_text', required: true, description: 'Text whose surrounding whitespace should be removed.' } }, outputs: { text: { type: 'text', semantic: 'plain_text', description: 'The same text with surrounding whitespace removed.' } }, permissions: { project: [], network: [], filesystem: [], secrets: [] }, ui: [{ control: 'text', port: 'text' }] },
3
+ manifest: { schemaVersion: 1, license: 'MIT', provenance: EXAMPLE_PROVENANCE, sdkVersion: '0.1.0', id: 'example.clean-text',action:{label:'Clean text',running:'Cleaning text…'}, version: '1.0.0', name: 'Clean text', description: 'Trims extra space from text and returns a clean copy for the next step.', kind: 'task', runtime: 'recipe', inputs: { text: { type: 'text', semantic: 'plain_text', required: true, description: 'Text whose surrounding whitespace should be removed.' } }, outputs: { text: { type: 'text', semantic: 'plain_text', description: 'The same text with surrounding whitespace removed.' } }, permissions: { project: [], network: [], filesystem: [], secrets: [] }, ui: [{ control: 'text', port: 'text' }] },
4
4
  recipe: { schemaVersion: 1, steps: [{ id: 'clean', op: 'text.trim', args: { text: { $input: 'text' } } }], outputs: { text: { $step: 'clean' } } },
5
5
  tests: [{ name: 'Trims surrounding whitespace', input: { text: ' StillMade\n' }, expected: { text: 'StillMade' } }],
6
6
  };
7
7
  const pixels = Array.from({ length: 5 * 5 * 4 }, (_, i) => i >= 48 && i <= 50 ? 0 : 255);
8
8
  const expanded = pixels.map((v, i) => { const p = Math.floor(i / 4), x = p % 5, y = Math.floor(p / 5); return x >= 1 && x <= 3 && y >= 1 && y <= 3 && i % 4 !== 3 ? 0 : v; });
9
9
  export const lineArt = {
10
- manifest: { schemaVersion: 1, license: 'MIT', provenance: EXAMPLE_PROVENANCE, sdkVersion: '0.1.0', id: 'example.thicken-lines', version: '1.0.1', name: 'Thicken line art', description: 'Expands dark outlines in an image while preserving surrounding colors, creating a new image for the next production step.', kind: 'task', runtime: 'recipe', inputs: { image: { type: 'image', primary: true, semantic: 'source_image', required: true, description: 'Line-art image whose dark outlines should be expanded.' }, thickness: { type: 'integer', default: 2, min: 1, max: 8, semantic: 'line_thickness', required: false, description: 'Number of pixels by which to expand dark outlines.' }, threshold: { type: 'number', default: 100, min: 0, max: 255, semantic: 'darkness_threshold', required: false, description: 'Brightness threshold used to identify dark outline pixels.' } }, outputs: { image: { type: 'image', primary: true, semantic: 'processed_image', description: 'A new image with expanded dark outlines.' } }, permissions: { project: [], network: [], filesystem: [], secrets: [] }, ui: [{ control: 'asset', port: 'image' }, { control: 'slider', port: 'thickness' }, { control: 'slider', port: 'threshold' }] },
10
+ manifest: { schemaVersion: 1, license: 'MIT', provenance: EXAMPLE_PROVENANCE, sdkVersion: '0.1.0', id: 'example.thicken-lines',action:{label:'Thicken lines',running:'Thickening lines…'}, version: '1.0.1', name: 'Thicken line art', description: 'Expands dark outlines in an image while preserving surrounding colors, creating a new image for the next production step.', kind: 'task', runtime: 'recipe', inputs: { image: { type: 'image', primary: true, semantic: 'source_image', required: true, description: 'Line-art image whose dark outlines should be expanded.' }, thickness: { type: 'integer', default: 2, min: 1, max: 8, semantic: 'line_thickness', required: false, description: 'Number of pixels by which to expand dark outlines.' }, threshold: { type: 'number', default: 100, min: 0, max: 255, semantic: 'darkness_threshold', required: false, description: 'Brightness threshold used to identify dark outline pixels.' } }, outputs: { image: { type: 'image', primary: true, semantic: 'processed_image', description: 'A new image with expanded dark outlines.' } }, permissions: { project: [], network: [], filesystem: [], secrets: [] }, ui: [{ control: 'asset', port: 'image' }, { control: 'slider', port: 'thickness' }, { control: 'slider', port: 'threshold' }] },
11
11
  recipe: { schemaVersion: 1, steps: [{ id: 'outlines', op: 'image.thicken-lines', args: { image: { $input: 'image' }, thickness: { $input: 'thickness' }, threshold: { $input: 'threshold' } } }], outputs: { image: { $step: 'outlines' } } },
12
12
  tests: [{ name: 'One dark point expands to a 3×3 square', input: { image: { width: 5, height: 5, data: pixels }, thickness: 1 }, expected: { image: { width: 5, height: 5, data: expanded } } }],
13
13
  };
14
14
 
15
15
  export const javascriptText = {
16
- manifest: { ...textCleanup.manifest, id: 'example.word-count', name: 'Word count', description: 'Counts words and returns trimmed text using isolated JavaScript.', runtime: 'javascript', outputs: { text: { type: 'text', semantic: 'plain_text', description: 'The trimmed source text.' }, words: { type: 'integer', semantic: 'word_count', description: 'The number of whitespace-separated words.' } } },
16
+ manifest: { ...textCleanup.manifest, id: 'example.word-count',action:{label:'Count words',running:'Counting words…'}, name: 'Word count', description: 'Counts words and returns trimmed text using isolated JavaScript.', runtime: 'javascript', outputs: { text: { type: 'text', semantic: 'plain_text', description: 'The trimmed source text.' }, words: { type: 'integer', semantic: 'word_count', description: 'The number of whitespace-separated words.' } } },
17
17
  code: 'const text = input.text.trim();\nreturn { text, words: text ? text.split(/\\s+/).length : 0 };',
18
18
  tests: [{ name: 'Counts words', input: { text: ' Hello StillMade ' }, expected: { text: 'Hello StillMade', words: 2 } }, { name: 'Empty text', input: { text: ' ' }, expected: { text: '', words: 0 } }],
19
19
  };
@@ -4,32 +4,6 @@
4
4
  export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
5
5
  "sm.ai-decide@1.0.0",
6
6
  "sm.ai-write@1.0.0",
7
- "sm.animated-scene-assistant@1.0.0",
8
- "sm.animated-scene@2.0.0",
9
- "sm.animated-scene@2.1.0",
10
- "sm.animated-scene@2.2.0",
11
- "sm.animated-scene@2.3.0",
12
- "sm.animated-scene@2.3.1",
13
- "sm.animated-scene@2.3.2",
14
- "sm.animated-scene@2.3.3",
15
- "sm.animated-scene@2.3.4",
16
- "sm.animated-scene@2.3.5",
17
- "sm.animated-scene@2.3.6",
18
- "sm.animated-scene@2.3.7",
19
- "sm.animated-scene@2.3.8",
20
- "sm.animated-scene@2.3.9",
21
- "sm.blueprint-assistant@1.0.0",
22
- "sm.blueprint@2.0.0",
23
- "sm.blueprint@2.1.0",
24
- "sm.blueprint@2.1.1",
25
- "sm.blueprint@2.1.2",
26
- "sm.blueprint@2.1.3",
27
- "sm.blueprint@2.1.4",
28
- "sm.blueprint@2.1.5",
29
- "sm.blueprint@2.1.6",
30
- "sm.blueprint@2.1.7",
31
- "sm.blueprint@2.1.8",
32
- "sm.blueprint@2.1.9",
33
7
  "sm.board-editor@2.0.0",
34
8
  "sm.board-editor@2.1.0",
35
9
  "sm.board-editor@2.2.0",
@@ -47,19 +21,11 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
47
21
  "sm.captions@2.0.0",
48
22
  "sm.character@2.0.0",
49
23
  "sm.character@2.0.1",
50
- "sm.chat@2.0.0",
51
- "sm.chat@2.0.1",
52
- "sm.chat@2.0.2",
53
- "sm.chat@2.0.3",
54
24
  "sm.compare@2.0.0",
55
25
  "sm.compare@2.0.1",
56
26
  "sm.compare@2.0.2",
57
27
  "sm.connect-app@1.0.0",
58
28
  "sm.contact-fields@1.0.0",
59
- "sm.continue-if@1.0.0",
60
- "sm.describe-media@2.0.0",
61
- "sm.describe-media@2.0.1",
62
- "sm.describe-media@2.1.0",
63
29
  "sm.edit-assistant@2.0.0",
64
30
  "sm.edit-assistant@2.0.1",
65
31
  "sm.edit-assistant@2.0.2",
@@ -92,8 +58,6 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
92
58
  "sm.grade@2.0.0",
93
59
  "sm.grade@2.0.1",
94
60
  "sm.grade@2.0.2",
95
- "sm.image-editor@2.0.0",
96
- "sm.image-editor@2.0.1",
97
61
  "sm.import-media@2.0.0",
98
62
  "sm.import-media@2.0.1",
99
63
  "sm.import-media@2.0.2",
@@ -101,7 +65,6 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
101
65
  "sm.location@2.0.1",
102
66
  "sm.meta-creative@1.0.0",
103
67
  "sm.motion-graphics@2.0.0",
104
- "sm.new-items@1.0.0",
105
68
  "sm.panel-strip@2.0.0",
106
69
  "sm.panel-strip@2.1.0",
107
70
  "sm.panel-strip@2.2.0",
@@ -118,7 +81,6 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
118
81
  "sm.panel-strip@2.4.2",
119
82
  "sm.photo-notes@1.0.0",
120
83
  "sm.photo-notes@1.1.0",
121
- "sm.pick-photos@1.0.0",
122
84
  "sm.pipeline@2.0.0",
123
85
  "sm.pipeline@2.1.0",
124
86
  "sm.pipeline@2.2.0",
@@ -128,38 +90,12 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
128
90
  "sm.pipeline@2.2.4",
129
91
  "sm.pipeline@2.2.5",
130
92
  "sm.pipeline@2.2.6",
131
- "sm.quality-audit@1.0.0",
132
- "sm.quality-audit@1.0.1",
133
- "sm.quality-audit@1.0.2",
134
- "sm.research@2.0.0",
135
- "sm.research@2.0.1",
136
- "sm.research@2.0.2",
137
93
  "sm.rough-cut@2.0.0",
138
94
  "sm.rough-cut@2.0.1",
139
95
  "sm.rough-cut@2.0.2",
140
96
  "sm.score@2.0.0",
141
- "sm.script@2.0.0",
142
- "sm.script@2.1.0",
143
- "sm.script@2.1.1",
144
- "sm.script@2.1.2",
145
- "sm.script@2.1.3",
146
- "sm.script@2.1.4",
147
- "sm.script@2.1.5",
148
- "sm.shot-plan-assistant@1.0.0",
149
- "sm.shot-plan@2.0.0",
150
- "sm.shot-plan@2.1.0",
151
- "sm.shot-plan@2.1.1",
152
- "sm.shot-plan@2.1.2",
153
- "sm.shot-plan@2.1.3",
154
- "sm.shot-plan@2.2.0",
155
- "sm.shot-plan@2.2.1",
156
- "sm.stock-search@2.0.0",
157
97
  "sm.style@2.0.0",
158
98
  "sm.style@2.0.1",
159
- "sm.thumbnail@2.0.0",
160
- "sm.thumbnail@2.0.1",
161
- "sm.transcribe@2.0.0",
162
- "sm.transcribe@2.0.1",
163
99
  "sm.transition@2.0.0",
164
100
  "sm.transition@2.0.1",
165
101
  "sm.transition@2.0.2",
@@ -179,26 +115,7 @@ export const FIRST_PARTY_SDK_IDENTITIES=Object.freeze(new Set([
179
115
  "sm.voiceover@2.1.6",
180
116
  "sm.when@1.0.0",
181
117
  "sm.when@1.1.0",
182
- "sm.whiteboard-assistant@1.0.0",
183
118
  "sm.write-summary@1.0.0",
184
- "sm.youtube@2.0.0",
185
- "stillmade.narration@1.0.0",
186
- "stillmade.narration@1.0.1",
187
- "stillmade.occasion-story@1.0.0",
188
- "stillmade.occasion-story@1.0.1",
189
- "stillmade.occasion-story@1.0.2",
190
- "stillmade.photo-reel-composer@1.0.0",
191
- "stillmade.photo-reel-composer@1.0.1",
192
- "stillmade.photo-reel-composer@1.0.2",
193
- "stillmade.product-ad-variants@1.0.0",
194
- "stillmade.release-visuals@1.0.0",
195
- "stillmade.release-visuals@1.0.1",
196
- "stillmade.release-visuals@1.0.2",
197
- "stillmade.screen-recorder@1.0.0",
198
- "stillmade.screen-recorder@1.0.1",
199
- "stillmade.short-form-finish@1.0.0",
200
- "stillmade.short-form-finish@1.0.1",
201
- "stillmade.short-form-finish@1.0.2",
202
- "stillmade.short-form-finish@1.0.3"
119
+ "stillmade.generate@1.0.0"
203
120
  ]));
204
121
  export const isFirstPartySdkIdentity=manifest=>FIRST_PARTY_SDK_IDENTITIES.has(`${manifest?.id}@${manifest?.version}`);
@@ -1,22 +1,5 @@
1
- import shortFormFinishV100 from '../../blocks/short-form-finish/versions/1.0.0/package.js';
2
- import shortFormFinishV101 from '../../blocks/short-form-finish/versions/1.0.1/package.js';
3
- import shortFormFinishV102 from '../../blocks/short-form-finish/versions/1.0.2/package.js';
4
- import shortFormFinish from '../../blocks/short-form-finish/package.js';
5
- import screenRecorderV100 from '../../blocks/screen-recorder/versions/1.0.0/package.js';
6
- import screenRecorder from '../../blocks/screen-recorder/package.js';
7
- import narrationV100 from '../../blocks/narration/versions/1.0.0/package.js';
8
- import narration from '../../blocks/narration/package.js';
9
- import occasionStoryV100 from '../../blocks/occasion-story/versions/1.0.0/package.js';
10
- import releaseVisualsV100 from '../../blocks/release-visuals/versions/1.0.0/package.js';
11
- import occasionStoryV101 from '../../blocks/occasion-story/versions/1.0.1/package.js';
12
- import releaseVisualsV101 from '../../blocks/release-visuals/versions/1.0.1/package.js';
13
- import occasionStory from '../../blocks/occasion-story/package.js';
14
- import releaseVisuals from '../../blocks/release-visuals/package.js';
1
+ import generate from '../../blocks/generate/package.js';
15
2
  import importMedia from '../../blocks/native/sm.import-media/versions/2.0.0/package.js';
16
- import photoReelComposerV100 from '../../blocks/photo-reel-composer/versions/1.0.0/package.js';
17
- import photoReelComposerV101 from '../../blocks/photo-reel-composer/versions/1.0.1/package.js';
18
- import photoReelComposer from '../../blocks/photo-reel-composer/versions/1.0.2/package.js';
19
- import productAdVariants from '../../blocks/product-ad-variants/versions/1.0.0/package.js';
20
3
  import importMediaV201 from '../../blocks/native/sm.import-media/versions/2.0.1/package.js';
21
4
  import importMediaV202 from '../../blocks/native/sm.import-media/versions/2.0.2/package.js';
22
5
  import editorV229 from '../../blocks/native/sm.editor/versions/2.2.9/package.js';
@@ -34,9 +17,6 @@ import gradeV202 from '../../blocks/native/sm.grade/versions/2.0.2/package.js';
34
17
  import compare from '../../blocks/native/sm.compare/versions/2.0.0/package.js';
35
18
  import compareV201 from '../../blocks/native/sm.compare/versions/2.0.1/package.js';
36
19
  import compareV202 from '../../blocks/native/sm.compare/versions/2.0.2/package.js';
37
- import qualityAuditV102 from '../../blocks/native/sm.quality-audit/versions/1.0.2/package.js';
38
- import stockSearch from '../../blocks/native/sm.stock-search/versions/2.0.0/package.js';
39
- import youtubePublication from '../../blocks/native/sm.youtube/versions/2.0.0/package.js';
40
20
  import exportVideo from '../../blocks/native/sm.export-video/versions/2.0.0/package.js';
41
21
  import exportVideoV210 from '../../blocks/native/sm.export-video/versions/2.1.0/package.js';
42
22
  import exportAudio from '../../blocks/native/sm.export-audio/versions/2.0.0/package.js';
@@ -44,14 +24,10 @@ import motionGraphics from '../../blocks/native/sm.motion-graphics/versions/2.0.
44
24
  import audioScore from '../../blocks/native/sm.score/versions/2.0.0/package.js';
45
25
  import generateImage from '../../blocks/native/sm.generate-image/versions/2.0.0/package.js';
46
26
  import generateImageV210 from '../../blocks/native/sm.generate-image/versions/2.1.0/package.js';
47
- import thumbnail from '../../blocks/native/sm.thumbnail/versions/2.0.0/package.js';
48
- import thumbnailV201 from '../../blocks/native/sm.thumbnail/versions/2.0.1/package.js';
49
27
  import writeSummary from '../../blocks/native/sm.write-summary/versions/1.0.0/package.js';
50
28
  import automationWhen from '../../blocks/native/sm.when/versions/1.0.0/package.js';
51
29
  import automationV110 from '../../blocks/native/sm.when/versions/1.1.0/package.js';
52
- import automationNewItems from '../../blocks/native/sm.new-items/versions/1.0.0/package.js';
53
30
  import automationAiDecide from '../../blocks/native/sm.ai-decide/versions/1.0.0/package.js';
54
- import automationContinueIf from '../../blocks/native/sm.continue-if/versions/1.0.0/package.js';
55
31
  import connectApp from '../../blocks/native/sm.connect-app/versions/1.0.0/package.js';
56
32
  import automationAiWrite from '../../blocks/native/sm.ai-write/versions/1.0.0/package.js';
57
33
  import automationMetaCreative from '../../blocks/native/sm.meta-creative/versions/1.0.0/package.js';
@@ -64,35 +40,16 @@ import voiceoverV213 from '../../blocks/native/sm.voiceover/versions/2.1.3/packa
64
40
  import voiceoverV214 from '../../blocks/native/sm.voiceover/versions/2.1.4/package.js';
65
41
  import voiceoverV215 from '../../blocks/native/sm.voiceover/versions/2.1.5/package.js';
66
42
  import voiceoverV216 from '../../blocks/native/sm.voiceover/versions/2.1.6/package.js';
67
- import script from '../../blocks/native/sm.script/versions/2.0.0/package.js';
68
- import scriptV210 from '../../blocks/native/sm.script/versions/2.1.0/package.js';
69
- import scriptV211 from '../../blocks/native/sm.script/versions/2.1.1/package.js';
70
- import scriptV212 from '../../blocks/native/sm.script/versions/2.1.2/package.js';
71
- import scriptV213 from '../../blocks/native/sm.script/versions/2.1.3/package.js';
72
- import scriptV214 from '../../blocks/native/sm.script/versions/2.1.4/package.js';
73
- import scriptV215 from '../../blocks/native/sm.script/versions/2.1.5/package.js';
74
43
  import generateVideo from '../../blocks/native/sm.generate-video/versions/2.0.0/package.js';
75
44
  import generateVideoV201 from '../../blocks/native/sm.generate-video/versions/2.0.1/package.js';
76
- import chat from '../../blocks/native/sm.chat/versions/2.0.0/package.js';
77
- import chatV201 from '../../blocks/native/sm.chat/versions/2.0.1/package.js';
78
- import chatV202 from '../../blocks/native/sm.chat/versions/2.0.2/package.js';
79
- import chatV203 from '../../blocks/native/sm.chat/versions/2.0.3/package.js';
80
45
  import character from '../../blocks/native/sm.character/versions/2.0.0/package.js';
81
46
  import characterV201 from '../../blocks/native/sm.character/versions/2.0.1/package.js';
82
47
  import location from '../../blocks/native/sm.location/versions/2.0.0/package.js';
83
48
  import locationV201 from '../../blocks/native/sm.location/versions/2.0.1/package.js';
84
49
  import style from '../../blocks/native/sm.style/versions/2.0.0/package.js';
85
50
  import styleV201 from '../../blocks/native/sm.style/versions/2.0.1/package.js';
86
- import research from '../../blocks/native/sm.research/versions/2.0.0/package.js';
87
- import researchV201 from '../../blocks/native/sm.research/versions/2.0.1/package.js';
88
- import researchV202 from '../../blocks/native/sm.research/versions/2.0.2/package.js';
89
- import imageEditor from '../../blocks/native/sm.image-editor/versions/2.0.0/package.js';
90
- import imageEditorV201 from '../../blocks/native/sm.image-editor/versions/2.0.1/package.js';
91
- import describeMedia from '../../blocks/native/sm.describe-media/versions/2.0.0/package.js';
92
- import describeMediaV201 from '../../blocks/native/sm.describe-media/versions/2.0.1/package.js';
93
51
  // October 29, 2026: StillMade credits stopped paying for image.describe on September 13, so Describe media 2.1.0
94
52
  // and Photo notes 1.1.0 run on media.analyze (credits, per image); the image.describe releases stay for exact pins.
95
- import describeMediaV210 from '../../blocks/native/sm.describe-media/versions/2.1.0/package.js';
96
53
  import videoAssets from '../../blocks/native/sm.video-assets/versions/2.0.0/package.js';
97
54
  import videoAssetsV210 from '../../blocks/native/sm.video-assets/versions/2.1.0/package.js';
98
55
  import videoAssetsV211 from '../../blocks/native/sm.video-assets/versions/2.1.1/package.js';
@@ -108,31 +65,9 @@ import finalEditV202 from '../../blocks/native/sm.final-edit/versions/2.0.2/pack
108
65
  import editAssistant from '../../blocks/native/sm.edit-assistant/versions/2.0.0/package.js';
109
66
  import editAssistantV201 from '../../blocks/native/sm.edit-assistant/versions/2.0.1/package.js';
110
67
  import editAssistantV202 from '../../blocks/native/sm.edit-assistant/versions/2.0.2/package.js';
111
- import transcribe from '../../blocks/native/sm.transcribe/versions/2.0.0/package.js';
112
- import transcribeV201 from '../../blocks/native/sm.transcribe/versions/2.0.1/package.js';
113
- import blueprint from '../../blocks/native/sm.blueprint/versions/2.0.0/package.js';
114
- import blueprintV210 from '../../blocks/native/sm.blueprint/versions/2.1.0/package.js';
115
- import blueprintV211 from '../../blocks/native/sm.blueprint/versions/2.1.1/package.js';
116
- import blueprintV212 from '../../blocks/native/sm.blueprint/versions/2.1.2/package.js';
117
- import blueprintV213 from '../../blocks/native/sm.blueprint/versions/2.1.3/package.js';
118
- import blueprintV214 from '../../blocks/native/sm.blueprint/versions/2.1.4/package.js';
119
- import blueprintV215 from '../../blocks/native/sm.blueprint/versions/2.1.5/package.js';
120
- import blueprintV216 from '../../blocks/native/sm.blueprint/versions/2.1.6/package.js';
121
- import blueprintV218 from '../../blocks/native/sm.blueprint/versions/2.1.8/package.js';
122
- import blueprintV219 from '../../blocks/native/sm.blueprint/versions/2.1.9/package.js';
123
- import blueprintV217 from '../../blocks/native/sm.blueprint/versions/2.1.7/package.js';
124
- import shotPlan from '../../blocks/native/sm.shot-plan/versions/2.0.0/package.js';
125
- import shotPlanV210 from '../../blocks/native/sm.shot-plan/versions/2.1.0/package.js';
126
- import shotPlanV211 from '../../blocks/native/sm.shot-plan/versions/2.1.1/package.js';
127
- import shotPlanV212 from '../../blocks/native/sm.shot-plan/versions/2.1.2/package.js';
128
- import shotPlanV213 from '../../blocks/native/sm.shot-plan/versions/2.1.3/package.js';
129
- import shotPlanV220 from '../../blocks/native/sm.shot-plan/versions/2.2.0/package.js';
130
- import shotPlanV221 from '../../blocks/native/sm.shot-plan/versions/2.2.1/package.js';
131
68
  import boardEditor from '../../blocks/native/sm.board-editor/versions/2.0.0/package.js';
132
69
  import panelStripV200 from '../../blocks/native/sm.panel-strip/versions/2.0.0/package.js';
133
70
  import panelStrip from '../../blocks/native/sm.panel-strip/versions/2.1.0/package.js';
134
- import animatedSceneV200 from '../../blocks/native/sm.animated-scene/versions/2.0.0/package.js';
135
- import animatedScene from '../../blocks/native/sm.animated-scene/versions/2.1.0/package.js';
136
71
  import editor from '../../blocks/native/sm.editor/versions/2.0.0/package.js';
137
72
  import pipelineV200 from '../../blocks/native/sm.pipeline/versions/2.0.0/package.js';
138
73
  import pipeline from '../../blocks/native/sm.pipeline/versions/2.1.0/package.js';
@@ -143,11 +78,6 @@ import pipelineV225 from '../../blocks/native/sm.pipeline/versions/2.2.5/package
143
78
  import pipelineV226 from '../../blocks/native/sm.pipeline/versions/2.2.6/package.js';
144
79
  import pipelineV224 from '../../blocks/native/sm.pipeline/versions/2.2.4/package.js';
145
80
  import pipelineV222 from '../../blocks/native/sm.pipeline/versions/2.2.2/package.js';
146
- import shotPlanAssistant from '../../blocks/native/sm.shot-plan-assistant/versions/1.0.0/package.js';
147
- import animatedSceneAssistant from '../../blocks/native/sm.animated-scene-assistant/versions/1.0.0/package.js';
148
- import whiteboardAssistant from '../../blocks/native/sm.whiteboard-assistant/versions/1.0.0/package.js';
149
- import qualityAudit from '../../blocks/native/sm.quality-audit/versions/1.0.0/package.js';
150
- import qualityAuditV101 from '../../blocks/native/sm.quality-audit/versions/1.0.1/package.js';
151
81
  import editorV210 from '../../blocks/native/sm.editor/versions/2.1.0/package.js';
152
82
  import editorV220 from '../../blocks/native/sm.editor/versions/2.2.0/package.js';
153
83
  import editorV221 from '../../blocks/native/sm.editor/versions/2.2.1/package.js';
@@ -183,24 +113,12 @@ import boardEditorV228 from '../../blocks/native/sm.board-editor/versions/2.2.8/
183
113
  import boardEditorV229 from '../../blocks/native/sm.board-editor/versions/2.2.9/package.js';
184
114
  import boardEditorV2210 from '../../blocks/native/sm.board-editor/versions/2.2.10/package.js';
185
115
  import boardEditorV2211 from '../../blocks/native/sm.board-editor/versions/2.2.11/package.js';
186
- import animatedSceneV220 from '../../blocks/native/sm.animated-scene/versions/2.2.0/package.js';
187
- import animatedSceneV230 from '../../blocks/native/sm.animated-scene/versions/2.3.0/package.js';
188
- import animatedSceneV231 from '../../blocks/native/sm.animated-scene/versions/2.3.1/package.js';
189
- import animatedSceneV232 from '../../blocks/native/sm.animated-scene/versions/2.3.2/package.js';
190
- import animatedSceneV233 from '../../blocks/native/sm.animated-scene/versions/2.3.3/package.js';
191
- import animatedSceneV234 from '../../blocks/native/sm.animated-scene/versions/2.3.4/package.js';
192
- import animatedSceneV235 from '../../blocks/native/sm.animated-scene/versions/2.3.5/package.js';
193
- import animatedSceneV236 from '../../blocks/native/sm.animated-scene/versions/2.3.6/package.js';
194
- import animatedSceneV237 from '../../blocks/native/sm.animated-scene/versions/2.3.7/package.js';
195
- import animatedSceneV238 from '../../blocks/native/sm.animated-scene/versions/2.3.8/package.js';
196
- import animatedSceneV239 from '../../blocks/native/sm.animated-scene/versions/2.3.9/package.js';
197
- import blueprintAssistant from '../../blocks/native/sm.blueprint-assistant/versions/1.0.0/package.js';
116
+
198
117
  import photoNotes from '../../blocks/native/sm.photo-notes/versions/1.0.0/package.js';
199
118
  import photoNotesV110 from '../../blocks/native/sm.photo-notes/versions/1.1.0/package.js';
200
- import pickPhotos from '../../blocks/native/sm.pick-photos/versions/1.0.0/package.js';
201
119
 
202
120
  // Exact, independently executable first-party releases. Host workspaces do not
203
121
  // belong here. Changing implementation requires a new immutable version.
204
122
  const freeze=value=>{if(value&&typeof value==='object'&&!Object.isFrozen(value)){Object.values(value).forEach(freeze);Object.freeze(value);}return value;};
205
- export const FIRST_PARTY_SDK_PACKAGES = Object.freeze([freeze(productAdVariants),freeze(pickPhotos),freeze(photoNotesV110),freeze(photoNotes),freeze(youtubePublication),freeze(stockSearch),freeze(exportVideoV210),freeze(exportVideo),freeze(exportAudio),freeze(motionGraphics),freeze(audioScore),freeze(shortFormFinish),freeze(screenRecorder),freeze(screenRecorderV100),freeze(narration),freeze(narrationV100),freeze(shortFormFinishV102),freeze(shortFormFinishV101),freeze(shortFormFinishV100),freeze(occasionStory),freeze(occasionStoryV101),freeze(occasionStoryV100),freeze(releaseVisuals),freeze(releaseVisualsV101),freeze(releaseVisualsV100),freeze(photoReelComposer),freeze(photoReelComposerV101),freeze(photoReelComposerV100),freeze(importMediaV202),freeze(importMediaV201),freeze(editorV2212),freeze(editorV2211),freeze(editorV2210),freeze(editorV229),freeze(editorV228),freeze(panelStripV242),freeze(panelStripV241),freeze(panelStripV240),freeze(panelStripV237),freeze(panelStripV236),freeze(boardEditorV2211),freeze(boardEditorV2210),freeze(boardEditorV229),freeze(animatedSceneV239),freeze(animatedSceneV238),freeze(animatedSceneV237),freeze(pipelineV226),freeze(pipelineV225),freeze(pipelineV224),freeze(blueprintV219),freeze(blueprintV218),freeze(videoAssetsV214),freeze(videoAssetsV213),freeze(chatV203),freeze(chatV202),freeze(chatV201),freeze(shotPlanV221),freeze(shotPlanV220),freeze(shotPlanV213),freeze(shotPlanV212),freeze(voiceoverV216),freeze(voiceoverV215),freeze(voiceoverV214),freeze(voiceoverV213),freeze(scriptV215),freeze(scriptV214),freeze(scriptV213),freeze(roughCutV202),freeze(roughCutV201),freeze(finalEditV202),freeze(finalEditV201),freeze(editAssistantV202),freeze(editAssistantV201),freeze(gradeV202),freeze(gradeV201),freeze(transitionV202),freeze(transitionV201),freeze(qualityAuditV102),freeze(qualityAuditV101),freeze(compareV202),freeze(compareV201),freeze(compare),freeze(grade),freeze(transition),freeze(exportTimeline),freeze(captions),freeze(importMedia),freeze(generateImageV210),freeze(generateImage),freeze(thumbnailV201),freeze(thumbnail),freeze(writeSummary),freeze(automationV110),freeze(automationWhen),freeze(automationNewItems),freeze(automationAiDecide),freeze(automationContinueIf),freeze(connectApp),freeze(automationContactFields),freeze(automationAiWrite),freeze(automationMetaCreative),freeze(voiceover),freeze(voiceoverV210),freeze(voiceoverV211),freeze(voiceoverV212),freeze(script),freeze(scriptV210),freeze(scriptV211),freeze(scriptV212),freeze(generateVideoV201),freeze(generateVideo),freeze(chat),freeze(characterV201),freeze(character),freeze(locationV201),freeze(location),freeze(styleV201),freeze(style),freeze(researchV202),freeze(researchV201),freeze(research),freeze(imageEditorV201),freeze(imageEditor),freeze(describeMediaV210),freeze(describeMediaV201),freeze(describeMedia),freeze(videoAssets),freeze(videoAssetsV210),freeze(videoAssetsV211),freeze(videoAssetsV212),freeze(roughCut),freeze(finalEdit),freeze(editAssistant),freeze(transcribeV201),freeze(transcribe),freeze(blueprint),freeze(blueprintV210),freeze(blueprintV211),freeze(blueprintV212),freeze(blueprintV213),freeze(blueprintV214),freeze(blueprintV215),freeze(blueprintV216),freeze(blueprintV217),freeze(shotPlan),freeze(shotPlanV210),freeze(shotPlanV211),freeze(boardEditor),freeze(boardEditorV210),freeze(boardEditorV220),freeze(boardEditorV221),freeze(boardEditorV222),freeze(boardEditorV223),freeze(boardEditorV224),freeze(boardEditorV225),freeze(boardEditorV226),freeze(boardEditorV227),freeze(boardEditorV228),freeze(panelStripV200),freeze(panelStrip),freeze(panelStripV220),freeze(panelStripV230),freeze(panelStripV231),freeze(panelStripV232),freeze(panelStripV233),freeze(panelStripV234),freeze(panelStripV235),freeze(animatedSceneV200),freeze(animatedScene),freeze(animatedSceneV220),freeze(animatedSceneV230),freeze(animatedSceneV231),freeze(animatedSceneV232),freeze(animatedSceneV233),freeze(animatedSceneV234),freeze(animatedSceneV235),freeze(animatedSceneV236),freeze(editor),freeze(editorV210),freeze(editorV220),freeze(editorV221),freeze(editorV222),freeze(editorV223),freeze(editorV224),freeze(editorV225),freeze(editorV226),freeze(editorV227),freeze(pipelineV200),freeze(pipeline),freeze(pipelineV220),freeze(pipelineV221),freeze(pipelineV223),freeze(pipelineV222),freeze(shotPlanAssistant),freeze(animatedSceneAssistant),freeze(blueprintAssistant),freeze(whiteboardAssistant),freeze(qualityAudit)]);
123
+ export const FIRST_PARTY_SDK_PACKAGES = Object.freeze([freeze(generate),freeze(photoNotesV110),freeze(photoNotes),freeze(exportVideoV210),freeze(exportVideo),freeze(exportAudio),freeze(motionGraphics),freeze(audioScore),freeze(importMediaV202),freeze(importMediaV201),freeze(editorV2212),freeze(editorV2211),freeze(editorV2210),freeze(editorV229),freeze(editorV228),freeze(panelStripV242),freeze(panelStripV241),freeze(panelStripV240),freeze(panelStripV237),freeze(panelStripV236),freeze(boardEditorV2211),freeze(boardEditorV2210),freeze(boardEditorV229),freeze(pipelineV226),freeze(pipelineV225),freeze(pipelineV224),freeze(videoAssetsV214),freeze(videoAssetsV213),freeze(voiceoverV216),freeze(voiceoverV215),freeze(voiceoverV214),freeze(voiceoverV213),freeze(roughCutV202),freeze(roughCutV201),freeze(finalEditV202),freeze(finalEditV201),freeze(editAssistantV202),freeze(editAssistantV201),freeze(gradeV202),freeze(gradeV201),freeze(transitionV202),freeze(transitionV201),freeze(compareV202),freeze(compareV201),freeze(compare),freeze(grade),freeze(transition),freeze(exportTimeline),freeze(captions),freeze(importMedia),freeze(generateImageV210),freeze(generateImage),freeze(writeSummary),freeze(automationV110),freeze(automationWhen),freeze(automationAiDecide),freeze(connectApp),freeze(automationContactFields),freeze(automationAiWrite),freeze(automationMetaCreative),freeze(voiceover),freeze(voiceoverV210),freeze(voiceoverV211),freeze(voiceoverV212),freeze(generateVideoV201),freeze(generateVideo),freeze(characterV201),freeze(character),freeze(locationV201),freeze(location),freeze(styleV201),freeze(style),freeze(videoAssets),freeze(videoAssetsV210),freeze(videoAssetsV211),freeze(videoAssetsV212),freeze(roughCut),freeze(finalEdit),freeze(editAssistant),freeze(boardEditor),freeze(boardEditorV210),freeze(boardEditorV220),freeze(boardEditorV221),freeze(boardEditorV222),freeze(boardEditorV223),freeze(boardEditorV224),freeze(boardEditorV225),freeze(boardEditorV226),freeze(boardEditorV227),freeze(boardEditorV228),freeze(panelStripV200),freeze(panelStrip),freeze(panelStripV220),freeze(panelStripV230),freeze(panelStripV231),freeze(panelStripV232),freeze(panelStripV233),freeze(panelStripV234),freeze(panelStripV235),freeze(editor),freeze(editorV210),freeze(editorV220),freeze(editorV221),freeze(editorV222),freeze(editorV223),freeze(editorV224),freeze(editorV225),freeze(editorV226),freeze(editorV227),freeze(pipelineV200),freeze(pipeline),freeze(pipelineV220),freeze(pipelineV221),freeze(pipelineV223),freeze(pipelineV222) ]);
206
124
  export const firstPartySdkPackage = manifest => FIRST_PARTY_SDK_PACKAGES.find(pkg => pkg.manifest.id === manifest?.id && pkg.manifest.version === manifest?.version);
@@ -64,10 +64,22 @@ export function imageSettings(value,{strict=false}={}){
64
64
  return {model,aspectRatio:axis('aspectRatio',entry.aspectRatios,entry.aspectRatios.includes('1:1')?'1:1':undefined),resolution:axis('resolution',entry.resolutions),quality:axis('quality',entry.qualities,entry.qualities.includes('medium')?'medium':undefined)};
65
65
  }
66
66
 
67
- /** Fill and check video settings, including Veo's 1080p-is-8-seconds rule. */
68
- export function videoSettings(value,{strict=false,firstFrame=false}={}){
67
+ // November 3, 2026: a listing Block asked for a 9:16 clip and named no model, and the default (Vidu Q3 Turbo) takes no
68
+ // aspect ratio. With no model named, a shape the default cannot make picks the cheapest model here that makes it and
69
+ // takes the frames and the duration asked (prices in server/services/tiers.js); a named model is always kept.
70
+ export const VIDEO_SHAPE_MODELS=Object.freeze(['wan-2.6-flash','veo-3.1-lite','seedance-2.0-mini','kling-v3-turbo','seedance-2.0-fast','seedance-2.0']);
71
+ function defaultVideoModel(raw,{firstFrame,lastFrame}){
72
+ const shape=raw.aspectRatio;
73
+ if(shape===undefined||shape===DEFAULT_SETTING||shape==='auto'||VIDEO_GENERATION_MODELS[DEFAULT_VIDEO_MODEL].aspectRatios.includes(shape))return DEFAULT_VIDEO_MODEL;
74
+ const fits=entry=>entry.aspectRatios.includes(shape)&&(firstFrame||!entry.requiresFirstFrame)&&(!lastFrame||entry.lastFrame)&&(raw.duration===undefined||entry.durations.includes(raw.duration));
75
+ return VIDEO_SHAPE_MODELS.find(model=>fits(VIDEO_GENERATION_MODELS[model]))??DEFAULT_VIDEO_MODEL;
76
+ }
77
+
78
+ /** Fill and check video settings, including Veo's 1080p-is-8-seconds rule. With no model named, the default model is
79
+ * the one that makes the asked aspect ratio (VIDEO_SHAPE_MODELS). */
80
+ export function videoSettings(value,{strict=false,firstFrame=false,lastFrame=false}={}){
69
81
  const raw=settingsObject(value,VIDEO_SETTING_KEYS,'Video');
70
- const model=raw.model??DEFAULT_VIDEO_MODEL,entry=VIDEO_GENERATION_MODELS[model];
82
+ const model=raw.model??defaultVideoModel(raw,{firstFrame,lastFrame}),entry=VIDEO_GENERATION_MODELS[model];
71
83
  assert(entry,'CAPABILITY_SETTINGS',`Choose a video model from: ${Object.keys(VIDEO_GENERATION_MODELS).join(', ')}`);
72
84
  const axis=(name,allowed,fallback)=>{
73
85
  const chosen=pick(allowed,raw[name],fallback);
@@ -3,7 +3,7 @@ import {EXAMPLE_PROVENANCE} from './example-license.js';
3
3
  // Provider, model, payment and image loading belong to the trusted host. The
4
4
  // package owns only the typed contract, bounded analysis instructions and tests.
5
5
  export const imageDescription={
6
- manifest:{schemaVersion:1,sdkVersion:'0.1.0',id:'example.describe-image',version:'1.0.0',name:'Describe an image',description:'Turn one owned image into concise production-ready visual notes.',kind:'task',runtime:'capability',entry:'src/capability.json',license:'MIT',provenance:EXAMPLE_PROVENANCE,inputs:{image:{type:'image',primary:true,semantic:'source_image',required:true,description:'Owned image to inspect for visible production details.'}},outputs:{description:{type:'text',primary:true,role:'visual_description',semantic:'visual_description',description:'Concise production notes based only on visible image details.'}},permissions:{project:[],capabilities:['image.describe'],network:[],filesystem:[],secrets:[]},ui:[{control:'asset',port:'image'}]},
6
+ manifest:{schemaVersion:1,sdkVersion:'0.1.0',id:'example.describe-image',action:{label:'Describe image',running:'Describing image…'},version:'1.0.0',name:'Describe an image',description:'Turn one owned image into concise production-ready visual notes.',kind:'task',runtime:'capability',entry:'src/capability.json',license:'MIT',provenance:EXAMPLE_PROVENANCE,inputs:{image:{type:'image',primary:true,semantic:'source_image',required:true,description:'Owned image to inspect for visible production details.'}},outputs:{description:{type:'text',primary:true,role:'visual_description',semantic:'visual_description',description:'Concise production notes based only on visible image details.'}},permissions:{project:[],capabilities:['image.describe'],network:[],filesystem:[],secrets:[]},ui:[{control:'asset',port:'image'}]},
7
7
  capability:{schemaVersion:1,operation:'image.describe',image:{$input:'image'},instruction:'Describe only visibly supported subject matter, composition, lighting, color, texture, camera feel and visual style. Return concise production-ready plain text.',maxTokens:700,output:'description'},
8
8
  tests:[{name:'Blue image',input:{image:{width:1,height:1,data:[20,80,200,255]}},expectations:{description:{kind:'text',minLength:20,maxLength:32768,includes:['blue']}}}],
9
9
  };