@sogni-ai/sogni-protocol 1.0.0-alpha.10

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 (95) hide show
  1. package/README.md +89 -0
  2. package/catalogs/audio-models.json +34 -0
  3. package/catalogs/quality-presets.json +30 -0
  4. package/catalogs/seedance-reference-limits.json +9 -0
  5. package/enums/chat-run-status.json +12 -0
  6. package/enums/chat-run-waiting-reasons.json +10 -0
  7. package/enums/token-types.json +7 -0
  8. package/enums/tool-names.json +85 -0
  9. package/manifests/app-tools.json +297 -0
  10. package/manifests/composition-tools.json +228 -0
  11. package/manifests/generation-tools.json +1275 -0
  12. package/manifests/openai-tools.json +1830 -0
  13. package/package.json +44 -0
  14. package/prompts/tools/add_subtitles.json +12 -0
  15. package/prompts/tools/analyze_image.json +12 -0
  16. package/prompts/tools/analyze_video.json +11 -0
  17. package/prompts/tools/animate_photo.json +13 -0
  18. package/prompts/tools/apply_style.json +11 -0
  19. package/prompts/tools/ask_clarifying_question.json +10 -0
  20. package/prompts/tools/change_angle.json +11 -0
  21. package/prompts/tools/compose_instrumental.json +11 -0
  22. package/prompts/tools/compose_lyrics.json +12 -0
  23. package/prompts/tools/compose_script.json +21 -0
  24. package/prompts/tools/compose_workflow.json +17 -0
  25. package/prompts/tools/compose_workflow_template.json +23 -0
  26. package/prompts/tools/create_asset_manifest.json +9 -0
  27. package/prompts/tools/dance_montage.json +9 -0
  28. package/prompts/tools/edit_image.json +11 -0
  29. package/prompts/tools/enhance_prompt.json +20 -0
  30. package/prompts/tools/extend_video.json +11 -0
  31. package/prompts/tools/extract_metadata.json +9 -0
  32. package/prompts/tools/finalize_response.json +10 -0
  33. package/prompts/tools/generate_image.json +11 -0
  34. package/prompts/tools/generate_music.json +7 -0
  35. package/prompts/tools/generate_video.json +10 -0
  36. package/prompts/tools/inspect_asset.json +10 -0
  37. package/prompts/tools/label_asset.json +13 -0
  38. package/prompts/tools/manage_memory.json +12 -0
  39. package/prompts/tools/map_assets_for_model.json +9 -0
  40. package/prompts/tools/orbit_video.json +13 -0
  41. package/prompts/tools/overlay_video.json +10 -0
  42. package/prompts/tools/refine_result.json +11 -0
  43. package/prompts/tools/replace_video_segment.json +14 -0
  44. package/prompts/tools/resolve_personas.json +9 -0
  45. package/prompts/tools/restore_photo.json +12 -0
  46. package/prompts/tools/set_content_filter.json +9 -0
  47. package/prompts/tools/sound_to_video.json +9 -0
  48. package/prompts/tools/stitch_video.json +9 -0
  49. package/prompts/tools/validate_asset_references.json +10 -0
  50. package/prompts/tools/video_to_video.json +13 -0
  51. package/schemas/agent/intent-input.schema.json +128 -0
  52. package/schemas/agent/turn-analysis.schema.json +75 -0
  53. package/schemas/artifacts/artifact-graph.schema.json +42 -0
  54. package/schemas/artifacts/artifact-node.schema.json +137 -0
  55. package/schemas/billing/spend-gate.schema.json +151 -0
  56. package/schemas/billing/workflow-authorization.schema.json +83 -0
  57. package/schemas/errors/error.schema.json +21 -0
  58. package/schemas/errors/repair-control.schema.json +40 -0
  59. package/schemas/events/artifact-reference.schema.json +22 -0
  60. package/schemas/events/progress-event.schema.json +28 -0
  61. package/schemas/events/run-event.schema.json +122 -0
  62. package/schemas/events/workflow-event.schema.json +22 -0
  63. package/schemas/prompt-contract.schema.json +42 -0
  64. package/schemas/storyboards/storyboard-planning-contract.schema.json +108 -0
  65. package/schemas/tools/add_subtitles.schema.json +77 -0
  66. package/schemas/tools/animate_photo.schema.json +104 -0
  67. package/schemas/tools/apply_style.schema.json +37 -0
  68. package/schemas/tools/change_angle.schema.json +30 -0
  69. package/schemas/tools/compose_instrumental.schema.json +24 -0
  70. package/schemas/tools/compose_lyrics.schema.json +28 -0
  71. package/schemas/tools/compose_script.schema.json +68 -0
  72. package/schemas/tools/compose_workflow.schema.json +67 -0
  73. package/schemas/tools/compose_workflow_template.schema.json +156 -0
  74. package/schemas/tools/dance_montage.schema.json +47 -0
  75. package/schemas/tools/edit_image.schema.json +76 -0
  76. package/schemas/tools/enhance_prompt.schema.json +76 -0
  77. package/schemas/tools/extend_video.schema.json +43 -0
  78. package/schemas/tools/generate_image.schema.json +109 -0
  79. package/schemas/tools/generate_music.schema.json +62 -0
  80. package/schemas/tools/generate_video.schema.json +98 -0
  81. package/schemas/tools/manage_memory.schema.json +28 -0
  82. package/schemas/tools/orbit_video.schema.json +70 -0
  83. package/schemas/tools/overlay_video.schema.json +126 -0
  84. package/schemas/tools/refine_result.schema.json +43 -0
  85. package/schemas/tools/replace_video_segment.schema.json +61 -0
  86. package/schemas/tools/resolve_personas.schema.json +15 -0
  87. package/schemas/tools/restore_photo.schema.json +47 -0
  88. package/schemas/tools/set_content_filter.schema.json +14 -0
  89. package/schemas/tools/sound_to_video.schema.json +71 -0
  90. package/schemas/tools/stitch_video.schema.json +52 -0
  91. package/schemas/tools/tool-metadata.schema.json +78 -0
  92. package/schemas/tools/video_to_video.schema.json +78 -0
  93. package/schemas/workflows/durable-workflow-run.schema.json +165 -0
  94. package/schemas/workflows/durable-workflow-step.schema.json +141 -0
  95. package/version.json +4 -0
