@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,27 @@
1
+ // What a storage action removes and what it costs later, in the words its confirm dialog
2
+ // uses: shared by the web page (Settings → Backup & storage, a project's Free up space) and
3
+ // the agent's tool, so both state the same count and size.
4
+ // "512 B", "3.4 MB", "12 GB": binary units, one decimal under ten.
5
+ export function fileSize(bytes) {
6
+ if (bytes < 1024)
7
+ return `${String(bytes)} B`;
8
+ const units = ["KB", "MB", "GB", "TB"];
9
+ let value = bytes;
10
+ let unit = units[0];
11
+ for (const candidate of units) {
12
+ value /= 1024;
13
+ unit = candidate;
14
+ if (value < 1024 || candidate === units.at(-1))
15
+ break;
16
+ }
17
+ return `${value >= 10 ? value.toFixed(0) : value.toFixed(1).replace(/\.0$/, "")} ${unit}`;
18
+ }
19
+ // What Delete old versions removes and what it costs later; without the numbers when they
20
+ // can't be read. `scope` is whose History it empties: every project's, or one project's.
21
+ export function oldVersionsConsequence(size, scope = "projects") {
22
+ const what = size === undefined
23
+ ? "the older versions"
24
+ : `${String(size.files)} file(s) of older versions and frees ${fileSize(size.bytes)}`;
25
+ const where = scope === "project" ? "this project's History folder" : "the projects' History folders";
26
+ return `This deletes ${what}: what is in ${where} (earlier renders, narration before a change). The current video, shorts, thumbnails and everything in Upload stay, and so does anything you uploaded or put there yourself. Going back to one of those older versions makes its files again, which takes time and uses provider credits.`;
27
+ }
@@ -8,6 +8,7 @@ import { transact } from "../../kernel/db/tx.js";
8
8
  import { liveProject } from "../admission/repo.js";
9
9
  import { silenceGapSecondsMax } from "../admission/rules.js";
10
10
  import { detectSlots } from "../admission/substitute.js";
11
+ import { agentConnectionKey } from "../agents/connection.js";
11
12
  import { fontMaxBytes } from "../fonts/model.js";
12
13
  import { readFontMetadata } from "../fonts/sfnt.js";
13
14
  import { lintEntry, lintPrompt } from "../library/lint.js";
