Types and compatibility
The shared type system, media representation, semantic roles, and safe conversions.
Supported types
textnumberintegerbooleanjsonobjectimagevideoaudioassetscripttranscriptbriefbibleshot-planpanel-documentboard-documentscene-documenttimelinetimeline-editfile-bundleworkspace-edittimeline-rangeresearchcharacterlocationstylepipelineapprovalsceneshotmaskdepthposemetadataproject-contextfiledocumenttableurldatecolor
Any listed type can use an array suffix, for example shot[] or image[].
Runtime values
| Type family | Value |
|---|---|
| text | A string up to 1,000,000 characters |
| number / integer | A finite number, with integer and bounds checks as declared |
| boolean | true or false |
| json | Safe finite JSON data |
| image | Portable media reference or bounded RGBA image |
| video / audio / asset | Portable media reference |
| file-bundle | Validated file or ZIP download data; media requires host authorization |
| Production documents | JSON object with schemaVersion: 1 |
| Arrays | Array whose members match the base type |
A media reference is {assetId, versionId, kind}. A decoded image is {width, height, data}, with width × height × 4 integer RGBA channels. The current pixel ceiling is 1,048,576.
Compatibility rules
The raw compatible check requires exact type matching except integer → number. Arrays match their full declared type. If an input declares a semantic role, its producer must declare the same role. The host connection registry can additionally apply the guarded conversions below.
compatible({type: 'image', role: 'source_image'},
{type: 'image', role: 'source_image'});
// { compatible: true, adapter: null }Primary ports
Declare one primary input and output for automatic insertion. A sole port is inferred for older packages. Multiple candidates without a declared primary do not produce an arbitrary connection.
Safe connection conversions
StillMade can insert a versioned, deterministic conversion between compatible Steps. This is part of the host connection contract; it does not add a visible Step or ask the user to wire ports. Advanced connection controls show the conversion and preserve its version when the Project Type is saved.
compatible(outputPort, inputPort) remains the strict raw-value check: exact types and matching required roles, with the existing integer-to-number widening. Use connectionCompatibility(outputPort, inputPort) to discover a supported connection conversion. Pass the complete port objects, including role. validateConnectionAdapter(outputPort, inputPort, {permissions, adapter}) also checks the receiving Block's manifest.permissions.project grants and the saved adapter pin. Static compatibility is a possibility, not a promise that any value can run: the actual output and authorized context must pass runtime validation.
| Conversion | What the host accepts | Receiving Block permissions |
|---|---|---|
| integer → number | A valid whole number, preserving its value | None |
| image / video / audio → asset | A saved media reference of that kind | None |
| asset → image / video / audio | A media reference whose actual kind matches the requested type | None |
| scene → shot[] | The scene's authorized shots, in its recorded project order | context.scenes.read, context.shots.read |
| shot → image | That shot's unambiguous, current, host-selected image, present in authorized project assets | context.shots.read, context.assets.read |
The conversion IDs are integer-to-number, image-to-asset, video-to-asset, audio-to-asset, asset-to-image, asset-to-video, asset-to-audio, scene-to-shots, and shot-to-selected-image. Each is currently version 1. New cross-type Project Type connections must include the exact pin:
{
"from": { "stage": "source", "port": "asset" },
"to": { "stage": "process", "port": "image" },
"adapter": { "id": "asset-to-image", "version": 1 }
}The builder adds this metadata automatically. Existing direct and integer-to-number connections remain valid without an explicit pin. Changing a pin changes the workflow identity and invalidates connected-run and automation reviews. The host uses adaptConnectionValue(outputPort, inputPort, value, {context, permissions, adapter}) before validating the receiving input; a conversion returns copied data and cannot modify the project.
Roles are preserved, never invented: a character_reference image cannot become a source_image. Scene and shot conversions use scoped host context rather than trusting a Block's embedded shot list or selected-image claim. Missing, ambiguous, stale or unauthorized selections stop the run. StillMade does not choose the first image or fall back to a historical version. Inline RGBA pixels must be saved as a media reference before a media-to-asset conversion. No conversion here downloads, decodes, generates, or uploads media. Video-to-audio, video-to-frames, model inference and other semantic transformations still require an explicit supported capability and its own execution permissions.
Additional shared types include scene, shot, shot[], mask, depth, pose, metadata and project-context. Their current values are versioned JSON documents (schemaVersion: 1); declaring a context port does not grant project access. Scoped context and conditional/batch execution are available for supported SDK Blocks. ComfyUI uses the explicit account connection and review described below; unattended remote automation and additional media adapters remain separate work.
Context bindings
| Field | Exact type |
|---|---|
| script | script |
| transcript | transcript |
| shots | shot[] |
| shotPlan | shot-plan |
| scenes | scene[] |
| characters | character[] |
| locations | location[] |
| styles | style[] |
| assets | asset[] |
| files | file[] |
| generations | metadata[] |
| versions | metadata[] |
| timeline | timeline |
| editor | timeline |
| canvas | pipeline |
| blueprint | bible |
| panels | panel-document |
| board | board-document |
| animation | scene-document |
| metadata | metadata |
Downloadable Block results
Declare an output with type: "file-bundle" (or file-bundle[]). The shared Block preview automatically offers Download, including when the Block has a custom view. A file result is inert data: it grants no filesystem access and never mounts HTML, runs code or triggers a download before the user clicks. This output type is separate from the .stillmade-block archive used to install the Block itself.
{
"schemaVersion": 1,
"format": "file",
"name": "notes.txt",
"files": [{"path": "notes.txt", "text": "My notes"}]
}For ZIP output use format: "zip", a .zip name, and multiple files. Each member contains exactly {path,text} or {path,source}. A source retains the complete selected media reference (kind, assetId, versionId, url, and its existing role/metadata); never invent a reference or substitute a URL. Media entries need asset.read in manifest.permissions.project and an authorized selection from the host's existing media inputs or declared project context. Declaration alone is not permission. The host checks the full reference identity and current account/selection before transferring bytes. Detached history views can download text-only results; media ZIPs need the Block's current authorized media context. Unavailable, unselected or unsupported media aborts the ZIP instead of returning an archive with missing members. The host does not install dependencies.
File downloads contain exactly one text member whose path equals name. Supported text suffixes: .txt, .md, .json, .csv, .xml, .fcpxml, .srt, .vtt. ZIP media suffixes: .png, .jpg, .jpeg, .webp, .gif, .mp4, .webm, .mov, .mp3, .wav, .ogg, .m4a, .flac, .bin. Use safe relative ASCII paths, at most 180 characters, with no traversal, backslashes, empty segments, trailing dots, reserved device names or case collisions. Include 1–256 files and at most 3,000,000 UTF-8 text bytes total; the existing runtime JSON/memory limits also apply. Media transfers use the host limits above. Files are delivered as downloads with a non-executable MIME type; no archive member becomes a host filesystem path.
isFileBundle(value) and the normal validate/test/pack commands enforce this contract. Include normal and edge fixtures containing the complete expected file data. The File notes example is a complete portable package using this output. Import it, change the text, run it and click Download. No custom download code is needed in the Block view.
Original media file admission
The SDK exports MEDIA_IMPORT_FORMATS, MEDIA_IMPORT_MAX_BYTES and inspectMediaImportFile({name,size,type?}) for trusted hosts handling files the user selected. The inspector returns {kind,extension} and rejects unsupported extensions, conflicting MIME categories, empty/nonintegral sizes and files above 50 MiB. It does not read file bytes, grant filesystem access or authorize an upload.
Supported originals are PNG/JPG/JPEG/WebP/GIF images; MP4/MOV/WebM videos; and MP3/WAV/M4A/AAC/OGG audio. Each format specifies the canonical upload MIME type. An absent MIME type or application/octet-stream may use the recognized extension. The existing authenticated media upload endpoint uses the same table and limit, preserves original bytes through its signed PUT, and returns a durable media URL. No image downscaling is applied to audio or video originals.
The host's existing Canvas upload adapter accepts optional signal and assertCurrent controls. The callback must throw when the owning session is no longer current; checks run before upload and before caching or returning results. Isolated previews retain local files without a cloud request. A file selected in a generic Block preview is still temporary unless a supported host persistence flow explicitly saves it; declaring asset.create alone does not upload it.
These utilities extend the shared upload boundary. They are not a new guest file API or proof that the native Import media workspace is a portable sandbox Block.