@@ -0,0 +1,9 @@
1
+ {
2
+ "contractId": "set_content_filter_v1",
3
+ "version": "1.0.0",
4
+ "toolName": "set_content_filter",
5
+ "baseDescription": "set_content_filter enables or disables the Safe Content Filter. Call only when the user\nexplicitly asks to change this setting. Do not toggle it as part of ordinary generation.\n\nIf disabling requires host-side confirmation, let the handler surface that permission flow;\ndo not claim the setting changed unless the tool result says it did.",
6
+ "parameterDocs": {
7
+ "enabled": "true to enable the filter, false to disable it. Only set from explicit user intent."
8
+ }
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "contractId": "sound_to_video_v1",
3
+ "version": "1.0.0",
4
+ "toolName": "sound_to_video",
5
+ "baseDescription": "sound_to_video creates audio-synced video from an audio source. Works with uploaded audio\nfiles (mp3, m4a, wav) OR previously generated music from generate_music (auto-detected).\n\nWhen the user asks to \"turn that song/music into a video\" after generate_music, use\nsound_to_video — it will automatically find the generated audio.\n\nFor music visualization (syncing video to a specific song or audio track), use the\ngenerate_music → sound_to_video pipeline. Do NOT use animate_photo or generate_video for\naudio-driven visualization.\n\nanimate_photo and generate_video produce audio natively via LTX 2.3 — never pre-generate\naudio for those tools. sound_to_video is only for when the audio IS the primary creative\ninput driving the video output.",
6
+ "parameterDocs": {
7
+ "audioSource": "Uploaded audio file or reference to a prior generate_music result. Auto-detected when omitted after generate_music."
8
+ }
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "contractId": "stitch_video_v1",
3
+ "version": "1.0.0",
4
+ "toolName": "stitch_video",
5
+ "baseDescription": "stitch_video joins multiple video clips into one. Reference generated videos by their\n0-based video indices (from videoStartIndex in prior tool results). Reference uploaded\nvideos with negative indices in current UI order: -1 = first uploaded video, -2 = second, etc.\nWhen the user asks to stitch these/all uploaded videos and does not name a different order,\nuse the current UI order exactly: [-1,-2,...].\nIf the user explicitly names a different playback order, preserve that requested order exactly;\ndo not rewrite it back to UI order.\n\nNEVER tell the user to \"upload\" a video that was already generated in this conversation —\nstitch_video can reference them directly by index.\n\nWhen stitching results from a batch video tool, use ONLY the video indices actually returned\nby that tool. Do not infer the stitch list from the number of source images, keyframes, or\nstoryboard panels. Example: if 5 uploaded keyframes create 4 adjacent animate_photo\ntransition clips and the tool result returns videos at indices 0,1,2,3, call stitch_video\nwith videoIndices=[0,1,2,3] — never include index 4 unless a fifth video was returned.\n\nDo not use stitch_video for alternating/interleaved time slices such as \"alternate\n1s from each video\"; stitch_video joins whole clips end-to-end, while interleaving existing\nvideo slices belongs to repeated replace_video_segment calls with replacementVideoIndex and\nreplacementStartSeconds/replacementEndSeconds.\n\nNote: video_to_video requires an actual uploaded video file and cannot use generated video\nindices. Do not claim exact final runtime, dimensions, or aspect ratio after a tool finishes\nunless that value was explicitly requested by the user or explicitly returned by the tool.",
6
+ "parameterDocs": {
7
+ "videoIndices": "Ordered source video indices. Use non-negative generated-video indices from prior tool results; use negative uploaded-video indices in current UI order (-1 first uploaded video, -2 second, etc.)."
8
+ }
9
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "contractId": "validate_asset_references_v1",
3
+ "version": "1.0.0",
4
+ "toolName": "validate_asset_references",
5
+ "baseDescription": "validate_asset_references checks a prompt for provider-specific manifest reference tokens\nand reports which resolve or dangle. Call right before dispatching a prompt that names\nmanifest assets with model_ref tokens.\n\nDo not use this for plain uploaded references. If validation finds dangling refs, repair the\nprompt or register/map the asset before running an expensive generation tool.",
6
+ "parameterDocs": {
7
+ "model_id": "Target provider/model id whose reference token format should be scanned.",
8
+ "prompt": "Prompt text about to be sent to the model."
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "contractId": "video_to_video_v1",
3
+ "version": "1.0.0",
4
+ "toolName": "video_to_video",
5
+ "baseDescription": "video_to_video transforms an uploaded video. Use for uploaded-video restyling, enhancement,\nupscaling/remastering, motion transfer from video to image, subject replacement, edge/pose/\ndepth-guided restyle, or explicit Seedance V2V transforms.\n\nThis tool requires an uploaded video source. Do not use it for generated video indices. For\ngenerated or uploaded partial edits use replace_video_segment; for appended time use\nextend_video; for logos/text overlays use overlay_video; for stitching use stitch_video.\n\nChoose controlMode by intent. Use detailer for quality-only enhancement without restyling.\nUse seedance-v2v only when the user asks to transform/enhance/remaster an uploaded video\nwith Seedance. For detailer, describe the original scene plus quality terms, not new content.",
6
+ "parameterDocs": {
7
+ "prompt": "Describe the target appearance in present tense. For detailer, describe the original content plus quality qualifiers only.",
8
+ "videoSourceIndex": "Uploaded video index. Omit when there is one uploaded video; use 0 for first uploaded video or -1 if using negative upload notation.",
9
+ "controlMode": "Pick from intent: detailer for enhance, seedance-v2v for explicit Seedance V2V, canny/depth/pose for control-net restyles, animate-move/replace for WAN Animate.",
10
+ "sourceImageIndex": "Required for animate-move and animate-replace. Ignored by canny, depth, and detailer.",
11
+ "duration": "Set only when the user requests a different output length; otherwise let the tool match/cap the source duration."
12
+ }
13
+ }
@@ -0,0 +1,128 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.sogni.ai/creative-agent/2026-05-20.1/agent/intent-input.schema.json",
4
+ "title": "L1 IntentClassifier input packet",
5
+ "schemaVersion": "2026-05-20.1",
6
+ "description": "Compact context packet handed to the L1 IntentClassifier at the start of every user turn. Qwen3 256k context window comfortably holds 16-reference-image generations and multi-segment storyboard frames; the runtime never silently drops artifacts. Trim only when measured budget pressure forces it. This contract is built deterministically by the host (browser or cloud runner) from explicit runtime state; it never re-derives semantics from transcript regex.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "currentMessage": {
11
+ "type": "string",
12
+ "description": "Raw user message text for this turn. Verbatim; sanitization belongs to boundary code, not this contract."
13
+ },
14
+ "currentMessageDetails": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "description": "Optional structured form of the user's latest message. Producers MAY emit either currentMessage or this; consumers MUST handle BOTH. `text` is required so consumers can always derive a string-level view.",
18
+ "properties": {
19
+ "id": { "type": "string" },
20
+ "role": { "type": "string", "enum": ["user", "system"] },
21
+ "text": { "type": "string" },
22
+ "createdAt": { "type": "string", "format": "date-time" },
23
+ "localeHint": { "type": "string" }
24
+ },
25
+ "required": ["text"]
26
+ },
27
+ "runtimeFlags": {
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "description": "Optional runtime feature flags the planner reads to decide tool surface and confirmation policy.",
31
+ "properties": {
32
+ "surface": {
33
+ "type": "string",
34
+ "enum": ["browser", "hosted_chat", "durable_chat", "workflow", "native", "skill"],
35
+ "description": "Originating producer surface."
36
+ },
37
+ "allowPaidTools": { "type": "boolean" },
38
+ "allowMutatingTools": { "type": "boolean" },
39
+ "durableRequired": { "type": "boolean" }
40
+ }
41
+ },
42
+ "activeState": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "description": "Currently-resolved focus state owned by the runtime (artifact graph + workflow runner). Populated from explicit runtime references — never from prose extraction.",
46
+ "properties": {
47
+ "activeArtifactId": { "type": "string" },
48
+ "activeArtifactType": {
49
+ "type": "string",
50
+ "enum": ["image", "video", "audio", "text", "workflow", "collection"]
51
+ },
52
+ "pendingAction": {
53
+ "type": "object",
54
+ "description": "Opaque reference to a runtime-tracked pending action (e.g. proposed plan awaiting selection). Shape is owned by the runtime, not this schema.",
55
+ "additionalProperties": true
56
+ },
57
+ "awaitingConfirmation": { "type": "boolean" },
58
+ "lastToolResult": {
59
+ "type": "object",
60
+ "additionalProperties": false,
61
+ "properties": {
62
+ "toolName": { "type": "string" },
63
+ "toolCallId": { "type": "string" },
64
+ "status": { "type": "string" }
65
+ },
66
+ "required": ["toolName", "toolCallId", "status"]
67
+ },
68
+ "activeWorkflowRunId": { "type": "string" }
69
+ }
70
+ },
71
+ "artifactState": {
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "description": "Stable artifact identifiers owned by the ArtifactGraph. Qwen3 256k context window comfortably holds 16-reference-image generations and multi-segment storyboard frames; the runtime never silently drops artifacts. Trim only when measured budget pressure forces it.",
75
+ "properties": {
76
+ "selectedArtifactIds": {
77
+ "type": "array",
78
+ "description": "Artifacts the user or planner has explicitly focused (e.g. multi-select for batch edit).",
79
+ "items": { "type": "string" }
80
+ },
81
+ "artifactIds": {
82
+ "type": "array",
83
+ "description": "Full conversation artifact id list. UNBOUNDED by default. Qwen3 256k context window comfortably holds 16-reference-image generations and multi-segment storyboard frames; the runtime never silently drops artifacts. Trim only when measured budget pressure forces it.",
84
+ "items": { "type": "string" }
85
+ },
86
+ "lastGeneratedArtifactId": { "type": "string" },
87
+ "lastEditedArtifactId": { "type": "string" }
88
+ },
89
+ "required": ["selectedArtifactIds", "artifactIds"]
90
+ },
91
+ "recentTurns": {
92
+ "type": "array",
93
+ "description": "Recent transcript window. UNBOUNDED by default. Qwen3 256k context window comfortably holds 16-reference-image generations and multi-segment storyboard frames; the runtime never silently drops artifacts. Trim only when measured budget pressure forces it. Sliding-window trimming from contextWindow.ts only fires after measured token budget pressure, and writes the trimmed prefix into conversationSummary.",
94
+ "items": {
95
+ "type": "object",
96
+ "additionalProperties": false,
97
+ "properties": {
98
+ "role": { "type": "string", "enum": ["user", "assistant", "tool", "system"] },
99
+ "content": { "type": "string" },
100
+ "sequence": { "type": "integer", "minimum": 0 }
101
+ },
102
+ "required": ["role", "content", "sequence"]
103
+ }
104
+ },
105
+ "conversationSummary": {
106
+ "type": "string",
107
+ "description": "Rolling summary of trimmed prefix. Empty string until the first measured-budget-pressure trim event."
108
+ },
109
+ "userPreferences": {
110
+ "type": "object",
111
+ "description": "Free-form preference payload (e.g. preferred model tier, default aspect ratio). Deliberate exception: additionalProperties is true here, mirroring the data/metadata pattern in other contracts, because preferences evolve faster than the schema.",
112
+ "additionalProperties": true
113
+ },
114
+ "availableCapabilitiesSummary": {
115
+ "type": "array",
116
+ "description": "Short human-readable capability strings the planner can quote when answering capability questions without invoking any tool.",
117
+ "items": { "type": "string" }
118
+ }
119
+ },
120
+ "required": [
121
+ "currentMessage",
122
+ "activeState",
123
+ "artifactState",
124
+ "recentTurns",
125
+ "conversationSummary",
126
+ "availableCapabilitiesSummary"
127
+ ]
128
+ }
@@ -0,0 +1,75 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.sogni.ai/creative-agent/2026-05-20.1/agent/turn-analysis.schema.json",
4
+ "title": "L1 IntentClassifier output",
5
+ "schemaVersion": "2026-05-20.1",
6
+ "description": "Structured output of the L1 IntentClassifier. Produced by an LLM-backed classifier, a typed planner, runtime state inspection, the artifact graph, or an explicit user signal — never by regex. Per the Codex reconciliation in v2 plan §1.A, the legacy SignalSource value 'regex' is intentionally absent from SignalProvenance; regex is demoted to bounded fact extraction (dimensions, durations, file types) which does not produce semantic intent.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "$defs": {
10
+ "SignalProvenance": {
11
+ "type": "string",
12
+ "description": "Which layer authored this TurnAnalysis. The value 'regex' is intentionally absent — regex extracts bounded facts only and never decides intent, tool surface, or routing.",
13
+ "enum": [
14
+ "classifier",
15
+ "planner",
16
+ "runtime_state",
17
+ "artifact_graph",
18
+ "user_explicit"
19
+ ]
20
+ }
21
+ },
22
+ "properties": {
23
+ "domain": {
24
+ "type": "string",
25
+ "enum": ["chat", "image", "video", "audio", "analysis", "workflow", "memory", "settings", "unknown"]
26
+ },
27
+ "intent": {
28
+ "type": "string",
29
+ "enum": ["generate", "edit", "analyze", "transform", "question", "capability", "continue", "reference", "configure", "clarify", "unknown"]
30
+ },
31
+ "executionMode": {
32
+ "type": "string",
33
+ "enum": ["none", "tool", "multi_tool", "workflow"]
34
+ },
35
+ "userWantsExecution": { "type": "boolean" },
36
+ "isCapabilityQuestion": { "type": "boolean" },
37
+ "isFutureInstruction": { "type": "boolean" },
38
+ "isReferenceOnly": { "type": "boolean" },
39
+ "needsPriorContext": { "type": "boolean" },
40
+ "needsClarification": { "type": "boolean" },
41
+ "referencedArtifacts": {
42
+ "type": "array",
43
+ "items": { "type": "string" },
44
+ "description": "Artifact ids the classifier resolved from the user's message via the artifact graph (positional references like 'the second image' resolve to stable ids before this list is emitted)."
45
+ },
46
+ "requiredCapabilities": {
47
+ "type": "array",
48
+ "items": { "type": "string" },
49
+ "description": "Free-form capability tags the planner must satisfy (e.g. 'image_generation', 'video_stitch')."
50
+ },
51
+ "confidence": {
52
+ "type": "number",
53
+ "minimum": 0,
54
+ "maximum": 1
55
+ },
56
+ "provenance": {
57
+ "$ref": "#/$defs/SignalProvenance"
58
+ }
59
+ },
60
+ "required": [
61
+ "domain",
62
+ "intent",
63
+ "executionMode",
64
+ "userWantsExecution",
65
+ "isCapabilityQuestion",
66
+ "isFutureInstruction",
67
+ "isReferenceOnly",
68
+ "needsPriorContext",
69
+ "referencedArtifacts",
70
+ "requiredCapabilities",
71
+ "needsClarification",
72
+ "confidence",
73
+ "provenance"
74
+ ]
75
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.sogni.ai/creative-agent/2026-05-20.1/artifacts/artifact-graph.schema.json",
4
+ "title": "Artifact graph serialization",
5
+ "schemaVersion": "2026-05-20.1",
6
+ "description": "Durable serialization of the in-memory ArtifactGraph. The same shape mirrors into ChatRunRecord.artifacts[] and WorkflowRunRecord.artifacts[] so cloud and hosted runners can reconstitute the graph on resume. The runtime Map<string, ArtifactNode> serializes as a positional nodes[] array; artifactId uniqueness is a runtime invariant, not expressible cheaply in JSON Schema.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "nodes": {
11
+ "type": "array",
12
+ "description": "All artifact nodes in the graph. Uniqueness on artifactId is enforced by the runtime, not the schema.",
13
+ "items": {
14
+ "$ref": "./artifact-node.schema.json"
15
+ }
16
+ },
17
+ "selectedId": {
18
+ "type": "string",
19
+ "description": "Optional currently-selected artifact id. When present, MUST match an artifactId in nodes."
20
+ },
21
+ "compatibilityProjections": {
22
+ "type": "object",
23
+ "additionalProperties": false,
24
+ "description": "Read-only legacy projections derived from nodes[]. Populated during the Phase 1-4 transition so existing chat-side code that still reads positional URL arrays keeps working. Deleted after plan Phase 5.",
25
+ "properties": {
26
+ "resultUrls": {
27
+ "type": "array",
28
+ "items": { "type": "string" }
29
+ },
30
+ "videoResultUrls": {
31
+ "type": "array",
32
+ "items": { "type": "string" }
33
+ },
34
+ "audioResultUrls": {
35
+ "type": "array",
36
+ "items": { "type": "string" }
37
+ }
38
+ }
39
+ }
40
+ },
41
+ "required": ["nodes"]
42
+ }
@@ -0,0 +1,137 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.sogni.ai/creative-agent/2026-05-20.1/artifacts/artifact-node.schema.json",
4
+ "title": "Artifact graph node",
5
+ "schemaVersion": "2026-05-20.1",
6
+ "description": "One node in the v2 ArtifactGraph. ArtifactNode replaces positional URL arrays (resultUrls / videoResultUrls / audioResultUrls) deleted in plan Phase 5. Every tool result auto-registers one node. Lineage edges resolve continuation ('use the second image', 'make it cinematic') without transcript scraping. Per-model token shape lives in modelRefs (gpt-image-2 'Image 1', seedance-2 '@Image1', ltx-2.3 'context_image_0').",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "$defs": {
10
+ "ArtifactEdge": {
11
+ "type": "object",
12
+ "additionalProperties": false,
13
+ "description": "Typed lineage edge from this artifact to one of its parents.",
14
+ "properties": {
15
+ "parentId": { "type": "string" },
16
+ "relation": {
17
+ "type": "string",
18
+ "enum": [
19
+ "derived_from",
20
+ "edited_from",
21
+ "styled_from",
22
+ "animated_from",
23
+ "stitched_from",
24
+ "extended_from",
25
+ "segmented_from",
26
+ "reference_for"
27
+ ]
28
+ }
29
+ },
30
+ "required": ["parentId", "relation"]
31
+ },
32
+ "ArtifactVersion": {
33
+ "type": "object",
34
+ "additionalProperties": false,
35
+ "description": "One version of an artifact. Retries, refinements, and user-driven redos all append a version rather than mutating an existing one.",
36
+ "properties": {
37
+ "versionId": { "type": "string" },
38
+ "uri": { "type": "string" },
39
+ "createdAt": { "type": "string", "format": "date-time" },
40
+ "reason": {
41
+ "type": "string",
42
+ "enum": ["initial", "retry", "refinement", "audit_repair", "user_redo"]
43
+ },
44
+ "jobId": {
45
+ "type": "string",
46
+ "description": "Optional sogni-socket job id that produced this version."
47
+ }
48
+ },
49
+ "required": ["versionId", "createdAt", "reason"]
50
+ },
51
+ "ArtifactSource": {
52
+ "oneOf": [
53
+ {
54
+ "type": "object",
55
+ "additionalProperties": false,
56
+ "properties": {
57
+ "type": { "const": "upload" },
58
+ "uploadId": { "type": "string" }
59
+ },
60
+ "required": ["type", "uploadId"]
61
+ },
62
+ {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "properties": {
66
+ "type": { "const": "tool_result" },
67
+ "runId": { "type": "string" },
68
+ "toolCallId": { "type": "string" }
69
+ },
70
+ "required": ["type", "toolCallId"]
71
+ },
72
+ {
73
+ "type": "object",
74
+ "additionalProperties": false,
75
+ "properties": {
76
+ "type": { "const": "workflow_stage" },
77
+ "workflowRunId": { "type": "string" },
78
+ "stageId": { "type": "string" },
79
+ "itemId": { "type": "string" }
80
+ },
81
+ "required": ["type", "workflowRunId", "stageId"]
82
+ }
83
+ ]
84
+ }
85
+ },
86
+ "properties": {
87
+ "artifactId": {
88
+ "type": "string",
89
+ "pattern": "^art_(?:[0-9A-Z]{26}|[0-9a-fA-F]{32}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$",
90
+ "description": "Stable artifact id. ULID form (`art_` + 26-char Crockford base32 body) is RECOMMENDED for new ids — use `generateUlidArtifactId()` from `@sogni-ai/sogni-intelligence-client/artifacts`. Two legacy forms remain accepted so existing in-wild ids stay valid: `art_` + 32 hex chars (UUID with hyphens stripped, the historical `createArtifactNode` output) and `art_` + canonical UUID with hyphens. Read-side validators must keep accepting all three; write-side code SHOULD enforce ULID via `preferUlid()`."
91
+ },
92
+ "kind": {
93
+ "type": "string",
94
+ "enum": ["image", "video", "audio", "text", "workflow", "collection"]
95
+ },
96
+ "uri": {
97
+ "type": "string",
98
+ "description": "Canonical resolvable URI for the current version (mirrors versions[last].uri for convenience)."
99
+ },
100
+ "mimeType": { "type": "string" },
101
+ "userLabel": {
102
+ "type": "string",
103
+ "description": "Friendly label the user (or the system on the user's behalf) chose. Optional; planner falls back to artifactId + kind."
104
+ },
105
+ "modelRefs": {
106
+ "type": "object",
107
+ "additionalProperties": { "type": "string" },
108
+ "description": "Per-model formatter mapping. Keys are model ids; values are the token that model expects in prompts. Examples: gpt-image-2 -> 'Image 1', seedance-2 -> '@Image1', ltx-2.3 -> 'context_image_0'. Use the asset-reference helpers in @sogni/creative-agent rather than hand-formatting."
109
+ },
110
+ "source": { "$ref": "#/$defs/ArtifactSource" },
111
+ "parents": {
112
+ "type": "array",
113
+ "items": { "$ref": "#/$defs/ArtifactEdge" }
114
+ },
115
+ "versions": {
116
+ "type": "array",
117
+ "minItems": 1,
118
+ "items": { "$ref": "#/$defs/ArtifactVersion" }
119
+ },
120
+ "metadata": {
121
+ "type": "object",
122
+ "additionalProperties": true,
123
+ "description": "Free-form artifact metadata (width/height, durationSeconds, seed, prompt summary, etc.). Boundary code populates known fields; consumers must not assume any specific field is present."
124
+ },
125
+ "createdAt": { "type": "string", "format": "date-time" }
126
+ },
127
+ "required": [
128
+ "artifactId",
129
+ "kind",
130
+ "modelRefs",
131
+ "source",
132
+ "parents",
133
+ "versions",
134
+ "metadata",
135
+ "createdAt"
136
+ ]
137
+ }
@@ -0,0 +1,151 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.sogni.ai/creative-agent/2026-05-20.1/billing/spend-gate.schema.json",
4
+ "title": "Spend gate request and state",
5
+ "schemaVersion": "2026-05-20.1",
6
+ "description": "Single shared spend-approval state machine for atomic tool calls (scope='tool_call'), grouped concurrent dispatches (scope='parallel_batch'), and workflow authorizations (scope='workflow_run'). One envelope, one state enum, one transition log. Per-job settlement remains on the sogni-socket project+N path — this gate authorizes spend, it does not move funds. Note: additionalProperties:false is applied per oneOf branch, not at the root — a root-level additionalProperties has no sibling properties to whitelist against and would reject every payload.",
7
+ "type": "object",
8
+ "$defs": {
9
+ "SpendGateState": {
10
+ "type": "string",
11
+ "description": "Canonical lifecycle states for a spend gate. 'not_required' = free / no-cost tool. 'preview_required' = estimate must be shown to the user. 'waiting_for_user' = awaiting confirm or cancel. 'confirmed' = user accepted; runner may dispatch. 'cancelled' = user declined. 'insufficient_credit' = wallet balance below estimate. 'safety_review_required' = blocked pending human or automated safety review. 'failed' = unrecoverable error in the gate itself.",
12
+ "enum": [
13
+ "not_required",
14
+ "preview_required",
15
+ "waiting_for_user",
16
+ "confirmed",
17
+ "cancelled",
18
+ "insufficient_credit",
19
+ "safety_review_required",
20
+ "failed"
21
+ ]
22
+ },
23
+ "SpendGateDecision": {
24
+ "type": "string",
25
+ "description": "Decision recorded when the gate leaves waiting_for_user. Three historical vocabularies are accepted: 'confirm'/'cancel' (canonical), 'approved'/'rejected' (pre-2026-05-20 aliases still emitted by sogni-api and sogni-creative-agent durable runs), and 'cancelled' (alternate past-tense spelling collapsing to 'cancel'). Consumers normalize via normalizeSpendDecision so business logic only sees the canonical pair.",
26
+ "enum": ["confirm", "cancel", "approved", "rejected", "cancelled"]
27
+ },
28
+ "SpendEstimateLineItem": {
29
+ "type": "object",
30
+ "additionalProperties": false,
31
+ "description": "One row of the spend estimate breakdown. Sum of (units * model price) across line items yields the estimate's capacityUnits.",
32
+ "properties": {
33
+ "model": { "type": "string" },
34
+ "units": { "type": "number", "minimum": 0 },
35
+ "tokenType": {
36
+ "type": "string",
37
+ "enum": ["spark", "sogni"]
38
+ }
39
+ },
40
+ "required": ["model", "units", "tokenType"]
41
+ },
42
+ "SpendGateEstimate": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "description": "Estimate payload carried on the gate itself. capacityUnits = sum of breakdown units; tokenType = denomination the breakdown is in; maxAcceptableUnits = optional caller-supplied cap.",
46
+ "properties": {
47
+ "capacityUnits": { "type": "number", "minimum": 0 },
48
+ "breakdown": {
49
+ "type": "array",
50
+ "minItems": 1,
51
+ "items": { "$ref": "#/$defs/SpendEstimateLineItem" }
52
+ },
53
+ "tokenType": {
54
+ "type": "string",
55
+ "enum": ["spark", "sogni"]
56
+ },
57
+ "maxAcceptableUnits": { "type": "number", "minimum": 0 }
58
+ },
59
+ "required": ["capacityUnits", "breakdown", "tokenType"]
60
+ },
61
+ "PendingToolCall": {
62
+ "type": "object",
63
+ "additionalProperties": false,
64
+ "description": "Minimal reference to a tool call this gate covers (when scope='tool_call' the array has one entry; when scope='parallel_batch' it carries the constituent calls of the fan-out; when scope='workflow_run' the gate authorizes the umbrella and pendingToolCalls may be empty).",
65
+ "properties": {
66
+ "toolCallId": { "type": "string" },
67
+ "toolName": { "type": "string" },
68
+ "estimateUnits": { "type": "number", "minimum": 0 }
69
+ },
70
+ "required": ["toolCallId", "toolName"]
71
+ },
72
+ "PendingWorkflowPlan": {
73
+ "type": "object",
74
+ "additionalProperties": false,
75
+ "description": "Reference to the workflow run this gate authorizes (only meaningful when scope='workflow_run').",
76
+ "properties": {
77
+ "workflowRunId": { "type": "string" },
78
+ "templateId": { "type": "string" }
79
+ },
80
+ "required": ["workflowRunId", "templateId"]
81
+ }
82
+ },
83
+ "oneOf": [
84
+ {
85
+ "type": "object",
86
+ "additionalProperties": false,
87
+ "properties": {
88
+ "gateId": { "type": "string" },
89
+ "runId": { "type": "string" },
90
+ "scope": { "const": "tool_call" },
91
+ "toolCallId": { "type": "string" },
92
+ "pendingToolCalls": {
93
+ "type": "array",
94
+ "items": { "$ref": "#/$defs/PendingToolCall" }
95
+ },
96
+ "estimate": { "$ref": "#/$defs/SpendGateEstimate" },
97
+ "state": { "$ref": "#/$defs/SpendGateState" },
98
+ "reason": { "type": "string" },
99
+ "decision": { "$ref": "#/$defs/SpendGateDecision" },
100
+ "createdAt": { "type": "string", "format": "date-time" },
101
+ "decidedAt": { "type": "string", "format": "date-time" },
102
+ "updatedAt": { "type": "string", "format": "date-time" },
103
+ "failureReason": { "type": "string" }
104
+ },
105
+ "required": ["gateId", "scope", "toolCallId", "estimate", "state", "updatedAt"]
106
+ },
107
+ {
108
+ "type": "object",
109
+ "additionalProperties": false,
110
+ "properties": {
111
+ "gateId": { "type": "string" },
112
+ "runId": { "type": "string" },
113
+ "scope": { "const": "parallel_batch" },
114
+ "pendingToolCalls": {
115
+ "type": "array",
116
+ "minItems": 1,
117
+ "items": { "$ref": "#/$defs/PendingToolCall" }
118
+ },
119
+ "estimate": { "$ref": "#/$defs/SpendGateEstimate" },
120
+ "state": { "$ref": "#/$defs/SpendGateState" },
121
+ "reason": { "type": "string" },
122
+ "decision": { "$ref": "#/$defs/SpendGateDecision" },
123
+ "createdAt": { "type": "string", "format": "date-time" },
124
+ "decidedAt": { "type": "string", "format": "date-time" },
125
+ "updatedAt": { "type": "string", "format": "date-time" },
126
+ "failureReason": { "type": "string" }
127
+ },
128
+ "required": ["gateId", "scope", "pendingToolCalls", "estimate", "state", "updatedAt"]
129
+ },
130
+ {
131
+ "type": "object",
132
+ "additionalProperties": false,
133
+ "properties": {
134
+ "gateId": { "type": "string" },
135
+ "runId": { "type": "string" },
136
+ "scope": { "const": "workflow_run" },
137
+ "workflowRunId": { "type": "string" },
138
+ "pendingWorkflowPlan": { "$ref": "#/$defs/PendingWorkflowPlan" },
139
+ "estimate": { "$ref": "#/$defs/SpendGateEstimate" },
140
+ "state": { "$ref": "#/$defs/SpendGateState" },
141
+ "reason": { "type": "string" },
142
+ "decision": { "$ref": "#/$defs/SpendGateDecision" },
143
+ "createdAt": { "type": "string", "format": "date-time" },
144
+ "decidedAt": { "type": "string", "format": "date-time" },
145
+ "updatedAt": { "type": "string", "format": "date-time" },
146
+ "failureReason": { "type": "string" }
147
+ },
148
+ "required": ["gateId", "scope", "workflowRunId", "estimate", "state", "updatedAt"]
149
+ }
150
+ ]
151
+ }