@gentbajko/slopify 3.16.0 → 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/edge/http/actions.js +2 -2
  2. package/dist/edge/http/actor.js +10 -0
  3. package/dist/edge/http/app.js +21 -0
  4. package/dist/edge/http/checkpoints.js +14 -6
  5. package/dist/edge/http/external-reviews.js +95 -0
  6. package/dist/edge/http/projects.js +36 -18
  7. package/dist/edge/http/reviews.js +53 -18
  8. package/dist/edge/http/revisions.js +5 -1
  9. package/dist/edge/http/settings.js +9 -1
  10. package/dist/edge/http/starts.js +41 -0
  11. package/dist/edge/http/studio.js +5 -130
  12. package/dist/edge/mcp/attribution.js +38 -0
  13. package/dist/edge/mcp/call-route.js +203 -0
  14. package/dist/edge/mcp/excluded-routes.js +95 -0
  15. package/dist/edge/mcp/file-tools.js +141 -0
  16. package/dist/edge/mcp/http.js +79 -0
  17. package/dist/edge/mcp/instructions.js +17 -0
  18. package/dist/edge/mcp/library-tools.js +143 -0
  19. package/dist/edge/mcp/remake-tools.js +90 -0
  20. package/dist/edge/mcp/review-tools.js +64 -0
  21. package/dist/edge/mcp/route-tool.js +61 -0
  22. package/dist/edge/mcp/server.js +48 -0
  23. package/dist/edge/mcp/state-shapes.js +60 -0
  24. package/dist/edge/mcp/tools-channels.js +153 -0
  25. package/dist/edge/mcp/tools-library.js +151 -0
  26. package/dist/edge/mcp/tools-planning.js +137 -0
  27. package/dist/edge/mcp/tools-projects.js +167 -0
  28. package/dist/edge/mcp/tools-publishing.js +148 -0
  29. package/dist/edge/mcp/tools-runs.js +94 -0
  30. package/dist/edge/mcp/tools-schedules.js +143 -0
  31. package/dist/edge/mcp/tools-setup.js +234 -0
  32. package/dist/edge/mcp/tools-storage.js +99 -0
  33. package/dist/edge/mcp/tools.js +21 -0
  34. package/dist/extension/slopify-studio-chrome.zip +0 -0
  35. package/dist/extension/slopify-studio-firefox.zip +0 -0
  36. package/dist/kernel/db/migrations/0055-external-reviews.sql +56 -0
  37. package/dist/kernel/db/migrations/0056-agent-actions.sql +20 -0
  38. package/dist/kernel/db/migrations/0057-agent-action-once.sql +5 -0
  39. package/dist/kernel/db/migrations/0058-revision-work-admission.sql +3 -0
  40. package/dist/kernel/db/migrations/0059-external-review-canceled.sql +29 -0
  41. package/dist/kernel/db/migrations/0060-reservation-work.sql +4 -0
  42. package/dist/kernel/db/migrations/0061-project-control-counts.sql +4 -0
  43. package/dist/kernel/db/migrations/0062-told-failures.sql +8 -0
  44. package/dist/kernel/pipeline.js +6 -0
  45. package/dist/kernel/runner/graph.js +37 -2
  46. package/dist/kernel/runner/index.js +6 -3
  47. package/dist/main.js +29 -13
  48. package/dist/patch-notes/3.17.0.md +10 -0
  49. package/dist/patch-notes/3.18.0.md +13 -0
  50. package/dist/patch-notes/index.json +12 -0
  51. package/dist/slices/admission/admitted-work.js +53 -0
  52. package/dist/slices/admission/heads.js +5 -0
  53. package/dist/slices/admission/repo.js +47 -29
  54. package/dist/slices/admission/schema.js +1 -0
  55. package/dist/slices/admission/stage-writes.js +21 -0
  56. package/dist/slices/admission/start.js +2 -1
  57. package/dist/slices/admission/state-words.js +110 -0
  58. package/dist/slices/agents/attention-work.js +233 -0
  59. package/dist/slices/agents/attention.js +168 -0
  60. package/dist/slices/agents/connection.js +15 -0
  61. package/dist/slices/agents/model.js +29 -0
  62. package/dist/slices/agents/repo.js +66 -0
  63. package/dist/slices/cancel/index.js +4 -1
  64. package/dist/slices/control/index.js +2 -1
  65. package/dist/slices/episodes/summarize.js +2 -1
  66. package/dist/slices/estimate/index.js +6 -2
  67. package/dist/slices/images/run.js +2 -1
  68. package/dist/slices/library/transfer.js +69 -0
  69. package/dist/slices/narration/run.js +2 -1
  70. package/dist/slices/notifications/address.js +40 -0
  71. package/dist/slices/notifications/notifier.js +43 -10
  72. package/dist/slices/notifications/rules.js +111 -45
  73. package/dist/slices/notifications/server.js +14 -0
  74. package/dist/slices/notifications/settings.js +1 -1
  75. package/dist/slices/notifications/subject.js +30 -0
  76. package/dist/slices/notifications/told.js +33 -0
  77. package/dist/slices/play-drafts/readiness.js +9 -2
  78. package/dist/slices/play-drafts/schema.js +2 -1
  79. package/dist/slices/rebuild/narration-words.js +76 -0
  80. package/dist/slices/rebuild/recipe-model.js +1 -0
  81. package/dist/slices/rebuild/recipe-reviews.js +44 -23
  82. package/dist/slices/rebuild/recovery-repo.js +0 -20
  83. package/dist/slices/rebuild/recovery-selection.js +14 -3
  84. package/dist/slices/rebuild/recovery.js +5 -2
  85. package/dist/slices/rebuild/remake-outdated.js +27 -0
  86. package/dist/slices/rebuild/repo.js +7 -2
  87. package/dist/slices/rebuild/review-redo.js +12 -0
  88. package/dist/slices/rebuild/runtime-agent-review.js +121 -0
  89. package/dist/slices/rebuild/runtime-export-inputs.js +26 -11
  90. package/dist/slices/rebuild/runtime-review.js +12 -2
  91. package/dist/slices/rebuild/runtime-run.js +11 -2
  92. package/dist/slices/rebuild/runtime-store.js +32 -5
  93. package/dist/slices/reruns/index.js +2 -1
  94. package/dist/slices/research/run.js +2 -1
  95. package/dist/slices/reviews/external-model.js +21 -0
  96. package/dist/slices/reviews/external-release.js +38 -0
  97. package/dist/slices/reviews/external-repo.js +109 -0
  98. package/dist/slices/reviews/external-rows.js +68 -0
  99. package/dist/slices/reviews/external-waiting.js +75 -0
  100. package/dist/slices/reviews/holds.js +144 -0
  101. package/dist/slices/reviews/model.js +14 -0
  102. package/dist/slices/reviews/repo.js +27 -10
  103. package/dist/slices/reviews/rules.js +5 -3
  104. package/dist/slices/reviews/verdict.js +18 -8
  105. package/dist/slices/revisions/repo.js +3 -0
  106. package/dist/slices/revisions/schema.js +1 -0
  107. package/dist/slices/schedules/agenda.js +47 -17
  108. package/dist/slices/schedules/prepare.js +6 -0
  109. package/dist/slices/schedules/schema.js +13 -1
  110. package/dist/slices/storage/consequences.js +27 -0
  111. package/dist/slices/storage/portable.js +3 -0
  112. package/dist/slices/storage/project-files.js +66 -0
  113. package/dist/slices/studio/settings.js +0 -35
  114. package/dist/slices/studio/stats.js +2 -34
  115. package/dist/slices/subtitles/prepare.js +2 -1
  116. package/dist/slices/uploads/repo.js +12 -6
  117. package/dist/slices/uploads/rules.js +5 -0
  118. package/dist/slices/video/write-export.js +1 -1
  119. package/dist/slices/video/write-subtitles.js +1 -1
  120. package/dist/tutorials/Agents-MCP.md +168 -0
  121. package/dist/tutorials/Calendar.md +2 -1
  122. package/dist/tutorials/First-Launch-and-Welcome.md +1 -0
  123. package/dist/tutorials/Home-and-Projects.md +16 -9
  124. package/dist/tutorials/Home.md +1 -0
  125. package/dist/tutorials/Notifications.md +25 -6
  126. package/dist/tutorials/Project-Page.md +8 -2
  127. package/dist/tutorials/Publishing-to-YouTube.md +1 -0
  128. package/dist/tutorials/Recovery-and-Retries.md +17 -3
  129. package/dist/tutorials/Reviews-and-Checkpoints.md +22 -5
  130. package/dist/tutorials/Schedules.md +1 -1
  131. package/dist/tutorials/Settings-Reference.md +15 -0
  132. package/dist/tutorials/Troubleshooting.md +3 -1
  133. package/dist/tutorials/Your-First-Short.md +1 -0
  134. package/dist/tutorials/_Sidebar.md +1 -0
  135. package/dist/web/assets/{index-VbpUYLAp.css → index-BKbZIxAq.css} +1 -1
  136. package/dist/web/assets/index-Drth_MfQ.js +325 -0
  137. package/dist/web/assets/{pdf-C6KhxeKQ.js → pdf-CQFUyijy.js} +1 -1
  138. package/dist/web/index.html +2 -2
  139. package/package.json +1 -1
  140. package/dist/slices/studio/performance.js +0 -83
  141. package/dist/slices/studio/report-stats.js +0 -39
  142. package/dist/slices/studio/report.js +0 -172
  143. package/dist/web/assets/index-Dvz8bsiD.js +0 -325
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ import { projectStates, stageKinds, stageStates } from "../../kernel/pipeline.js";
3
+ import { progressOf } from "../../kernel/runner/graph.js";
4
+ import { projectStateWord, stageStateWords } from "../../slices/admission/state-words.js";
5
+ const flags = {
6
+ setAside: z.boolean().optional(),
7
+ prepared: z.boolean().optional(),
8
+ waitingForAgent: z.boolean().optional(),
9
+ stopped: z.boolean().optional(),
10
+ stepFailed: z.boolean().optional(),
11
+ };
12
+ const listSchema = z
13
+ .object({
14
+ projects: z.array(z.object({ status: z.enum(projectStates), progress: z.number(), ...flags }).loose()),
15
+ })
16
+ .loose();
17
+ const bodySchema = z
18
+ .object({
19
+ project: z.object({ status: z.enum(projectStates), ...flags }).loose(),
20
+ stages: z.array(z
21
+ .object({
22
+ kind: z.enum(stageKinds),
23
+ state: z.enum(stageStates),
24
+ progressCurrent: z.number().nullable().optional(),
25
+ progressTotal: z.number().nullable().optional(),
26
+ })
27
+ .loose()),
28
+ })
29
+ .loose();
30
+ // The Projects list with each project's state in the words Slopify's pages use.
31
+ export function listWithStateWords(value) {
32
+ const parsed = listSchema.safeParse(value);
33
+ if (!parsed.success)
34
+ return value;
35
+ return {
36
+ ...parsed.data,
37
+ projects: parsed.data.projects.map((project) => ({
38
+ ...project,
39
+ stateWord: projectStateWord(project),
40
+ })),
41
+ };
42
+ }
43
+ // A project's page: the project's and each step's state in words.
44
+ export function projectWithStateWords(value) {
45
+ const parsed = bodySchema.safeParse(value);
46
+ if (!parsed.success)
47
+ return value;
48
+ const { project, stages } = parsed.data;
49
+ const progress = progressOf(stages.map((stage) => ({
50
+ kind: stage.kind,
51
+ state: stage.state,
52
+ progressCurrent: stage.progressCurrent ?? null,
53
+ progressTotal: stage.progressTotal ?? null,
54
+ })));
55
+ return {
56
+ ...parsed.data,
57
+ project: { ...project, stateWord: projectStateWord({ ...project, progress }) },
58
+ stages: stages.map((stage) => ({ ...stage, stateWord: stageStateWords[stage.state] })),
59
+ };
60
+ }
@@ -0,0 +1,153 @@
1
+ import { formBody } from "./route-tool.js";
2
+ export const channelTools = [
3
+ {
4
+ name: "list_channels",
5
+ route: "GET /api/channels",
6
+ title: "List channels",
7
+ description: "Channels with their brand kits.",
8
+ },
9
+ {
10
+ name: "create_channel",
11
+ route: "POST /api/channels",
12
+ title: "Create a channel",
13
+ description: formBody("New channel"),
14
+ },
15
+ {
16
+ name: "get_channel",
17
+ route: "GET /api/channels/:id",
18
+ title: "Read a channel",
19
+ description: "One channel with its cast in order, brand kit and settings.",
20
+ },
21
+ {
22
+ name: "update_channel",
23
+ route: "PUT /api/channels/:id",
24
+ title: "Save a channel",
25
+ description: formBody("the channel's tabs"),
26
+ },
27
+ {
28
+ name: "set_ai_disclosure",
29
+ route: "PUT /api/channels/:id/ai-disclosure",
30
+ title: "Set the AI disclosure",
31
+ description: formBody("the channel's AI disclosure"),
32
+ },
33
+ {
34
+ name: "delete_channel",
35
+ route: "DELETE /api/channels/:id",
36
+ title: "Delete a channel",
37
+ description: "Deletes the channel.",
38
+ confirm: "It is deleted permanently: channels do not go to Settings → Trash, so it cannot be restored. Its cast and their reference pictures, its episode summaries, its video memory and its brand settings go with it. Its projects move to the default channel and keep what they were made with.",
39
+ },
40
+ {
41
+ name: "add_cast_member",
42
+ route: "POST /api/channels/:id/cast",
43
+ title: "Add a cast member",
44
+ description: formBody("the Cast tab's Add"),
45
+ },
46
+ {
47
+ name: "move_cast_member",
48
+ route: "POST /api/channels/:id/cast/move",
49
+ title: "Move a cast member",
50
+ description: `Cast order, or moving a cast member to another channel, like the Cast tab's move. ${formBody("the Cast tab's move")}`,
51
+ },
52
+ {
53
+ name: "update_cast_member",
54
+ route: "PUT /api/channels/cast/:memberId",
55
+ title: "Save a cast member",
56
+ description: formBody("the cast editor"),
57
+ },
58
+ {
59
+ name: "delete_cast_member",
60
+ route: "DELETE /api/channels/cast/:memberId",
61
+ title: "Delete a cast member",
62
+ description: "Deletes the cast member.",
63
+ confirm: "It is deleted permanently: cast members do not go to Settings → Trash, so it cannot be restored. Its reference pictures go with it, and new videos stop using them. Videos already made keep the pictures they were started with.",
64
+ },
65
+ {
66
+ name: "upload_cast_picture",
67
+ route: "POST /api/channels/cast/:memberId/images",
68
+ title: "Upload a cast picture",
69
+ description: "Adds a picture of the cast member from an image file.",
70
+ upload: "raw",
71
+ },
72
+ {
73
+ name: "generate_cast_picture",
74
+ route: "POST /api/channels/cast/:memberId/generate",
75
+ title: "Generate a cast picture",
76
+ description: "Draws a cast picture with the image provider, a paid call, like Generate on the Cast tab.",
77
+ },
78
+ {
79
+ name: "delete_cast_picture",
80
+ route: "DELETE /api/channels/cast/:memberId/images/:imageId",
81
+ title: "Delete a cast picture",
82
+ description: "Removes one picture of the cast member.",
83
+ },
84
+ {
85
+ name: "get_cast_picture",
86
+ route: "GET /api/channels/pictures/:sha256",
87
+ title: "Read a cast picture",
88
+ description: "A cast or brand picture by its sha256, as an image.",
89
+ },
90
+ {
91
+ name: "set_channel_template",
92
+ route: "PUT /api/channels/templates/:templateId",
93
+ title: "Set a template's channel",
94
+ description: formBody("the template's channel choice"),
95
+ },
96
+ {
97
+ name: "list_episode_memories",
98
+ route: "GET /api/channels/:id/episodes",
99
+ title: "Read episode memory",
100
+ description: "The channel's summaries of finished episodes that later scripts may refer to.",
101
+ },
102
+ {
103
+ name: "set_episode_memory",
104
+ route: "PUT /api/channels/:id/episodes/setting",
105
+ title: "Set episode memory",
106
+ description: formBody("the channel's episode memory switch"),
107
+ },
108
+ {
109
+ name: "update_episode_memory",
110
+ route: "PUT /api/channels/:id/episodes/:memoryId",
111
+ title: "Edit an episode summary",
112
+ description: formBody("the episode summary editor"),
113
+ },
114
+ {
115
+ name: "delete_episode_memory",
116
+ route: "DELETE /api/channels/:id/episodes/:memoryId",
117
+ title: "Delete an episode summary",
118
+ description: "Deletes one summary.",
119
+ confirm: "It is deleted permanently: episode summaries do not go to Settings → Trash, so it cannot be restored. New episodes stop being reminded of it. The video itself is not changed.",
120
+ },
121
+ {
122
+ name: "list_channel_videos",
123
+ route: "GET /api/channels/:id/videos",
124
+ title: "Read existing videos",
125
+ description: "The channel's existing video titles imported from YouTube Studio.",
126
+ },
127
+ {
128
+ name: "preview_channel_videos",
129
+ route: "POST /api/channels/:id/videos/preview",
130
+ title: "Preview an existing-videos import",
131
+ description: formBody("the Existing videos import"),
132
+ unrecorded: true,
133
+ },
134
+ {
135
+ name: "import_channel_videos",
136
+ route: "POST /api/channels/:id/videos",
137
+ title: "Import existing videos",
138
+ description: formBody("the Existing videos import"),
139
+ },
140
+ {
141
+ name: "remove_channel_videos",
142
+ route: "DELETE /api/channels/:id/videos",
143
+ title: "Remove every existing video",
144
+ description: "Removes every imported title.",
145
+ confirm: "This removes every imported video title of the channel.",
146
+ },
147
+ {
148
+ name: "remove_channel_video",
149
+ route: "DELETE /api/channels/:id/videos/:videoId",
150
+ title: "Remove an existing video",
151
+ description: "Removes one imported title.",
152
+ },
153
+ ];
@@ -0,0 +1,151 @@
1
+ import { formBody } from "./route-tool.js";
2
+ export const libraryTools = [
3
+ {
4
+ name: "list_prompts",
5
+ route: "GET /api/prompts",
6
+ title: "List prompts",
7
+ description: "Library prompts of every kind.",
8
+ },
9
+ {
10
+ name: "create_prompt",
11
+ route: "POST /api/prompts",
12
+ title: "Create a prompt",
13
+ description: "Body: {kind: article | image | thumbnail | narration | description | shorts | review | script, name, body}.",
14
+ },
15
+ {
16
+ name: "update_prompt",
17
+ route: "PUT /api/prompts/:id",
18
+ title: "Save a prompt",
19
+ description: "Body: {kind, name, body}. Saving adds a history version; a rename follows into templates and Play drafts.",
20
+ },
21
+ {
22
+ name: "set_prompt_photorealistic",
23
+ route: "PUT /api/prompts/:id/photorealistic",
24
+ title: "Mark a prompt photorealistic",
25
+ description: "Body: {photorealistic: true | false}.",
26
+ },
27
+ {
28
+ name: "prompt_history",
29
+ route: "GET /api/prompts/:id/history",
30
+ title: "Read a prompt's history",
31
+ description: "Every saved version of the prompt.",
32
+ },
33
+ {
34
+ name: "restore_prompt_version",
35
+ route: "POST /api/prompts/:id/history/:version/restore",
36
+ title: "Restore a prompt version",
37
+ description: "Makes an earlier version the prompt's text again, as a new version.",
38
+ },
39
+ {
40
+ name: "prompt_used_by",
41
+ route: "GET /api/prompts/:id/used-by",
42
+ title: "Where a prompt is used",
43
+ description: "Templates, drafts and schedules that use the prompt.",
44
+ },
45
+ {
46
+ name: "delete_prompt",
47
+ route: "DELETE /api/prompts/:id",
48
+ title: "Delete a prompt",
49
+ description: "Moves the prompt to the trash.",
50
+ confirm: "Moves it to the trash for 30 days (Settings → Trash). Projects that used it keep their text.",
51
+ },
52
+ {
53
+ name: "list_entries",
54
+ route: "GET /api/entries",
55
+ title: "List intros and outros",
56
+ description: "Library intros and outros.",
57
+ },
58
+ {
59
+ name: "create_entry",
60
+ route: "POST /api/entries",
61
+ title: "Create an intro or outro",
62
+ description: "Body: {category: intro | outro, mode: text | llm, name, body}.",
63
+ },
64
+ {
65
+ name: "update_entry",
66
+ route: "PUT /api/entries/:id",
67
+ title: "Save an intro or outro",
68
+ description: "Body: {category, mode, name, body}. Saving adds a history version.",
69
+ },
70
+ {
71
+ name: "entry_history",
72
+ route: "GET /api/entries/:id/history",
73
+ title: "Read an entry's history",
74
+ description: "Every saved version of the intro or outro.",
75
+ },
76
+ {
77
+ name: "restore_entry_version",
78
+ route: "POST /api/entries/:id/history/:version/restore",
79
+ title: "Restore an entry version",
80
+ description: "Makes an earlier version current again, as a new version.",
81
+ },
82
+ {
83
+ name: "entry_used_by",
84
+ route: "GET /api/entries/:id/used-by",
85
+ title: "Where an entry is used",
86
+ description: "Templates and drafts that use the intro or outro.",
87
+ },
88
+ {
89
+ name: "delete_entry",
90
+ route: "DELETE /api/entries/:id",
91
+ title: "Delete an intro or outro",
92
+ description: "Moves it to the trash.",
93
+ confirm: "Moves it to the trash for 30 days (Settings → Trash). Projects that used it keep their text.",
94
+ },
95
+ {
96
+ name: "list_document_themes",
97
+ route: "GET /api/document-themes",
98
+ title: "List PDF themes",
99
+ description: "The built-in PDF themes and the ones saved in the Library.",
100
+ },
101
+ {
102
+ name: "create_document_theme",
103
+ route: "POST /api/document-themes",
104
+ title: "Create a PDF theme",
105
+ description: formBody("the PDF theme editor"),
106
+ },
107
+ {
108
+ name: "update_document_theme",
109
+ route: "PUT /api/document-themes/:id",
110
+ title: "Save a PDF theme",
111
+ description: formBody("the PDF theme editor"),
112
+ },
113
+ {
114
+ name: "delete_document_theme",
115
+ route: "DELETE /api/document-themes/:id",
116
+ title: "Delete a PDF theme",
117
+ description: "Deletes the theme.",
118
+ confirm: "It is deleted permanently: PDF themes do not go to Settings → Trash, so it cannot be restored. Projects that used it keep their own copy of the settings.",
119
+ },
120
+ {
121
+ name: "list_fonts",
122
+ route: "GET /api/fonts",
123
+ title: "List fonts",
124
+ description: "Caption and document fonts, bundled and uploaded.",
125
+ },
126
+ {
127
+ name: "upload_font",
128
+ route: "POST /api/fonts",
129
+ title: "Upload a font",
130
+ description: "Adds a TTF or OTF font file.",
131
+ upload: "multipart",
132
+ },
133
+ {
134
+ name: "get_shared_glossary",
135
+ route: "GET /api/pronunciations/shared",
136
+ title: "Read the shared glossary",
137
+ description: "The pronunciation glossary shared by every project.",
138
+ },
139
+ {
140
+ name: "get_narration_aliases",
141
+ route: "GET /api/pronunciations/aliases",
142
+ title: "Read narration aliases",
143
+ description: "Library → Narration aliases: words the narration says differently from how they are written.",
144
+ },
145
+ {
146
+ name: "set_narration_aliases",
147
+ route: "PUT /api/pronunciations/aliases",
148
+ title: "Save narration aliases",
149
+ description: "Saves the whole ordered list, like Save aliases. Body: {aliases: [{written, spoken, wholeWord, caseSensitive}]}.",
150
+ },
151
+ ];
@@ -0,0 +1,137 @@
1
+ import { formBody } from "./route-tool.js";
2
+ const drafts = [
3
+ {
4
+ name: "list_drafts",
5
+ route: "GET /api/drafts",
6
+ title: "List Play drafts",
7
+ description: "Play drafts: saved setups for Create, not started yet.",
8
+ },
9
+ {
10
+ name: "create_draft",
11
+ route: "POST /api/drafts",
12
+ title: "Create a Play draft",
13
+ description: "Create, like New project: a draft whose document is the Play form (kind and outputs, format including square 1:1, sources, prompts, voices, reviews, the queue switch). Body: {id: a new UUID, document}. get_draft shows a document's shape; instantiate_template makes one from a template.",
14
+ },
15
+ {
16
+ name: "get_draft",
17
+ route: "GET /api/drafts/:id",
18
+ title: "Read a Play draft",
19
+ description: "A Play draft: its document, version, attachments, latest review and start.",
20
+ },
21
+ {
22
+ name: "save_draft",
23
+ route: "PUT /api/drafts/:id",
24
+ title: "Save a Play draft",
25
+ description: "Saves the draft's document; queue: true is Play's Queue switch, so several videos run one after another. Body: {baseVersion: the draft's version, mutationId: a new UUID, document}.",
26
+ },
27
+ {
28
+ name: "fork_draft",
29
+ route: "POST /api/drafts/:id/fork",
30
+ title: "Copy a Play draft",
31
+ description: "Body: {id: a new UUID for the copy, sourceId: this draft's id, document}.",
32
+ },
33
+ {
34
+ name: "discard_draft",
35
+ route: "DELETE /api/drafts/:id",
36
+ title: "Discard a Play draft",
37
+ description: "Body: {baseVersion}.",
38
+ confirm: "This discards the draft and its uploaded files for good.",
39
+ },
40
+ {
41
+ name: "review_draft",
42
+ route: "POST /api/drafts/:id/review",
43
+ title: "Review a Play draft",
44
+ description: "Play's Review step: readiness, warnings and the cost estimate the person sees before Start. Body: {baseVersion}. start_draft needs this review's id.",
45
+ unrecorded: true,
46
+ },
47
+ {
48
+ name: "start_draft",
49
+ route: "POST /api/drafts/:id/start",
50
+ title: "Start a Play draft",
51
+ description: "Start in Play: starts the reviewed draft's runs, which spend what the review estimated. Body: {baseVersion, reviewId: the id review_draft returned}.",
52
+ },
53
+ {
54
+ name: "upload_draft_attachment",
55
+ route: "PUT /api/drafts/:id/attachments/:attachmentId/file",
56
+ title: "Upload a draft's file",
57
+ description: "Uploads a draft's file (audio, images, thumbnail or establishing image) under the attachment id its document names.",
58
+ upload: "multipart",
59
+ },
60
+ {
61
+ name: "get_draft_attachment",
62
+ route: "GET /api/drafts/:id/attachments/:attachmentId/file",
63
+ title: "Read a draft's picture",
64
+ description: "A draft's uploaded picture, as an image.",
65
+ },
66
+ {
67
+ name: "list_staged_files",
68
+ route: "GET /api/staging",
69
+ title: "List uploaded files",
70
+ description: "Files uploaded for edits and not used yet.",
71
+ },
72
+ {
73
+ name: "upload_staged_file",
74
+ route: "POST /api/staging/:kind",
75
+ title: "Upload a file for an edit",
76
+ description: "Uploads a file an edit uses (kind: the stage it is for); save_revision's edit.uploads names it by the id this returns.",
77
+ upload: "multipart",
78
+ },
79
+ {
80
+ name: "delete_staged_file",
81
+ route: "DELETE /api/staging/:id",
82
+ title: "Remove an uploaded file",
83
+ description: "Removes an uploaded file no edit used.",
84
+ },
85
+ ];
86
+ const templates = [
87
+ {
88
+ name: "list_templates",
89
+ route: "GET /api/project-templates",
90
+ title: "List templates",
91
+ description: "Project templates with their versions.",
92
+ },
93
+ {
94
+ name: "create_template",
95
+ route: "POST /api/project-templates",
96
+ title: "Create a template",
97
+ description: formBody("the template editor"),
98
+ },
99
+ {
100
+ name: "template_from_project",
101
+ route: "POST /api/project-templates/from-project/:projectId",
102
+ title: "Save a project as a template",
103
+ description: formBody("Save as template on the project page"),
104
+ },
105
+ {
106
+ name: "next_chapter_template",
107
+ route: "POST /api/project-templates/next-chapter/:projectId",
108
+ title: "Template for the next chapter",
109
+ description: formBody("Next chapter on the project page"),
110
+ },
111
+ {
112
+ name: "get_template",
113
+ route: "GET /api/project-templates/:id",
114
+ title: "Read a template",
115
+ description: "One template with its current version and history.",
116
+ },
117
+ {
118
+ name: "update_template",
119
+ route: "PUT /api/project-templates/:id",
120
+ title: "Save a template",
121
+ description: `${formBody("the template editor")} Saving adds a version.`,
122
+ },
123
+ {
124
+ name: "delete_template",
125
+ route: "DELETE /api/project-templates/:id",
126
+ title: "Delete a template",
127
+ description: formBody("Delete in Library → Templates"),
128
+ confirm: "Moves it to the trash for 30 days (Settings → Trash). Existing projects and drafts keep their setup.",
129
+ },
130
+ {
131
+ name: "instantiate_template",
132
+ route: "POST /api/project-templates/:id/instantiate",
133
+ title: "Use a template",
134
+ description: "Opens a new Play draft from the template, like Use template.",
135
+ },
136
+ ];
137
+ export const planningTools = [...drafts, ...templates];
@@ -0,0 +1,167 @@
1
+ import { z } from "zod";
2
+ import { oldVersionsConsequence } from "../../slices/storage/consequences.js";
3
+ import { control } from "./route-tool.js";
4
+ import { listWithStateWords, projectWithStateWords } from "./state-shapes.js";
5
+ // What get_project_storage (and the page's Free up space) reads for the old versions.
6
+ const historySchema = z.object({ historyFiles: z.number(), historyBytes: z.number() });
7
+ const projects = [
8
+ {
9
+ name: "list_projects",
10
+ route: "GET /api/projects",
11
+ title: "List projects",
12
+ description: "Every project as the Projects list shows it: id, title, status, stateWord (how Slopify's pages say it: Queued, Running, Paused, Waiting for your review, Waiting for the agent, Stopped (only Continue the run, resume_project, goes on), Prepared, Kept as is, Failed, Done with problems, Done, Canceled; an uploaded project reads Done once nothing it admitted is left to run; a run whose step failed while another step waits for the agent or the person reads by that wait, with stepFailed: true, and Failed once nothing else can go on), stepFailures (each step that failed while another step waits for the agent, the person or its day, even while a third still runs, as kind@finishedAt; absent otherwise), progress from 0 to 1, channelId and uploadedAt.",
13
+ shape: listWithStateWords,
14
+ },
15
+ {
16
+ name: "get_project",
17
+ route: "GET /api/projects/:id",
18
+ title: "Read a project",
19
+ description: "One project as its page shows it: revisionId (the baseRevisionId every change needs), resumable, holding (the checkpoints holding the run now, each waiting for the person or, a schedule's own Before Video hold, for its day), the project with its stateWord, each step with its state, stateWord (Not started, Running, Done, Provided, Failed, Canceled, Off), failure reason and ETA, and its current output files.",
20
+ shape: projectWithStateWords,
21
+ },
22
+ {
23
+ name: "trash_project",
24
+ route: "DELETE /api/projects/:id",
25
+ title: "Delete a project",
26
+ description: "Delete on the Projects list: moves the project to the trash. A running project is refused: cancel it first.",
27
+ confirm: "This moves the project to Settings → Backup & storage → Trash, where it is kept 30 days before it is deleted for good.",
28
+ },
29
+ {
30
+ name: "move_projects_to_channel",
31
+ route: "POST /api/projects/move-channel",
32
+ title: "Move to a channel",
33
+ description: "Move to a channel, for one project or many at once, like Move on the Projects list. Body: {projectIds: [project ids], channelId}.",
34
+ },
35
+ {
36
+ name: "get_run_queue",
37
+ route: "GET /api/projects/queue",
38
+ title: "Read the run queue",
39
+ description: "The batch queue: projects waiting their turn, in order. Play's Queue switch (the draft's queue field) puts a run's videos here.",
40
+ },
41
+ {
42
+ name: "move_in_run_queue",
43
+ route: "POST /api/projects/queue/:projectId/move",
44
+ title: "Move in the run queue",
45
+ description: "Moves a queued project one place earlier or later. Body: {by: -1 | 1}.",
46
+ },
47
+ {
48
+ name: "estimate_run",
49
+ route: "POST /api/projects/estimate",
50
+ title: "Estimate a run",
51
+ description: "The cost estimate Play shows before Start. Body: {draft: a run draft, expectedWords?: 1-100000, items?}. For a Play draft, review_draft returns the same estimate.",
52
+ unrecorded: true,
53
+ },
54
+ {
55
+ name: "get_run_cost",
56
+ route: "GET /api/projects/:id/run-cost",
57
+ title: "Read a project's run cost",
58
+ description: "What the project's runs cost so far, by stage and provider, and any wait on a CLI plan's limits, as its Run cost tab shows.",
59
+ },
60
+ {
61
+ name: "get_week",
62
+ route: "GET /api/home/week",
63
+ title: "Read this week",
64
+ description: "Home's This week: videos made, calls, cost and CLI plan standings. Query: {since: an ISO time, channelId?}.",
65
+ },
66
+ {
67
+ name: "mark_uploaded",
68
+ route: "PUT /api/projects/:id/uploaded",
69
+ title: "Mark uploaded",
70
+ description: "Mark uploaded (or its Undo), like Home → Ready to upload: records that the person uploaded the video. Call it once per project to mark several. Slopify never uploads. Body: {uploaded: true | false}.",
71
+ },
72
+ {
73
+ name: "keep_as_is",
74
+ route: "PUT /api/projects/:id/set-aside",
75
+ title: "Keep as is",
76
+ description: "Keep as is on Home's Needs you: a run waiting only on held work leaves the list without running anything, until the project's next edit. Body: {setAside: true}, or {setAside: false} for its Undo.",
77
+ },
78
+ {
79
+ name: "get_project_storage",
80
+ route: "GET /api/storage/projects/:id",
81
+ title: "Read a project's disk use",
82
+ description: "How much disk the project's outputs, working files and old versions (History) use, and what Keep outputs only or Delete old versions would free.",
83
+ },
84
+ {
85
+ name: "keep_project_outputs",
86
+ route: "POST /api/storage/projects/:id/keep-outputs",
87
+ title: "Keep outputs only",
88
+ description: "Frees a finished project's working files and keeps its outputs, like Free space on the project page.",
89
+ confirm: "This removes the working files the project was made from (images, narration parts, subtitle timing, render settings) and keeps the video, shorts, thumbnail, article, description and document. Changing the project later makes those files again first, which takes time and uses provider credits.",
90
+ },
91
+ {
92
+ name: "delete_old_versions",
93
+ route: "POST /api/storage/projects/:id/old-versions/delete",
94
+ title: "Delete old versions",
95
+ description: "Delete old versions for this one project; answers how many files were deleted and the bytes freed. Delete all old versions and Clean up everything (every project) are the person's, in Settings → Backup & storage.",
96
+ confirm: `${oldVersionsConsequence(undefined, "project")} get_project_storage says how many files and how much space first.`,
97
+ confirmFrom: {
98
+ route: "/api/storage/projects/:id",
99
+ text: (value) => {
100
+ const storage = historySchema.safeParse(value);
101
+ return storage.success
102
+ ? oldVersionsConsequence({ files: storage.data.historyFiles, bytes: storage.data.historyBytes }, "project")
103
+ : undefined;
104
+ },
105
+ },
106
+ },
107
+ ];
108
+ const outputs = [
109
+ {
110
+ name: "preview_added_output",
111
+ route: "POST /api/projects/:id/outputs/preview",
112
+ title: "Preview another output",
113
+ description: "Add another output, first step: what the new output reuses, makes and is estimated to cost, and any choice the project lacks for it. Body: {baseRevisionId, kind: narration | audiobook | images | video | pdf | podcast}.",
114
+ unrecorded: true,
115
+ },
116
+ {
117
+ name: "add_output",
118
+ route: "POST /api/projects/:id/outputs",
119
+ title: "Add another output",
120
+ description: 'Switches the output on as a new version and answers the workKeys to make; it starts nothing. Then preview_rebuild with {kind: "selected", workKeys} and start_rebuild, as the page\'s Add does. Body: {baseRevisionId, idempotencyKey: a new UUID, kind, adapt?: true to accept rewriting the text as a conversation}.',
121
+ },
122
+ ];
123
+ const decisions = [
124
+ {
125
+ name: "get_checkpoints",
126
+ route: "GET /api/projects/:id/checkpoints",
127
+ title: "Read review checkpoints",
128
+ description: "The project's review checkpoints: each gate's stage, state, fingerprint and currentFingerprint. A gate is Waiting for your review while held.",
129
+ },
130
+ {
131
+ name: "approve_checkpoint",
132
+ route: "POST /api/projects/:id/checkpoints/:checkpointId/approve",
133
+ title: "Approve a checkpoint",
134
+ description: "Approves a held checkpoint so the stages after it run, like Approve on the project page. Body: {revisionId, fingerprint: the gate's currentFingerprint, idempotencyKey: a new UUID}.",
135
+ },
136
+ {
137
+ name: "change_checkpoints",
138
+ route: "PATCH /api/projects/:id/checkpoints",
139
+ title: "Change checkpoints",
140
+ description: 'Sets the stages that stop for approval, like the Checkpoints tab. Body: {revisionId, stages: ["audio" | "images" | "video"]}.',
141
+ },
142
+ {
143
+ name: "list_reviews",
144
+ route: "GET /api/projects/:id/reviews",
145
+ title: "Read review verdicts",
146
+ description: "Each reviewed item's latest verdict: passed, reasons, outcome, attempt, action and redo state, and whether it is about the output shown now (current).",
147
+ },
148
+ {
149
+ name: "overrule_review",
150
+ route: "POST /api/projects/:id/reviews/:verdictId/overrule",
151
+ title: "Overrule a verdict",
152
+ description: "Keeps a flagged item as it is, like Overrule; it also calls off a redo still waiting to start.",
153
+ },
154
+ {
155
+ name: "redo_review",
156
+ route: "POST /api/projects/:id/reviews/:verdictId/redo",
157
+ title: "Redo a flagged item",
158
+ description: `Makes a flagged item again, like Redo beside it. ${control}`,
159
+ },
160
+ {
161
+ name: "submit_review_verdict",
162
+ route: "POST /api/projects/:id/external-reviews/:reviewId/verdict",
163
+ title: "Answer an external review",
164
+ description: 'Your verdict on a review waiting for an agent (read it first with get_external_review). Body: {verdict: "pass" | "fail", reasons: [one short sentence per problem]}: a fail needs at least one reason, at most 10, each 1 to 500 characters. The first verdict saved is kept.',
165
+ },
166
+ ];
167
+ export const projectTools = [...projects, ...outputs, ...decisions];