@@ -373,9 +374,11 @@ function portableSettings(settings) {
373
374
  // So does the Studio extension's pairing: its token reads every project's upload pack.
374
375
  // Which "What's new" tour and which patch notes this install has closed are about this
375
376
  // install, not the data.
377
+ // When an agent last connected is this install's own fact.
376
378
  if (key === "tutorial.session" ||
377
379
  // Whether this install showed its first-run welcome belongs to this install.
378
380
  key === "first-run.done" ||
381
+ key === agentConnectionKey ||
379
382
  key === notificationUrlKey ||
380
383
  key === studioPairingKey ||
381
384
  // What waits to be filled in Studio belongs to this install's pairing.
@@ -0,0 +1,66 @@
1
+ import { z } from "zod";
2
+ import { desiredPlaces } from "./arrange.js";
3
+ import { assetOf } from "./asset-name.js";
4
+ import { outputPath } from "./layout.js";
5
+ import { outputsOf, projectTitle } from "./repo.js";
6
+ // A project's files as its folder is arranged (`arrange.ts`): Upload (what is published),
7
+ // Working (what it was made from) and History (older versions). The place is where the file
8
+ // belongs even before the folder is arranged; `path` is where it is now and can move, so the
9
+ // `/files` URL is the handle to keep.
10
+ export const fileGroups = ["Upload", "Working", "History"];
11
+ export function groupOf(place) {
12
+ const first = place.split("/", 1)[0];
13
+ return first === "Upload" || first === "History" ? first : "Working";
14
+ }
15
+ const recordRow = z.object({
16
+ path: z.string(),
17
+ revision_id: z.string(),
18
+ id: z.string(),
19
+ role: z.string().nullable(),
20
+ });
21
+ // The newest version record of each stored path, for its `/files/:id/revisions/...` URL.
22
+ function revisionRecords(db, projectId) {
23
+ const records = new Map();
24
+ for (const row of db
25
+ .prepare(`SELECT a.path, o.revision_id, o.id, json_extract(o.descriptor,'$.role') AS role, o.created_at
26
+ FROM revision_outputs o JOIN project_assets a ON a.id=o.asset_id AND a.project_id=o.project_id
27
+ WHERE o.project_id=?
28
+ UNION ALL
29
+ SELECT a.path, p.revision_id, p.id, 'piece:'||COALESCE(json_extract(p.descriptor,'$.kind'),'other'), p.created_at
30
+ FROM revision_pieces p JOIN project_assets a ON a.id=p.asset_id AND a.project_id=p.project_id
31
+ WHERE p.project_id=?
32
+ ORDER BY 5`)
33
+ .all(projectId, projectId)) {
34
+ const record = recordRow.parse(row);
35
+ records.set(record.path, record);
36
+ }
37
+ return records;
38
+ }
39
+ export function projectFiles(deps, projectId) {
40
+ if (projectTitle(deps.db, projectId) === undefined)
41
+ return undefined;
42
+ const current = new Map(outputsOf(deps.db, projectId).map((output) => [output.path, output]));
43
+ const records = revisionRecords(deps.db, projectId);
44
+ return [...desiredPlaces(deps.db, projectId)]
45
+ .flatMap(([stored, place]) => {
46
+ const output = place.startsWith("History/") ? undefined : current.get(stored);
47
+ const record = records.get(stored);
48
+ const url = output !== undefined
49
+ ? `/files/${projectId}/${assetOf(output)}`
50
+ : record === undefined
51
+ ? undefined
52
+ : `/files/${projectId}/revisions/${record.revision_id}/${record.id}`;
53
+ if (url === undefined)
54
+ return [];
55
+ return [
56
+ {
57
+ group: groupOf(place),
58
+ place,
59
+ role: output?.role ?? record?.role ?? "other",
60
+ path: outputPath(deps.paths, projectId, stored),
61
+ url,
62
+ },
63
+ ];
64
+ })
65
+ .toSorted((a, b) => fileGroups.indexOf(a.group) - fileGroups.indexOf(b.group) || a.place.localeCompare(b.place));
66
+ }
@@ -242,38 +242,3 @@ export function studioRequestAllowed(db, token, origin) {
242
242
  return true;
243
243
  return pairing.origin !== null && origin === pairing.origin;
244
244
  }
245
- // Each channel's Studio Analytics view (Advanced mode's Explore link with its chosen columns),
246
- // which the extension opens every day to export into Channels → YouTube. The start stays as
247
- // saved; the extension moves the end to today.
248
- export const studioExportViewPrefix = "studio.exportView.";
249
- export function exportViewProblem(url) {
250
- let parsed;
251
- try {
252
- parsed = new URL(url.trim());
253
- }
254
- catch {
255
- return "That isn't a link. In YouTube Studio, open Analytics → Advanced mode, set it up, and copy the address from the browser's address bar.";
256
- }
257
- if (parsed.origin !== "https://studio.youtube.com" ||
258
- !/^\/channel\/UC[A-Za-z0-9_-]+\/analytics\/.+\/explore$/.test(parsed.pathname))
259
- return "That isn't a Studio Analytics Advanced mode link. In YouTube Studio, open Analytics → Advanced mode, set it up, and copy the address from the browser's address bar.";
260
- if (!parsed.searchParams.has("time_period"))
261
- return "That link has no date range. In Advanced mode, pick the dates (the start stays as you set it; the end follows today), then copy the address again.";
262
- return undefined;
263
- }
264
- export function readExportView(db, channelId) {
265
- return readSetting(db, `${studioExportViewPrefix}${channelId}`) ?? null;
266
- }
267
- export function saveExportView(db, channelId, url) {
268
- const key = `${studioExportViewPrefix}${channelId}`;
269
- if (url === null || url.trim() === "")
270
- db.prepare("DELETE FROM settings WHERE key = ?").run(key);
271
- else
272
- writeSetting(db, key, url.trim());
273
- }
274
- export function exportViews(db) {
275
- const prefix = studioExportViewPrefix;
276
- return db
277
- .prepare("SELECT key, value FROM settings WHERE substr(key, 1, ?) = ? ORDER BY key")
278
- .all(prefix.length, prefix).map((row) => ({ channelId: row.key.slice(prefix.length), url: row.value }));
279
- }
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ // Finished A/B tests as Studio shows them, read by the extension from the video's Analytics.
3
+ // Slopify never asks YouTube's API: these are what Studio showed when the extension last looked.
2
4
  export const abVariantSchema = z.object({
3
5
  title: z.string().max(200).nullable(),
4
6
  // Which thumbnail (1-3) the variant showed, when Studio says.
@@ -8,40 +10,6 @@ export const abVariantSchema = z.object({
8
10
  winner: z.boolean(),
9
11
  });
10
12
  const slot = (short) => short ?? 0;
11
- const num = (value) => (typeof value === "number" ? value : null);
12
- export function saveStats(db, stats) {
13
- db.prepare(`INSERT INTO video_stats(project_id,short,video_id,read_at,impressions,ctr,views,average_view_seconds,watch_hours)
14
- VALUES (?,?,?,?,?,?,?,?,?) ON CONFLICT(project_id,short) DO UPDATE SET video_id=excluded.video_id,
15
- read_at=excluded.read_at, impressions=excluded.impressions, ctr=excluded.ctr, views=excluded.views,
16
- average_view_seconds=excluded.average_view_seconds, watch_hours=excluded.watch_hours`).run(stats.projectId, slot(stats.short), stats.videoId, stats.readAt, stats.impressions, stats.ctr, stats.views, stats.averageViewSeconds, stats.watchHours);
17
- }
18
- function statsOf(row) {
19
- const short = Number(row.short);
20
- return {
21
- projectId: String(row.project_id),
22
- short: short === 0 ? null : short,
23
- videoId: String(row.video_id),
24
- readAt: String(row.read_at),
25
- impressions: num(row.impressions),
26
- ctr: num(row.ctr),
27
- views: num(row.views),
28
- averageViewSeconds: num(row.average_view_seconds),
29
- watchHours: num(row.watch_hours),
30
- };
31
- }
32
- export function projectStats(db, projectId) {
33
- return db
34
- .prepare("SELECT * FROM video_stats WHERE project_id=? ORDER BY short")
35
- .all(projectId)
36
- .map(statsOf);
37
- }
38
- // Each project's long video's numbers, for the Projects list.
39
- export function longVideoStats(db) {
40
- return new Map(db
41
- .prepare("SELECT * FROM video_stats WHERE short=0")
42
- .all()
43
- .map((row) => [String(row.project_id), statsOf(row)]));
44
- }
45
13
  export function saveAbResult(db, result) {
46
14
  db.prepare(`INSERT INTO ab_results(project_id,short,video_id,read_at,variants) VALUES (?,?,?,?,?)
47
15
  ON CONFLICT(project_id,short) DO UPDATE SET video_id=excluded.video_id, read_at=excluded.read_at,
@@ -4,7 +4,8 @@ import { join, relative, sep } from "node:path";
4
4
  import { z } from "zod";
5
5
  import { subtitleModelDir } from "../../kernel/paths.js";
6
6
  import { projectLanguage } from "../../kernel/ports/languages.js";
7
- import { projectById, setStageProgress } from "../admission/repo.js";
7
+ import { projectById } from "../admission/repo.js";
8
+ import { setStageProgress } from "../admission/stage-writes.js";
8
9
  import { captionFont, captionFontDeps } from "../fonts/coverage.js";
9
10
  import { resolveFont } from "../fonts/index.js";
10
11
  import { outputPath, projectDir } from "../storage/layout.js";
@@ -1,3 +1,5 @@
1
+ import { admittedUnfinished } from "../admission/admitted-work.js";
2
+ import { oneProject } from "../admission/repo.js";
1
3
  export function markUploaded(db, projectId, uploaded, at) {
2
4
  if (db.prepare("SELECT 1 FROM projects WHERE id=?").get(projectId) === undefined)
3
5
  return { ok: false, reason: "not-found" };
@@ -18,15 +20,19 @@ export function uploadedProjects(db) {
18
20
  .all()
19
21
  .map((row) => [String(row.project_id), String(row.uploaded_at)]));
20
22
  }
21
- // Uploaded projects with nothing admitted still to run: their leftover steps (a placeholder
22
- // from an edit after the upload, a pause from a bulk Pause) are not work the person is
23
- // waiting for, so the listing calls them done rather than "Paused" or "Waiting".
24
- export function uploadedAndSettled(db) {
23
+ // Uploaded projects with nothing left to run read done, not "Paused" or "Waiting": a step still
24
+ // allowed to run, or a held one admitted for the current version (`admittedUnfinished`, as
25
+ // `reviews/holds.ts` heldBesideReviews reads it), keeps one unsettled. What is left otherwise
26
+ // - a placeholder from an edit after the upload, a pause from a bulk Pause - is not work the
27
+ // person waits for. One project's alone when `projectId` is given.
28
+ export function uploadedAndSettled(db, projectId) {
29
+ const one = oneProject("u", projectId);
25
30
  return new Set(db
26
31
  .prepare(`SELECT u.project_id FROM project_uploads u WHERE NOT EXISTS(
27
32
  SELECT 1 FROM revision_work w WHERE w.project_id=u.project_id
28
- AND w.state IN ('pending','running') AND w.dispatch_state!='held')`)
29
- .all()
33
+ AND w.state IN ('pending','running')
34
+ AND (w.dispatch_state!='held' OR ${admittedUnfinished("w")}))${one.sql}`)
35
+ .all(...one.params)
30
36
  .map((row) => String(row.project_id)));
31
37
  }
32
38
  // "Keep as is": a waiting run the person leaves as it is, until the project's next edit. Set on
@@ -0,0 +1,5 @@
1
+ // An uploaded, settled project (`uploadedAndSettled`) reads done where its run would read paused
2
+ // or pending: the Projects list, a project's page and the calendar.
3
+ export function settledStatus(status, settled) {
4
+ return settled && (status === "paused" || status === "pending") ? "done" : status;
5
+ }
@@ -1,7 +1,7 @@
1
1
  import { copyFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
2
  import { dirname } from "node:path";
3
3
  import { transact } from "../../kernel/db/tx.js";
4
- import { setStageProgress } from "../admission/repo.js";
4
+ import { setStageProgress } from "../admission/stage-writes.js";
5
5
  import { outputPath } from "../storage/layout.js";
6
6
  import { deleteOutput, insertOutput, outputsOf } from "../storage/repo.js";
7
7
  import { subtitleRoles } from "../subtitles/prepare.js";
@@ -1,6 +1,6 @@
1
1
  import { rmSync, statSync } from "node:fs";
2
2
  import { transact } from "../../kernel/db/tx.js";
3
- import { setStageProgress } from "../admission/repo.js";
3
+ import { setStageProgress } from "../admission/stage-writes.js";
4
4
  import { outputPath } from "../storage/layout.js";
5
5
  import { deleteOutput, insertOutput, outputsOf } from "../storage/repo.js";
6
6
  import { subtitleRoles } from "../subtitles/prepare.js";
@@ -0,0 +1,168 @@
1
+ # Agents (MCP)
2
+
3
+ Slopify has a built-in MCP server, so an AI agent on your computer (Claude Code, Codex, or any other client that speaks MCP over HTTP) can run Slopify for you. It can start and fix runs, edit the Library and your setup, answer reviews and approve checkpoints, with the same checks, costs and refusals as the buttons in the web page. Run on a schedule, an agent can clear everything that would otherwise wait for you, right up to **Ready to upload**. Uploading stays yours.
4
+
5
+ **Where to find it:** **Settings → Connections → Agents (MCP)**.
6
+
7
+ ## What the Agents tab shows
8
+
9
+ | Part | What it is |
10
+ | --- | --- |
11
+ | **MCP address** | The address agents connect to: this install's address with `/mcp` after it, for example `http://127.0.0.1:6969/mcp`. It uses the address in your browser's address bar, so it matches your port and Docker's published port. |
12
+ | **Claude Code** | The command that adds Slopify to Claude Code, with **Copy**. Run it once in a terminal. |
13
+ | **Codex** | The lines that add Slopify to Codex, with **Copy**. Add them to `~/.codex/config.toml`. |
14
+ | Last connected | When an agent last connected, or "No agent has connected yet." |
15
+ | **What agents did** | The latest 20 changes made by agents, newest first: what was done, the project or schedule it was done to, and when. Changes outside a project (a Library prompt, a schedule, a setting) are listed here too. "No agent has changed anything yet." when there are none. |
16
+
17
+ If the browser blocks the clipboard, **Copy** says so; select the line and copy it yourself.
18
+
19
+ ## Connect an agent
20
+
21
+ Slopify must be running. The address in these examples is the default (`127.0.0.1`, port `6969`); copy the real lines from the Agents tab.
22
+
23
+ ### Claude Code
24
+
25
+ 1. Open **Settings → Connections → Agents (MCP)** and press **Copy** next to **Claude Code**.
26
+ 2. Paste it in a terminal and run it:
27
+
28
+ ```
29
+ claude mcp add --transport http slopify http://127.0.0.1:6969/mcp
30
+ ```
31
+
32
+ 3. Start Claude Code. `/mcp` there should list `slopify` as connected, and the Agents tab now shows when it last connected.
33
+
34
+ ### Codex
35
+
36
+ 1. Press **Copy** next to **Codex**.
37
+ 2. Add the lines to `~/.codex/config.toml`:
38
+
39
+ ```
40
+ [mcp_servers.slopify]
41
+ url = "http://127.0.0.1:6969/mcp"
42
+ ```
43
+
44
+ 3. Start Codex again so it reads the file.
45
+
46
+ ### Other agents
47
+
48
+ Any MCP client that connects by URL over Streamable HTTP takes the same **MCP address**. Slopify keeps no session: every request is a plain `POST`.
49
+
50
+ ### Docker
51
+
52
+ Nothing changes: the agent runs on your computer and connects to the port Docker publishes, the same address you open Slopify at. Copy the lines from the Agents tab in that Slopify. See [Docker](Docker).
53
+
54
+ ## No token, and what keeps web pages out
55
+
56
+ There is no key or token to set up, the same as the web page itself: Slopify has no login, and it listens on `127.0.0.1` so only your own computer reaches it.
57
+
58
+ Web pages are kept out by one check. A request that comes from a web page at another address (a browser always says where a request came from) is refused with "Slopify refused this MCP request because it came from a web page at another address." Agents on the command line send no such address and are let in. Pages on this computer's own addresses (`localhost`, `127.0.0.1`) are let in too.
59
+
60
+ If you start Slopify with `--host 0.0.0.0` (or another address other computers can reach), anyone who reaches the port can drive it through `/mcp`, just as they can through the web page. Keep it on `127.0.0.1` unless you mean that. See [Install](Install).
61
+
62
+ ## What agents can do
63
+
64
+ Anything you can do in the web page, except the few things listed under [What agents cannot do](#what-agents-cannot-do). That includes:
65
+
66
+ - **Projects:** make projects from Play drafts and templates (after the same review and estimate you see), read them, edit them, pause, continue and cancel runs, **Keep as is**, move projects to another channel, delete and restore them.
67
+ - **Fixing runs:** **Try ... again**, **Soften and retry**, remake a step or one item, regenerate a narration chunk, and **Choose what to remake**, with its estimate.
68
+ - **Reviews:** answer reviews set to **External agent (MCP)**, **Overrule** or **Redo** flagged items, and approve review checkpoints.
69
+ - **Planning:** release times, the posting plan, **Mark uploaded**, **Deleted on YouTube**, and **Prepare upload** up to **Ready to upload**.
70
+ - **Schedules:** schedules, their topic lists, approving or turning down held topics, and **Prepare**.
71
+ - **Library and setup:** prompts (with history), templates, channels and cast, Library import and export, and settings other than those below.
72
+ - **Reading:** Home's work list, the calendar, costs and estimates, a project's files (pictures come back as pictures; audio and video as their address in Slopify), and the material a review looks at.
73
+ - **Storage:** **Delete old versions** for one project.
74
+
75
+ Agents never start paid work you could not see: before a remake they get the same **Choose what to remake** list with its estimate, and they must accept it, as you do. There is no extra budget or limit for agents; the costs are the same as yours. Actions with a consequence (deleting, for example) need the agent to confirm them, as a dialog asks you.
76
+
77
+ Agents and their tools use the same state words as the page: **Queued**, **Running**, **Paused**, **Waiting for your review**, **Waiting for the agent**, **Prepared**, **Stopped**, **Kept as is**, **Failed**, **Done**.
78
+
79
+ ## What agents cannot do
80
+
81
+ | Not for agents | Why |
82
+ | --- | --- |
83
+ | Upload to YouTube, or send a video to the Studio extension (**Fill in YouTube Studio**, **Upload**) | Agents stop at **Ready to upload**. Uploading is yours; Slopify never makes a video public. See [Publishing to YouTube](Publishing-to-YouTube). |
84
+ | The extension's automatic pinned comment setting | It posts comments in your name. |
85
+ | Read or change provider keys, or make the extension's pairing token | They are credentials. Agents see each provider only as configured or missing. |
86
+ | Read the **Notification URL** | It can hold a secret (a private ntfy topic). Agents see only whether one is configured or missing, though they may set a new one. |
87
+ | Backups: export, import, restore, and the daily backup settings | They are yours, in Settings → Backup & storage. |
88
+ | Update Slopify, start at login, CLI paths, the extension's download | They are yours. |
89
+ | **Delete all old versions** and **Clean up everything** (all projects) | They are yours, in Settings → Backup & storage. **Delete old versions** for one project is allowed. |
90
+ | Open a folder on your desktop | Agents read files through Slopify instead. |
91
+
92
+ A request for any of these is refused with a sentence that says it is yours and where to do it.
93
+
94
+ ## Shown as "by agent"
95
+
96
+ Everything an agent changes is marked, so you can always tell its work from yours:
97
+
98
+ | Where | What you see |
99
+ | --- | --- |
100
+ | Project page → **Project history** | Versions an agent saved end in **· by agent**, and **Done by agents** lists what agents did to the project (approvals, retries, verdicts and so on). |
101
+ | Review badges | **Passed by agent**, **Flagged by agent**, **Redone after agent review**, **Accepted by agent** (an agent pressed Overrule), **Made again by agent** (an agent pressed Redo). |
102
+ | Notifications | A run an agent started ends its notices with **· started by agent** ("Started: *title* · by agent" in the browser); a flag from an agent's review ends with **· by agent**. |
103
+ | Settings → Agents (MCP) | **What agents did**: the latest 20 changes of every kind. |
104
+
105
+ ## The External agent reviewer
106
+
107
+ Instead of paying a reviewer model, you can let your agent do the [automatic reviews](Reviews-and-Checkpoints#automatic-reviews).
108
+
109
+ 1. On Play, open the **Reviews** row (or Edit project → **Reviews** on a project).
110
+ 2. Set **Who reviews** to **External agent (MCP)**. **Reviewer** and **Reviewer model** are greyed: the agent needs neither.
111
+ 3. Pick **Flag only** or **Flag and redo** for each stage, as usual. Picture stages (images, the thumbnail, shorts) work too: the agent is shown the pictures.
112
+
113
+ Reviews by the agent cost nothing and add nothing to the estimate. Redos it asks for cost what the item cost, as always.
114
+
115
+ ### What happens during the run
116
+
117
+ 1. When an item is made, its review waits for the agent. What depends on it waits too; other work carries on.
118
+ 2. If nothing else holds the run, the project reads **Waiting for the agent** everywhere: its header, Projects, Home's **Running now** and the calendar. The project page says "An agent is reviewing what was made." with nothing to press. It is not in **Needs you**, no "Waiting for you" notice goes out and there is no **Continue the run** to press: it goes on by itself.
119
+ 3. The agent reads the item and answers **pass** or **fail**. A fail needs at least one reason; at most 10 reasons, each up to 500 characters. The answer goes through the stage's mode, exactly like a model's verdict: a pass lets the work go on, a fail is flagged or made again.
120
+ 4. **The first answer wins.** If two agents answer the same review, the first one saved is kept and the second is refused ("This review was already answered"). The same goes for you: once an agent has pressed Overrule or Redo on a flagged item, or approved a checkpoint, pressing it yourself changes nothing and says it was already decided.
121
+
122
+ ### If no agent answers
123
+
124
+ A review nobody answers **passes by itself 2 hours** after it began waiting. Its badge reads **Passed without review** ("No agent answered within 2 hours, so it went ahead without review."), the work goes on, and you get a notification: **Passed without review: no agent answered in 2 h**, naming the project and the item. The two hours are saved with the review, so a restart neither resets nor loses them.
125
+
126
+ - **Paused project:** the review still passes at its two hours, but nothing runs until the run is continued.
127
+ - **Canceled run:** its waiting reviews stop and never pass. If you (or an agent) take the step up again, the review asks again and waits a fresh two hours.
128
+ - **Item made again meanwhile** (an edit or a remake): the old review is dropped and the new item gets its own.
129
+
130
+ ## Paused and stopped runs
131
+
132
+ An agent may **continue any paused run**, whoever paused it, once nothing else on the project still needs fixing. It may also press **Continue the run** on a run a restart left **Stopped**. If you want a run to stay put while an agent routine is running, cancel it instead of pausing it: canceled runs are not on the agent's list.
133
+
134
+ ## needs_attention and the attend routine
135
+
136
+ Slopify cannot wake an agent; the agent looks. Its work list is the `needs_attention` tool: Home's **Needs you**, as an agent reads it, oldest first.
137
+
138
+ | Entry | What the agent does |
139
+ | --- | --- |
140
+ | An external review waiting for a verdict | Reads the item and answers. |
141
+ | A flagged verdict nobody acted on | **Overrule** or **Redo**. |
142
+ | A review checkpoint waiting for approval | Checks the work before it and approves. A checkpoint from the template's own **Before Video** is listed; a schedule's **Prepared** hold is not, since that one waits for its day. |
143
+ | A failed step | Does what the error's own sentence says: for example regenerates narration chunk 5, accepts the **Choose what to remake** estimate, then continues the run. |
144
+ | A paused run, or a run a restart **Stopped** | Continues it once nothing on the project still needs fixing. |
145
+ | Topics a schedule holds for approval | Approves the ones that fit and turns down the rest. |
146
+ | A video marked **Late · upload now** | Reports it. It cannot upload. |
147
+ | A finishing touch the Studio extension failed (captions, end screen, pinned comment) | Reports it. Fixing it is the extension's or yours. |
148
+
149
+ Projects that are **Kept as is** are not listed. Each entry says whether Slopify is already retrying or redoing it by itself (the agent leaves those alone and looks again later) and whether the agent can act on it at all.
150
+
151
+ The steps above are the **attend routine**. Slopify hands it to every agent that connects, and offers it as the MCP prompt **attend**: in Claude Code, `/mcp__slopify__attend` runs it. To have it run unattended, schedule it in your agent, for example as a Claude Code scheduled task or a Codex automation every hour. An hourly routine answers external reviews well inside their two hours.
152
+
153
+ ## Tips
154
+
155
+ - Pair the External agent reviewer with an hourly attend routine, and a [Notification URL](Notifications#get-notifications-on-your-phone-notification-url) so you hear when a review passed without an answer: that usually means the routine stopped running.
156
+ - Check **What agents did** in the Agents tab now and then; each project's **Project history** has the full story for that project.
157
+ - If an agent's request is refused because the project changed meanwhile, it reads the project again and retries once; you do not need to do anything.
158
+
159
+ ## Related pages
160
+
161
+ - [Reviews and Checkpoints](Reviews-and-Checkpoints)
162
+ - [Notifications](Notifications)
163
+ - [Recovery and Retries](Recovery-and-Retries)
164
+ - [Home and Projects](Home-and-Projects)
165
+ - [Schedules](Schedules)
166
+ - [Publishing to YouTube](Publishing-to-YouTube)
167
+ - [Settings Reference](Settings-Reference)
168
+ - [AI CLIs](AI-CLIs)
@@ -10,7 +10,7 @@ The calendar starts on this week's Monday and shows four weeks, Monday to Sunday
10
10
 
11
11
  - **Scheduled runs**, each with the project title it will make. A run shows **Queued** when a topic is lined up, **Needs a topic** when its topic is still waiting for your approval or for generation, and **Paused** when its schedule is paused.
12
12
  - **Needs you**, above the weeks: every project that is waiting on you or ready to upload (see below).
13
- - **Projects** running or finished on their day, with their state: **Running**, **Paused**, **Queued**, **Failed**, **Done with problems**, **Done** or **Canceled**. A project waiting for a CLI plan says so, for example **Waiting for Codex limits (resets at 14:00)**.
13
+ - **Projects** running or finished on their day, with their state: **Running**, **Waiting for the agent**, **Paused**, **Queued**, **Waiting for your review**, **Prepared**, **Stopped**, **Kept as is**, **Failed**, **Done with problems**, **Done** or **Canceled**. A project waiting for a CLI plan says so, for example **Waiting for Codex limits (resets at 14:00)**.
14
14
  - **Batch queue**: projects waiting to start (see below).
15
15
  - **Suggested topics**, beside the weeks, for schedules that hold generated topics for approval.
16
16
 
@@ -26,6 +26,7 @@ Above the weeks, **Needs you** lists what cannot move on without you, then what
26
26
  | --- | --- | --- |
27
27
  | A failed project | **Failed** | **Open to fix** |
28
28
  | A project you paused | **Paused** | **Open to continue** |
29
+ | A project a restart stopped before it finished | **Stopped** | **Open to continue** |
29
30
  | A project held at a review checkpoint, or with an automatic review that flagged an item and waits for **Overrule** or **Redo** | **Waiting for your review** | **Open to review** |
30
31
  | A finished video not marked uploaded | **Ready to upload** | **Prepare upload** |
31
32
 
@@ -140,6 +140,7 @@ Settings → **Providers** shows a short welcome on a fresh install that names t
140
140
  - Try a sample before you add any keys: every screen works on it, and nothing costs anything.
141
141
  - You can come back to the first-run screen at `/welcome` until you skip it or make a real project.
142
142
  - After an update to a new major version, a **What's new** tour opens instead. See [Updating and Patch Notes](Updating-and-Patch-Notes).
143
+ - Already use Claude Code or Codex? Settings → Connections → **Agents (MCP)** has one line that lets it run Slopify for you: start and fix runs, answer reviews, everything up to Ready to upload. See [Agents (MCP)](Agents-MCP).
143
144
 
144
145
  ## Related pages
145
146
 
@@ -53,19 +53,22 @@ One work list: the things that don't move on until you act, then the finished vi
53
53
 
54
54
  | Item | Status | Button |
55
55
  | --- | --- | --- |
56
- | A run held at a review before a step | **Waiting for you · review before the narration** (or the images, or the video) | **Approve the article and record**, **Approve and draw the images** or **Approve and render**. Approving here lets the run continue. Open the project to look first. |
57
- | A run whose next step is held another way | **Waiting for you** | **Open to continue** |
58
- | A run you paused | **Paused** | **Open to continue** |
56
+ | A run held at a review before a step | **Waiting for your review · before the narration** (or the images, or the video) | **Approve the article and record**, **Approve and draw the images** or **Approve and render**. Approving here lets the run continue. Open the project to look first. |
57
+ | A run whose next step is held another way | **Waiting for your review** | **Open to continue** |
58
+ | A run a restart stopped before it finished | **Stopped** | **Continue the run**, or **Keep as is** to leave the rest unmade |
59
+ | A run you (or an agent) paused | **Paused** | **Open to continue** |
59
60
  | Topics a schedule suggested | **N new topics** | **Review topics**, on the calendar: queue the ones you want and reject the rest. |
60
61
  | A failed run | **Failed · <step>**, with a short reason | The fix to try: a button to Settings → Providers or Backup & storage when that is the fix, otherwise **Open to retry**. |
61
62
  | A run that failed because a CLI is signed out | **Failed · <step>** | **Copy sign-in command** copies the command (`claude auth login`, `codex login` or `gemini`). Run it in a terminal on the computer running Slopify, then **Open to retry** and press **Check again** on the project. |
62
63
  | A finished video you have not marked uploaded | **Ready to upload** | **Prepare upload** lists everything YouTube Studio asks for, with Copy buttons (Slopify never uploads for you; see [Publishing to YouTube](Publishing-to-YouTube)). **Mark uploaded** takes it off the list, with **Undo** in the notice; Projects still has it. |
63
64
 
65
+ A held run also offers **Keep as is**, which takes it off Needs you and leaves the rest unmade (with **Undo**); editing the project brings it back. A run waiting for an [agent's review](Agents-MCP#the-external-agent-reviewer) is not here: it goes on by itself, so it shows under Running now. A step that failed while the run waits on something else shows here as a failed item.
66
+
64
67
  Up to 4 failed runs and 4 videos ready to upload show here; **See all N ready to upload** opens Projects filtered to them. The bundled samples never show as ready to upload. Only the first item's button is lime; the rest are plain buttons. The heading says how many decisions are waiting and how many videos are ready to upload. With nothing waiting and nothing running, Home says **Nothing needs you and nothing is running**. See [Reviews and Checkpoints](Reviews-and-Checkpoints), [Schedules](Schedules) and [Recovery and Retries](Recovery-and-Retries).
65
68
 
66
69
  ### Running now
67
70
 
68
- Videos being made or paused, up to 3. Each shows the step it is on, how long each step has taken, the running step's time left (for example `2 of 8 · about 4 min left`), the last few images as they land (with a tile for the one being drawn) and **Images N of M** while drawing. A run waiting for a CLI plan says so instead of the step, for example **Waiting for Codex limits (resets at 14:00)**. Open one to follow it live, pause it or cancel it. With nothing running it says **Start the next video**.
71
+ Videos being made, and runs **Waiting for the agent** (an External agent reviews what they made; they carry on once it answers or after 2 hours), up to 3. Paused runs are under Needs you. Each shows the step it is on, how long each step has taken, the running step's time left (for example `2 of 8 · about 4 min left`), the last few images as they land (with a tile for the one being drawn) and **Images N of M** while drawing. A run waiting for a CLI plan says so instead of the step, for example **Waiting for Codex limits (resets at 14:00)**. Open one to follow it live, pause it or cancel it. With nothing running it says **Start the next video**.
69
72
 
70
73
  - With more than three running, **See all N running** opens Projects filtered to running.
71
74
  - Queued runs show as one line under them, for example "Queued, waiting to start (2): Hypatia, Cleopatra", with **See queued** (Projects filtered to queued).
@@ -102,11 +105,11 @@ Every run ever started, newest first unless you pick another order, for the chan
102
105
  | Filter | Shows |
103
106
  | --- | --- |
104
107
  | **All** | Everything. |
105
- | **Running** | Running and paused projects. |
108
+ | **Running** | Projects running, and those **Waiting for the agent**: the same runs Home's **See all N running** counts. |
106
109
  | **Queued** | Projects waiting for their turn in the video queue. |
107
- | **Needs you** | Projects waiting at a review or held step. |
110
+ | **Needs you** | Projects waiting for you: at a review or held step, paused, or **Stopped**. |
108
111
  | **Ready to upload** | Finished videos not marked uploaded. |
109
- | **Failed** | Failed projects and those **Done with problems**. |
112
+ | **Failed** | Failed projects, those **Done with problems**, and runs with a failed step that still wait on something else. |
110
113
 
111
114
  3. Pick an order beside the filter: **Newest first**, **Recently changed**, **Name (A–Z)** or **Status** (waiting for you first, then running, queued, failed and finished).
112
115
 
@@ -128,9 +131,13 @@ If nothing matches, the list says **No project matches. Clear the search or pick
128
131
  | State | Meaning |
129
132
  | --- | --- |
130
133
  | **Running** | A step is being made. A run waiting for a CLI plan says **Waiting for Codex limits (resets at 14:00)** instead. |
131
- | **Paused** | Paused by you; nothing runs until you resume. |
134
+ | **Paused** | Paused by you or an agent; nothing runs until it is continued. |
132
135
  | **Queued** | Waiting for its turn in the video queue. |
133
- | **Waiting for you** | Held at a review or checkpoint. |
136
+ | **Waiting for your review** | Held at a review or checkpoint. |
137
+ | **Waiting for the agent** | An External agent is reviewing what it made; it goes on by itself. |
138
+ | **Prepared** | Made ahead by its schedule; only the video is left, for its day. |
139
+ | **Stopped** | Stopped before it finished, for example by a restart. Open it and press **Continue the run**. |
140
+ | **Kept as is** | What is left was set aside with **Keep as is**. |
134
141
  | **Done** | Finished. |
135
142
  | **Done with problems** | Finished, but part of it failed. |
136
143
  | **Failed** | Stopped with an error. |
@@ -87,6 +87,7 @@ These pages cover Slopify 3.0: every screen, every setting and every workflow, s
87
87
  | [Schedules](Schedules) | Videos on a cadence, topic lists and self-filling topics |
88
88
  | [Calendar](Calendar) | What needs you, what is coming up, the video queue |
89
89
  | [Notifications](Notifications) | Browser and phone (ntfy) notifications |
90
+ | [Agents (MCP)](Agents-MCP) | Letting Claude Code, Codex or another agent run Slopify, the External agent reviewer and the attend routine |
90
91
  | [Backups](Backups) | Automatic backups, export and import |
91
92
 
92
93
  ### Channels
@@ -8,13 +8,30 @@ Runs can take a while, so Slopify can tell you when one needs you: in the browse
8
8
 
9
9
  | Event | Message |
10
10
  |---|---|
11
- | A run **finishes** | "Video ready", or "Run finished" for a run that makes audio only |
12
- | A run **fails** | The project title and the first line of the reason |
13
- | A run **stops to wait for you** | A review checkpoint is holding the next step, or the work left is waiting for Resume |
11
+ | A run **finishes** | "Video ready: *title*", or "Run finished: *title*" for a run that makes audio only |
12
+ | A run **finishes with problems** | "Video ready with problems: *title* — *reason*": the video was made but another step failed |
13
+ | A run **fails** | "Run failed: *title* — *reason*", with the first line of the reason |
14
+ | A step **fails while the run waits on something else** | "Step failed: *title* — *reason*". Only when another step is waiting: an agent's review, your checkpoint, or a prepared run's day. See [Step failed](#step-failed). |
15
+ | A run **stops to wait for you** | "Waiting for you: *title*": a review checkpoint is holding the next step, or the work left is waiting for **Continue the run** |
16
+ | An agent's review **passed without an answer** | "Passed without review: no agent answered in 2 h", naming the project and the item. See [Agents (MCP)](Agents-MCP#if-no-agent-answers). |
14
17
  | An automatic review **needs a decision** | "Review needs a decision: <title>", when an automatic review flags an item and keeps it (its redos ran out, or the stage only flags). It says what was flagged and to press **Overrule** or **Redo** on the project. Sent once per verdict. |
15
18
  | A schedule has **new topics waiting** for approval | For example "5 new topics are waiting for you" |
16
19
 
17
- Pausing or cancelling a run yourself sends nothing, and neither does a project that was already finished when the page loaded. A message holds the project title and what happened, never your keys, prompts or files.
20
+ Pausing or cancelling a run yourself sends nothing, and neither does a project that was already finished when the page loaded. A run waiting for an [agent's review](Agents-MCP#the-external-agent-reviewer), or a prepared run waiting for its day, sends no "Waiting for you": nobody needs to act. A message holds the project title and what happened, never your keys, prompts or files.
21
+
22
+ A run an [agent](Agents-MCP) started ends its messages with **· started by agent**, and a flag from an agent's review ends with **· by agent**.
23
+
24
+ ### Step failed
25
+
26
+ An ordinary run tells you once, at its end: "Run failed" or "Video ready with problems", with the end chime. A step that fails on the way is named there, not in a message of its own.
27
+
28
+ "Step failed" is for the other case: a step fails while another step of the run is waiting (for an agent's review, your checkpoint, or a prepared run's day). The run is not over and may wait for hours, so Slopify tells you right away instead of at the end. The project still reads by what it waits for, and the failed step shows on its page with its fix.
29
+
30
+ Each failure is told once. A restart neither repeats a message already sent nor loses one: what was told is saved. A step that fails again after **Try again** is a new failure and is told again. If the run later ends **Failed** on failures already told, no second "Run failed" goes out.
31
+
32
+ ### While you were away
33
+
34
+ When you come back to a Slopify tab after more than two minutes away (or open one after closing it), one notice sums up what happened meanwhile, for example "While you were away: 2 runs finished, 1 step failed beside a wait, 1 run waits for you.", with **Open Home**. It counts runs that finished, runs that failed, steps that failed beside a wait, and runs that started waiting for you. Runs waiting for an agent or for their day are not counted as waiting for you. It works from this browser's own memory of what it last saw, with no setting.
18
35
 
19
36
  ## Turn on browser notifications
20
37
 
@@ -44,7 +61,7 @@ Some browsers only allow notifications from installed apps. If the test says so,
44
61
 
45
62
  ## Run sounds
46
63
 
47
- **Settings → Notifications → Run sounds** (on by default) plays a chime when a run starts and a different, longer one when it finishes, fails or stops to wait for you, like a message arriving on a phone. **Play start** and **Play end** let you hear them.
64
+ **Settings → Notifications → Run sounds** (on by default) plays a chime when a run starts and a different, longer one when it finishes, fails or stops to wait for you, like a message arriving on a phone. A "Step failed" plays the end chime too. **Play start** and **Play end** let you hear them.
48
65
 
49
66
  - Works while any Slopify tab is open; only one tab plays each chime.
50
67
  - Needs no permission, but browsers keep a page silent until it has been clicked once, so click anywhere in Slopify after opening it.
@@ -53,7 +70,7 @@ Some browsers only allow notifications from installed apps. If the test says so,
53
70
 
54
71
  ## Get notifications on your phone (Notification URL)
55
72
 
56
- A Notification URL works with no Slopify tab open. When a run finishes, fails or waits, or a review needs a decision, Slopify sends that address one `POST` with a short plain-text body. Its last line is the project's link, so you can open it straight from the message, for example:
73
+ A Notification URL works with no Slopify tab open. When a run finishes, fails or waits, a step fails beside a wait, a review needs a decision or passes without an agent's answer, Slopify sends that address one `POST` with a short plain-text body. Its last line is the project's link, so you can open it straight from the message, for example:
57
74
 
58
75
  ```
59
76
  Video ready: Black holes explained
@@ -79,6 +96,7 @@ To turn it off, clear the field and press **Save**.
79
96
  - Any address that accepts a `POST` works, not just ntfy.
80
97
  - Each message is sent once, with a 5-second timeout, and is never retried. A failure is written to the log and does not affect the run.
81
98
  - The address stays on this machine: backups leave it out.
99
+ - [Agents](Agents-MCP) never see the address, only whether one is configured. They may set a new one.
82
100
  - In [Docker](Docker) the request goes out from the container, so the address must be reachable from there. `localhost` means the container itself.
83
101
 
84
102
  ## Tips
@@ -92,4 +110,5 @@ To turn it off, clear the field and press **Save**.
92
110
  - [Schedules](Schedules)
93
111
  - [Calendar](Calendar)
94
112
  - [Reviews and checkpoints](Reviews-and-Checkpoints)
113
+ - [Agents (MCP)](Agents-MCP)
95
114
  - [Troubleshooting](Troubleshooting)