@rubytech/create-maxy-code 0.1.557 → 0.1.559

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 (262) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +2 -1
  3. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts +1 -1
  4. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts.map +1 -1
  5. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js +7 -0
  6. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js.map +1 -1
  7. package/payload/platform/plugins/admin/mcp/dist/index.js +20 -0
  8. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  9. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts +14 -0
  10. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts.map +1 -0
  11. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js +18 -0
  12. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js.map +1 -0
  13. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +43 -1
  14. package/payload/platform/plugins/admin/skills/video-production/SKILL.md +106 -0
  15. package/payload/platform/plugins/admin/skills/video-production/__tests__/registration.sh +54 -0
  16. package/payload/platform/plugins/admin/skills/video-production/references/reel-authoring.md +83 -0
  17. package/payload/platform/plugins/admin/skills/video-production/render.mjs +429 -0
  18. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +17 -0
  19. package/payload/platform/plugins/docs/PLUGIN.md +1 -0
  20. package/payload/platform/plugins/docs/references/video-production.md +37 -0
  21. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.d.ts +5 -1
  22. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.d.ts.map +1 -1
  23. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.js +4 -1
  24. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.js.map +1 -1
  25. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.d.ts.map +1 -1
  26. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.js +2 -1
  27. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.js.map +1 -1
  28. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts.map +1 -1
  29. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js +2 -1
  30. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js.map +1 -1
  31. package/payload/platform/plugins/scheduling/PLUGIN.md +4 -2
  32. package/payload/platform/plugins/scheduling/mcp/dist/index.js +15 -3
  33. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  34. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js +72 -0
  35. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js.map +1 -1
  36. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/workflow-census.test.d.ts +2 -0
  37. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/workflow-census.test.d.ts.map +1 -0
  38. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/workflow-census.test.js +112 -0
  39. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/workflow-census.test.js.map +1 -0
  40. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +59 -6
  41. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
  42. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +154 -18
  43. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
  44. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +17 -2
  45. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  46. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list-kind.test.d.ts +2 -0
  47. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list-kind.test.d.ts.map +1 -0
  48. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list-kind.test.js +25 -0
  49. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list-kind.test.js.map +1 -0
  50. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts +15 -0
  51. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -1
  52. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +66 -46
  53. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -1
  54. package/payload/platform/plugins/storage-broker/mcp/dist/index.js +5 -2
  55. package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -1
  56. package/payload/platform/plugins/url-get/mcp/dist/index.js +13 -13
  57. package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
  58. package/payload/platform/plugins/workflows/PLUGIN.md +4 -1
  59. package/payload/platform/plugins/workflows/mcp/dist/__tests__/retry-delay.test.d.ts +2 -0
  60. package/payload/platform/plugins/workflows/mcp/dist/__tests__/retry-delay.test.d.ts.map +1 -0
  61. package/payload/platform/plugins/workflows/mcp/dist/__tests__/retry-delay.test.js +33 -0
  62. package/payload/platform/plugins/workflows/mcp/dist/__tests__/retry-delay.test.js.map +1 -0
  63. package/payload/platform/plugins/workflows/mcp/dist/index.js +1 -0
  64. package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
  65. package/payload/platform/plugins/workflows/mcp/dist/lib/__tests__/step-resolver.test.d.ts +2 -0
  66. package/payload/platform/plugins/workflows/mcp/dist/lib/__tests__/step-resolver.test.d.ts.map +1 -0
  67. package/payload/platform/plugins/workflows/mcp/dist/lib/__tests__/step-resolver.test.js +42 -0
  68. package/payload/platform/plugins/workflows/mcp/dist/lib/__tests__/step-resolver.test.js.map +1 -0
  69. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts +23 -5
  70. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.d.ts.map +1 -1
  71. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.js +20 -12
  72. package/payload/platform/plugins/workflows/mcp/dist/lib/step-resolver.js.map +1 -1
  73. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts +2 -0
  74. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.d.ts.map +1 -1
  75. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js +2 -0
  76. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-create.js.map +1 -1
  77. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts +10 -0
  78. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts.map +1 -1
  79. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +53 -7
  80. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
  81. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts.map +1 -1
  82. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js +2 -0
  83. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js.map +1 -1
  84. package/payload/platform/scripts/__tests__/provision-honours-disabled-agents.test.sh +4 -3
  85. package/payload/platform/scripts/__tests__/provision-specialist-retirement.test.sh +115 -0
  86. package/payload/platform/scripts/lib/provision-account-dir.sh +88 -13
  87. package/payload/platform/scripts/logs-rotate.sh +6 -1
  88. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  89. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +48 -0
  90. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/pty-tee.d.ts +16 -0
  92. package/payload/platform/services/claude-session-manager/dist/pty-tee.d.ts.map +1 -0
  93. package/payload/platform/services/claude-session-manager/dist/pty-tee.js +84 -0
  94. package/payload/platform/services/claude-session-manager/dist/pty-tee.js.map +1 -0
  95. package/payload/platform/templates/specialists/agents/content-producer.md +8 -0
  96. package/payload/premium-plugins/writer-craft/agents/writer-craft--manuscript-reviewer.md +2 -0
  97. package/payload/server/public/activity.html +5 -5
  98. package/payload/server/public/agents.html +4 -4
  99. package/payload/server/public/assets/{AdminLoginScreens-DrF7rAcb.js → AdminLoginScreens-DLogIsJT.js} +1 -1
  100. package/payload/server/public/assets/AdminLoginScreens-DLogIsJT.js.br +0 -0
  101. package/payload/server/public/assets/AdminLoginScreens-DLogIsJT.js.gz +0 -0
  102. package/payload/server/public/assets/{AdminShell-BVKfxSgh.js → AdminShell-B67tPTyE.js} +1 -1
  103. package/payload/server/public/assets/AdminShell-B67tPTyE.js.br +0 -0
  104. package/payload/server/public/assets/AdminShell-B67tPTyE.js.gz +0 -0
  105. package/payload/server/public/assets/{activity-CBcvL5MZ.js → activity-AVJSFESV.js} +1 -1
  106. package/payload/server/public/assets/activity-AVJSFESV.js.br +0 -0
  107. package/payload/server/public/assets/activity-AVJSFESV.js.gz +0 -0
  108. package/payload/server/public/assets/{admin-CCB9k3GB.js → admin-eFgbld42.js} +1 -1
  109. package/payload/server/public/assets/admin-eFgbld42.js.br +0 -0
  110. package/payload/server/public/assets/admin-eFgbld42.js.gz +0 -0
  111. package/payload/server/public/assets/{agents-Dzx8Uxgz.js → agents-CoWLZMf7.js} +1 -1
  112. package/payload/server/public/assets/agents-CoWLZMf7.js.br +0 -0
  113. package/payload/server/public/assets/agents-CoWLZMf7.js.gz +0 -0
  114. package/payload/server/public/assets/{browser-B7EsGyVd.js → browser-C6K66_fw.js} +1 -1
  115. package/payload/server/public/assets/browser-C6K66_fw.js.br +0 -0
  116. package/payload/server/public/assets/browser-C6K66_fw.js.gz +0 -0
  117. package/payload/server/public/assets/{calendar-D4qD0upn.js → calendar-Dnw_cP4b.js} +1 -1
  118. package/payload/server/public/assets/calendar-Dnw_cP4b.js.br +0 -0
  119. package/payload/server/public/assets/calendar-Dnw_cP4b.js.gz +0 -0
  120. package/payload/server/public/assets/chat-Cm0fkpg1.js +1 -0
  121. package/payload/server/public/assets/chat-Cm0fkpg1.js.br +0 -0
  122. package/payload/server/public/assets/chat-Cm0fkpg1.js.gz +0 -0
  123. package/payload/server/public/assets/chevron-left-CjRH-BtF.js +1 -0
  124. package/payload/server/public/assets/chevron-left-CjRH-BtF.js.br +0 -0
  125. package/payload/server/public/assets/clock-wbYEt1kM.js +1 -0
  126. package/payload/server/public/assets/clock-wbYEt1kM.js.br +0 -0
  127. package/payload/server/public/assets/clock-wbYEt1kM.js.gz +0 -0
  128. package/payload/server/public/assets/{copy-CG2tAK16.js → copy-kuNM0Etw.js} +1 -1
  129. package/payload/server/public/assets/copy-kuNM0Etw.js.br +0 -0
  130. package/payload/server/public/assets/copy-kuNM0Etw.js.gz +0 -0
  131. package/payload/server/public/assets/data-c47T2NCd.js +1 -0
  132. package/payload/server/public/assets/data-c47T2NCd.js.br +1 -0
  133. package/payload/server/public/assets/data-c47T2NCd.js.gz +0 -0
  134. package/payload/server/public/assets/{file-text-47cIljhV.js → file-text-BWkobrcx.js} +1 -1
  135. package/payload/server/public/assets/file-text-BWkobrcx.js.br +0 -0
  136. package/payload/server/public/assets/file-text-BWkobrcx.js.gz +0 -0
  137. package/payload/server/public/assets/{graph-CDbi-eqF.js → graph-BGjTL5Vz.js} +1 -1
  138. package/payload/server/public/assets/graph-BGjTL5Vz.js.br +0 -0
  139. package/payload/server/public/assets/graph-BGjTL5Vz.js.gz +0 -0
  140. package/payload/server/public/assets/{graph-labels-Byp2RkVg.js → graph-labels-Dvc5Kw_r.js} +1 -1
  141. package/payload/server/public/assets/graph-labels-Dvc5Kw_r.js.br +0 -0
  142. package/payload/server/public/assets/graph-labels-Dvc5Kw_r.js.gz +0 -0
  143. package/payload/server/public/assets/{operator-B0oOVB8R.js → operator-CzBGv_he.js} +1 -1
  144. package/payload/server/public/assets/operator-CzBGv_he.js.br +0 -0
  145. package/payload/server/public/assets/operator-CzBGv_he.js.gz +0 -0
  146. package/payload/server/public/assets/{page-Dfce3YQd.js → page-BIY4Ku8Q.js} +1 -1
  147. package/payload/server/public/assets/page-BIY4Ku8Q.js.br +0 -0
  148. package/payload/server/public/assets/page-BIY4Ku8Q.js.gz +0 -0
  149. package/payload/server/public/assets/{page-DrbfHSQ9.js → page-DaySqamH.js} +1 -1
  150. package/payload/server/public/assets/page-DaySqamH.js.br +0 -0
  151. package/payload/server/public/assets/page-DaySqamH.js.gz +0 -0
  152. package/payload/server/public/assets/play-C8RA52_i.js +1 -0
  153. package/payload/server/public/assets/play-C8RA52_i.js.br +0 -0
  154. package/payload/server/public/assets/play-C8RA52_i.js.gz +0 -0
  155. package/payload/server/public/assets/{public-DJwMfl-4.js → public-pwDSvhBv.js} +1 -1
  156. package/payload/server/public/assets/public-pwDSvhBv.js.br +0 -0
  157. package/payload/server/public/assets/public-pwDSvhBv.js.gz +0 -0
  158. package/payload/server/public/assets/{rotate-ccw-Dbj7NrEB.js → rotate-ccw-B6s7tkEt.js} +1 -1
  159. package/payload/server/public/assets/rotate-ccw-B6s7tkEt.js.br +0 -0
  160. package/payload/server/public/assets/rotate-ccw-B6s7tkEt.js.gz +0 -0
  161. package/payload/server/public/assets/routines-CFlyRdYg.js +2 -0
  162. package/payload/server/public/assets/routines-CFlyRdYg.js.br +0 -0
  163. package/payload/server/public/assets/routines-CFlyRdYg.js.gz +0 -0
  164. package/payload/server/public/assets/{skills-EtKyNK_3.js → skills-C45kfL_Q.js} +1 -1
  165. package/payload/server/public/assets/skills-C45kfL_Q.js.br +0 -0
  166. package/payload/server/public/assets/skills-C45kfL_Q.js.gz +0 -0
  167. package/payload/server/public/assets/{tasks-U0poqfi9.js → tasks-BwoCTCu5.js} +1 -1
  168. package/payload/server/public/assets/tasks-BwoCTCu5.js.br +0 -0
  169. package/payload/server/public/assets/tasks-BwoCTCu5.js.gz +0 -0
  170. package/payload/server/public/assets/{triangle-alert-D7zwttJW.js → triangle-alert-B3bthSzx.js} +1 -1
  171. package/payload/server/public/assets/triangle-alert-B3bthSzx.js.br +0 -0
  172. package/payload/server/public/assets/triangle-alert-B3bthSzx.js.gz +0 -0
  173. package/payload/server/public/assets/{useCopyFeedback-Ae_va0Ps.js → useCopyFeedback-BzJAkFEb.js} +1 -1
  174. package/payload/server/public/assets/useCopyFeedback-BzJAkFEb.js.br +0 -0
  175. package/payload/server/public/assets/useCopyFeedback-BzJAkFEb.js.gz +0 -0
  176. package/payload/server/public/assets/{useMediaQuery-HRgPNA8l.css → useMediaQuery-DW_xRrMl.css} +1 -1
  177. package/payload/server/public/assets/useMediaQuery-DW_xRrMl.css.br +0 -0
  178. package/payload/server/public/assets/{useMediaQuery-HRgPNA8l.css.gz → useMediaQuery-DW_xRrMl.css.gz} +0 -0
  179. package/payload/server/public/assets/useVoiceRecorder-_qI_BAOo.js +2 -0
  180. package/payload/server/public/assets/useVoiceRecorder-_qI_BAOo.js.br +0 -0
  181. package/payload/server/public/assets/useVoiceRecorder-_qI_BAOo.js.gz +0 -0
  182. package/payload/server/public/assets/{wrench-BmXV4Tmh.js → wrench-Bqux9jaw.js} +1 -1
  183. package/payload/server/public/assets/wrench-Bqux9jaw.js.br +0 -0
  184. package/payload/server/public/assets/wrench-Bqux9jaw.js.gz +0 -0
  185. package/payload/server/public/browser.html +4 -4
  186. package/payload/server/public/calendar.html +7 -7
  187. package/payload/server/public/chat.html +13 -13
  188. package/payload/server/public/data.html +11 -11
  189. package/payload/server/public/graph.html +9 -9
  190. package/payload/server/public/index.html +13 -13
  191. package/payload/server/public/operator.html +14 -14
  192. package/payload/server/public/public.html +13 -13
  193. package/payload/server/public/routines.html +7 -7
  194. package/payload/server/public/skills.html +5 -5
  195. package/payload/server/public/tasks.html +6 -6
  196. package/payload/server/server.js +262 -85
  197. package/payload/server/public/assets/AdminLoginScreens-DrF7rAcb.js.br +0 -0
  198. package/payload/server/public/assets/AdminLoginScreens-DrF7rAcb.js.gz +0 -0
  199. package/payload/server/public/assets/AdminShell-BVKfxSgh.js.br +0 -0
  200. package/payload/server/public/assets/AdminShell-BVKfxSgh.js.gz +0 -0
  201. package/payload/server/public/assets/activity-CBcvL5MZ.js.br +0 -0
  202. package/payload/server/public/assets/activity-CBcvL5MZ.js.gz +0 -0
  203. package/payload/server/public/assets/admin-CCB9k3GB.js.br +0 -0
  204. package/payload/server/public/assets/admin-CCB9k3GB.js.gz +0 -0
  205. package/payload/server/public/assets/agents-Dzx8Uxgz.js.br +0 -0
  206. package/payload/server/public/assets/agents-Dzx8Uxgz.js.gz +0 -0
  207. package/payload/server/public/assets/browser-B7EsGyVd.js.br +0 -0
  208. package/payload/server/public/assets/browser-B7EsGyVd.js.gz +0 -0
  209. package/payload/server/public/assets/calendar-D4qD0upn.js.br +0 -0
  210. package/payload/server/public/assets/calendar-D4qD0upn.js.gz +0 -0
  211. package/payload/server/public/assets/chat-BgOUm7r1.js +0 -1
  212. package/payload/server/public/assets/chat-BgOUm7r1.js.br +0 -0
  213. package/payload/server/public/assets/chat-BgOUm7r1.js.gz +0 -0
  214. package/payload/server/public/assets/chevron-left-CGsW9Vsp.js +0 -1
  215. package/payload/server/public/assets/chevron-left-CGsW9Vsp.js.br +0 -0
  216. package/payload/server/public/assets/clock-B_xWWLQW.js +0 -1
  217. package/payload/server/public/assets/clock-B_xWWLQW.js.br +0 -0
  218. package/payload/server/public/assets/clock-B_xWWLQW.js.gz +0 -0
  219. package/payload/server/public/assets/copy-CG2tAK16.js.br +0 -0
  220. package/payload/server/public/assets/copy-CG2tAK16.js.gz +0 -0
  221. package/payload/server/public/assets/data-YEC6LkxI.js +0 -1
  222. package/payload/server/public/assets/data-YEC6LkxI.js.br +0 -0
  223. package/payload/server/public/assets/data-YEC6LkxI.js.gz +0 -0
  224. package/payload/server/public/assets/file-text-47cIljhV.js.br +0 -0
  225. package/payload/server/public/assets/file-text-47cIljhV.js.gz +0 -0
  226. package/payload/server/public/assets/graph-CDbi-eqF.js.br +0 -0
  227. package/payload/server/public/assets/graph-CDbi-eqF.js.gz +0 -0
  228. package/payload/server/public/assets/graph-labels-Byp2RkVg.js.br +0 -0
  229. package/payload/server/public/assets/graph-labels-Byp2RkVg.js.gz +0 -0
  230. package/payload/server/public/assets/operator-B0oOVB8R.js.br +0 -0
  231. package/payload/server/public/assets/operator-B0oOVB8R.js.gz +0 -0
  232. package/payload/server/public/assets/page-Dfce3YQd.js.br +0 -0
  233. package/payload/server/public/assets/page-Dfce3YQd.js.gz +0 -0
  234. package/payload/server/public/assets/page-DrbfHSQ9.js.br +0 -0
  235. package/payload/server/public/assets/page-DrbfHSQ9.js.gz +0 -0
  236. package/payload/server/public/assets/play-Bankj0zU.js +0 -1
  237. package/payload/server/public/assets/play-Bankj0zU.js.br +0 -0
  238. package/payload/server/public/assets/play-Bankj0zU.js.gz +0 -0
  239. package/payload/server/public/assets/public-DJwMfl-4.js.br +0 -0
  240. package/payload/server/public/assets/public-DJwMfl-4.js.gz +0 -0
  241. package/payload/server/public/assets/rotate-ccw-Dbj7NrEB.js.br +0 -0
  242. package/payload/server/public/assets/rotate-ccw-Dbj7NrEB.js.gz +0 -0
  243. package/payload/server/public/assets/routines-CdqGJwyD.js +0 -2
  244. package/payload/server/public/assets/routines-CdqGJwyD.js.br +0 -0
  245. package/payload/server/public/assets/routines-CdqGJwyD.js.gz +0 -0
  246. package/payload/server/public/assets/skills-EtKyNK_3.js.br +0 -0
  247. package/payload/server/public/assets/skills-EtKyNK_3.js.gz +0 -0
  248. package/payload/server/public/assets/tasks-U0poqfi9.js.br +0 -0
  249. package/payload/server/public/assets/tasks-U0poqfi9.js.gz +0 -0
  250. package/payload/server/public/assets/triangle-alert-D7zwttJW.js.br +0 -0
  251. package/payload/server/public/assets/triangle-alert-D7zwttJW.js.gz +0 -0
  252. package/payload/server/public/assets/useCopyFeedback-Ae_va0Ps.js.br +0 -4
  253. package/payload/server/public/assets/useCopyFeedback-Ae_va0Ps.js.gz +0 -0
  254. package/payload/server/public/assets/useMediaQuery-HRgPNA8l.css.br +0 -0
  255. package/payload/server/public/assets/useVoiceRecorder-9qVOHhkb.js +0 -2
  256. package/payload/server/public/assets/useVoiceRecorder-9qVOHhkb.js.br +0 -0
  257. package/payload/server/public/assets/useVoiceRecorder-9qVOHhkb.js.gz +0 -0
  258. package/payload/server/public/assets/wrench-BmXV4Tmh.js.br +0 -0
  259. package/payload/server/public/assets/wrench-BmXV4Tmh.js.gz +0 -0
  260. /package/payload/server/public/assets/{useMediaQuery-DIm6yGOl.js → useMediaQuery-DJ6XeNN7.js} +0 -0
  261. /package/payload/server/public/assets/{useMediaQuery-DIm6yGOl.js.br → useMediaQuery-DJ6XeNN7.js.br} +0 -0
  262. /package/payload/server/public/assets/{useMediaQuery-DIm6yGOl.js.gz → useMediaQuery-DJ6XeNN7.js.gz} +0 -0
@@ -0,0 +1,429 @@
1
+ #!/usr/bin/env node
2
+ // Frame-accurate MP4 capture over the Chrome DevTools Protocol. No npm packages —
3
+ // Node 22's built-in fetch and WebSocket are the whole transport.
4
+ //
5
+ // node render.mjs --mode reel --target <path|file:|http:> --out <dir> --name <slug> \
6
+ // --width 1080 --height 1920 [--dsf 1] [--mobile] [--pre '<js>'] [--port 9333]
7
+ //
8
+ // node render.mjs --mode page --target <path|file:|http:> --out <dir> --name <slug> \
9
+ // --width 1920 --height 1080 --frames 900 [--dsf 1] [--mobile] \
10
+ // [--pre '<js>'] [--port 9333]
11
+ //
12
+ // The two modes differ in one thing: how the clock advances.
13
+ // reel — the page defines window.__frames and window.__seek(i); every frame is
14
+ // computed from the index alone, so the timeline is deterministic.
15
+ // page — Chrome's virtual time clock is advanced one frame at a time, so a page
16
+ // we do not control is captured with its own animation playing.
17
+ //
18
+ // This script owns the whole run: resolve the browser, launch it, capture, encode,
19
+ // poster, probe, stills, tear down, census. Splitting encode or cleanup out leaves
20
+ // the frame directory with no owner, which is how /tmp fills up with frames.
21
+
22
+ import {
23
+ writeFileSync, mkdirSync, rmSync, existsSync, readFileSync,
24
+ appendFileSync, statSync, readdirSync,
25
+ } from "node:fs";
26
+ import { spawn, execFileSync } from "node:child_process";
27
+ import { join } from "node:path";
28
+ import { tmpdir } from "node:os";
29
+ import { fileURLToPath, pathToFileURL } from "node:url";
30
+
31
+ // ---------------------------------------------------------------- arguments
32
+
33
+ const argv = process.argv.slice(2);
34
+ const arg = (flag, fallback = null) => {
35
+ const i = argv.indexOf(flag);
36
+ return i >= 0 && i + 1 < argv.length ? argv[i + 1] : fallback;
37
+ };
38
+ const flag = f => argv.includes(f);
39
+
40
+ const mode = arg("--mode");
41
+ const target = arg("--target");
42
+ const outDir = arg("--out");
43
+ const name = arg("--name");
44
+ const width = Number(arg("--width", 1080));
45
+ const height = Number(arg("--height", 1920));
46
+ const dsf = Number(arg("--dsf", 1));
47
+ const mobile = flag("--mobile");
48
+ const pre = arg("--pre");
49
+ const port = arg("--port", "9333");
50
+ const framesArg = arg("--frames");
51
+
52
+ const FRAME_MS = 1000 / 30;
53
+ const FRAMES_ROOT = join(tmpdir(), "video-production");
54
+ const T0 = Date.now();
55
+
56
+ // ---------------------------------------------------------------- logging
57
+ // Same fail-open shape as the platform hooks: always stdout, plus a durable
58
+ // breadcrumb in $LOG_DIR when that is a real directory. An unwritable log
59
+ // never fails a render.
60
+
61
+ const LOG_FILE =
62
+ process.env.LOG_DIR && existsSync(process.env.LOG_DIR)
63
+ ? join(process.env.LOG_DIR, "video-render.log")
64
+ : null;
65
+
66
+ function log(line) {
67
+ const s = `${new Date().toISOString()} ${line}`;
68
+ console.log(s);
69
+ if (LOG_FILE) { try { appendFileSync(LOG_FILE, s + "\n"); } catch { /* fail open */ } }
70
+ }
71
+
72
+ function die(line) { log(line); process.exit(1); }
73
+
74
+ const run = name || "unnamed";
75
+
76
+ if (!mode || !["reel", "page"].includes(mode)) die(`[video-render] op=bad-args run=${run} reason=mode-must-be-reel-or-page`);
77
+ if (!target) die(`[video-render] op=bad-args run=${run} reason=target-required`);
78
+ if (!outDir) die(`[video-render] op=bad-args run=${run} reason=out-required`);
79
+ if (!name) die(`[video-render] op=bad-args run=${run} reason=name-required`);
80
+ // --name is joined into four filesystem paths and both output filenames, so it is
81
+ // a path component, not a label. A slash or a dot-dot would escape them.
82
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
83
+ die(`[video-render] op=bad-args run=${run} reason=name-must-be-a-slug value=${JSON.stringify(name)}`);
84
+ }
85
+ if (mode === "page" && !framesArg) die(`[video-render] op=bad-args run=${run} reason=frames-required-in-page-mode`);
86
+
87
+ const url =
88
+ /^(https?|file):/.test(target) ? target : pathToFileURL(target).href;
89
+
90
+ log(`[video-render] op=request run=${run} mode=${mode} target=${url} w=${width} h=${height} dsf=${dsf} fps=30`);
91
+
92
+ // ---------------------------------------------------------------- census
93
+
94
+ // frameDirs/frameBytes are the strict leak signal: a capture directory outlives
95
+ // its run only when a render died without tearing down, so a healthy end reports
96
+ // zero for both. stillDirs/stillBytes are counted separately because verification
97
+ // stills are deliberately retained for the agent to read after the run — counting
98
+ // them as frames would make every successful render look like a leak.
99
+ function census(phase) {
100
+ let chromiumLive = 0, frameDirs = 0, frameBytes = 0, stillDirs = 0, stillBytes = 0;
101
+ try {
102
+ // A read, never a kill. This process's own argv carries no such string,
103
+ // so it cannot count itself.
104
+ chromiumLive = Number(execFileSync("bash", ["-c",
105
+ 'pgrep -f "remote-debugging-port=" 2>/dev/null | wc -l']).toString().trim());
106
+ } catch { /* pgrep absent or no matches */ }
107
+ try {
108
+ for (const d of readdirSync(FRAMES_ROOT)) {
109
+ const p = join(FRAMES_ROOT, d);
110
+ if (!statSync(p).isDirectory()) continue;
111
+ const isStills = d.endsWith("-stills");
112
+ let bytes = 0;
113
+ for (const f of readdirSync(p)) {
114
+ try { bytes += statSync(join(p, f)).size; } catch { /* raced */ }
115
+ }
116
+ if (isStills) { stillDirs++; stillBytes += bytes; }
117
+ else { frameDirs++; frameBytes += bytes; }
118
+ }
119
+ } catch { /* root absent on a first run */ }
120
+ log(`[video-census] phase=${phase} run=${run} chromiumLive=${chromiumLive} frameDirs=${frameDirs} frameBytes=${frameBytes} stillDirs=${stillDirs} stillBytes=${stillBytes}`);
121
+ }
122
+
123
+ census("start");
124
+
125
+ // ---------------------------------------------------------------- preflight
126
+
127
+ const platformRoot =
128
+ process.env.PLATFORM_ROOT || fileURLToPath(new URL("../../../../", import.meta.url));
129
+ const binFile = join(platformRoot, "config", "chromium-binary.path");
130
+
131
+ if (!existsSync(binFile)) {
132
+ die(`[video-render] op=chromium-missing run=${run} file=${binFile}`);
133
+ }
134
+ const bin = readFileSync(binFile, "utf8").trim();
135
+ if (!bin || !existsSync(bin)) {
136
+ die(`[video-render] op=chromium-missing run=${run} file=${binFile} bin=${bin || "(empty)"}`);
137
+ }
138
+ log(`[video-render] op=chromium-resolve run=${run} bin=${bin} source=config`);
139
+
140
+ for (const tool of ["ffmpeg", "ffprobe"]) {
141
+ try { execFileSync("which", [tool], { stdio: "ignore" }); }
142
+ catch { die(`[video-render] op=tool-missing run=${run} tool=${tool}`); }
143
+ }
144
+
145
+ const frameDir = join(FRAMES_ROOT, run);
146
+ const stillDir = join(FRAMES_ROOT, `${run}-stills`);
147
+ const profile = join(tmpdir(), `video-production-profile-${run}`);
148
+ rmSync(frameDir, { recursive: true, force: true });
149
+ rmSync(stillDir, { recursive: true, force: true });
150
+ rmSync(profile, { recursive: true, force: true });
151
+ mkdirSync(frameDir, { recursive: true });
152
+ mkdirSync(stillDir, { recursive: true });
153
+ mkdirSync(outDir, { recursive: true });
154
+
155
+ // ---------------------------------------------------------------- launch
156
+ // detached:true makes the child a process-group leader, which is what lets
157
+ // teardown kill the whole browser tree by group rather than leaving zygotes.
158
+ // Never pkill -f on the port: that pattern also matches the shell that ran it.
159
+
160
+ const child = spawn(bin, [
161
+ "--headless=new",
162
+ `--remote-debugging-port=${port}`,
163
+ "--remote-debugging-address=127.0.0.1",
164
+ `--user-data-dir=${profile}`,
165
+ "--no-sandbox",
166
+ "--disable-gpu",
167
+ "--disable-dev-shm-usage",
168
+ "--hide-scrollbars",
169
+ "--force-device-scale-factor=1",
170
+ "--no-first-run",
171
+ "--no-default-browser-check",
172
+ "--disable-background-networking",
173
+ "--disable-features=TranslateUI",
174
+ "--font-render-hinting=none",
175
+ `--window-size=${width},${height}`,
176
+ "about:blank",
177
+ ], { detached: true, stdio: "ignore" });
178
+ child.unref();
179
+ const chromePid = child.pid;
180
+ log(`[video-render] op=chromium-start run=${run} pid=${chromePid} port=${port}`);
181
+
182
+ // ---------------------------------------------------------------- CDP
183
+
184
+ async function browserSocket(ms = 20000) {
185
+ const start = Date.now();
186
+ while (Date.now() - start < ms) {
187
+ try {
188
+ const r = await fetch(`http://127.0.0.1:${port}/json/version`);
189
+ if (r.ok) return (await r.json()).webSocketDebuggerUrl;
190
+ } catch { /* not up yet */ }
191
+ await new Promise(r => setTimeout(r, 300));
192
+ }
193
+ return null;
194
+ }
195
+
196
+ const wsUrl = await browserSocket();
197
+ if (!wsUrl) {
198
+ killBrowser();
199
+ die(`[video-render] op=cdp-unreachable run=${run} port=${port}`);
200
+ }
201
+
202
+ const ws = new WebSocket(wsUrl);
203
+ await new Promise((res, rej) => {
204
+ ws.addEventListener("open", res, { once: true });
205
+ ws.addEventListener("error", rej, { once: true });
206
+ });
207
+
208
+ let msgId = 0, sessionId = null;
209
+ const waiting = new Map();
210
+ const listeners = new Map();
211
+
212
+ ws.addEventListener("message", e => {
213
+ const m = JSON.parse(e.data);
214
+ if (m.id && waiting.has(m.id)) { waiting.get(m.id)(m); waiting.delete(m.id); return; }
215
+ if (m.method && listeners.has(m.method)) {
216
+ const arr = listeners.get(m.method);
217
+ listeners.delete(m.method);
218
+ for (const fn of arr) fn(m.params);
219
+ }
220
+ });
221
+
222
+ // Arm BEFORE sending the command that triggers the event, or the event can
223
+ // land between the send and the listen and the await never resolves.
224
+ function armed(method) {
225
+ let resolve;
226
+ const p = new Promise(r => { resolve = r; });
227
+ if (!listeners.has(method)) listeners.set(method, []);
228
+ listeners.get(method).push(resolve);
229
+ return p;
230
+ }
231
+
232
+ const send = (method, params = {}, timeoutMs = 90000) => new Promise((res, rej) => {
233
+ const n = ++msgId;
234
+ const t = setTimeout(() => { waiting.delete(n); rej(new Error(`cdp timeout: ${method}`)); }, timeoutMs);
235
+ waiting.set(n, m => {
236
+ clearTimeout(t);
237
+ m.error ? rej(new Error(`${method}: ${JSON.stringify(m.error)}`)) : res(m.result);
238
+ });
239
+ const msg = { id: n, method, params };
240
+ if (sessionId) msg.sessionId = sessionId;
241
+ ws.send(JSON.stringify(msg));
242
+ });
243
+
244
+ const ev = async expr =>
245
+ (await send("Runtime.evaluate", { expression: expr, returnByValue: true, awaitPromise: true }))
246
+ ?.result?.value;
247
+
248
+ // Its own tab. Grabbing the first page target collides with any other CDP client
249
+ // already attached to this browser.
250
+ const { targetId } = await send("Target.createTarget", { url: "about:blank" });
251
+ sessionId = (await send("Target.attachToTarget", { targetId, flatten: true })).sessionId;
252
+
253
+ await send("Page.enable");
254
+ await send("Runtime.enable");
255
+ await send("Emulation.setDeviceMetricsOverride", {
256
+ width, height, deviceScaleFactor: dsf, mobile,
257
+ screenWidth: width, screenHeight: height,
258
+ });
259
+ // Load-bearing. Headless otherwise reports prefers-reduced-motion: reduce, and a
260
+ // page that honours it stops animating — a capture that is silently frozen.
261
+ await send("Emulation.setEmulatedMedia", {
262
+ features: [{ name: "prefers-reduced-motion", value: "no-preference" }],
263
+ });
264
+
265
+ // ---------------------------------------------------------------- teardown
266
+
267
+ function killBrowser() {
268
+ try { process.kill(-chromePid, "SIGTERM"); }
269
+ catch { try { process.kill(chromePid, "SIGTERM"); } catch { /* already gone */ } }
270
+ }
271
+
272
+ async function teardown() {
273
+ try { await send("Target.closeTarget", { targetId }, 10000); } catch { /* browser may be gone */ }
274
+ try { ws.close(); } catch { /* already closed */ }
275
+ log(`[video-render] op=chromium-stop run=${run} pid=${chromePid}`);
276
+ killBrowser();
277
+ // The delay is required. A synchronous check reports a premature alive=false.
278
+ await new Promise(r => setTimeout(r, 2000));
279
+ let alive = true;
280
+ try { process.kill(chromePid, 0); } catch { alive = false; }
281
+ if (alive) { try { process.kill(-chromePid, "SIGKILL"); } catch { /* raced */ } }
282
+ log(`[video-render] op=survives-stop run=${run} pid=${chromePid} alive=${alive}`);
283
+ rmSync(profile, { recursive: true, force: true });
284
+ }
285
+
286
+ // A failed render must leave no more behind than a successful one. The frames
287
+ // captured before the failure have no consumer, and a 4600-frame run abandons
288
+ // gigabytes if they are kept.
289
+ async function abort(line) {
290
+ await teardown();
291
+ rmSync(frameDir, { recursive: true, force: true });
292
+ rmSync(stillDir, { recursive: true, force: true }); // no stills exist on an abort
293
+ log(`${line} framesDirRemoved=${!existsSync(frameDir)}`);
294
+ census("abort");
295
+ process.exit(1);
296
+ }
297
+
298
+ // ---------------------------------------------------------------- navigate
299
+
300
+ const loaded = armed("Page.loadEventFired");
301
+ try { await send("Page.navigate", { url }); }
302
+ catch (e) { await abort(`[video-render] op=navigate-failed run=${run} url=${url} error=${e.message}`); }
303
+ await loaded;
304
+ await new Promise(r => setTimeout(r, 1500)); // fonts
305
+
306
+ const stepTime = async ms => {
307
+ const expired = armed("Emulation.virtualTimeBudgetExpired");
308
+ await send("Emulation.setVirtualTimePolicy", { policy: "advance", budget: ms });
309
+ await expired;
310
+ };
311
+
312
+ if (mode === "page") await stepTime(1); // arm the virtual clock
313
+ if (pre) {
314
+ try { await ev(pre); }
315
+ catch (e) { await abort(`[video-render] op=pre-failed run=${run} error=${e.message}`); }
316
+ if (mode === "page") await stepTime(60); // let the pre-actions settle
317
+ }
318
+
319
+ // ---------------------------------------------------------------- the clock
320
+
321
+ let total, preShot, postShot;
322
+ if (mode === "reel") {
323
+ total = await ev("window.__frames");
324
+ if (!Number.isFinite(total) || total < 1) {
325
+ await abort(`[video-render] op=frames-missing run=${run} value=${JSON.stringify(total)}`);
326
+ }
327
+ preShot = async i => { await ev(`window.__seek(${i})`); };
328
+ postShot = async () => {};
329
+ } else {
330
+ total = Number(framesArg);
331
+ if (!Number.isFinite(total) || total < 1) {
332
+ await abort(`[video-render] op=bad-args run=${run} reason=frames-not-a-positive-number`);
333
+ }
334
+ preShot = async () => {};
335
+ postShot = async () => { await stepTime(FRAME_MS); };
336
+ }
337
+ log(`[video-render] op=frames-declared run=${run} frames=${total}`);
338
+
339
+ // ---------------------------------------------------------------- capture
340
+
341
+ async function shot(i) {
342
+ let last;
343
+ for (let attempt = 0; attempt < 4; attempt++) {
344
+ try {
345
+ const r = await send("Page.captureScreenshot",
346
+ { format: "jpeg", quality: 92, captureBeyondViewport: false, fromSurface: true }, 25000);
347
+ if (r?.data) return Buffer.from(r.data, "base64");
348
+ last = new Error("no screenshot data");
349
+ } catch (e) { last = e; }
350
+ }
351
+ await abort(`[video-render] op=capture-failed run=${run} i=${i} error=${last?.message}`);
352
+ }
353
+
354
+ let bytesMin = Infinity, bytesMax = 0, bytesTotal = 0;
355
+ for (let i = 0; i < total; i++) {
356
+ await preShot(i);
357
+ const buf = await shot(i);
358
+ writeFileSync(join(frameDir, `f-${String(i).padStart(5, "0")}.jpg`), buf);
359
+ if (buf.length < bytesMin) bytesMin = buf.length;
360
+ if (buf.length > bytesMax) bytesMax = buf.length;
361
+ bytesTotal += buf.length;
362
+ if (i % 60 === 0) log(`[video-render] op=capture run=${run} i=${i} bytes=${buf.length}`);
363
+ await postShot(i);
364
+ }
365
+ // The blank-render discriminator. A frozen capture encodes to a perfectly valid,
366
+ // perfectly useless MP4, and nothing else separates it from a good one.
367
+ log(`[video-render] op=capture-done run=${run} frames=${total} bytesMin=${bytesMin} bytesMax=${bytesMax} bytesTotal=${bytesTotal}`);
368
+
369
+ await teardown();
370
+
371
+ // ---------------------------------------------------------------- encode
372
+
373
+ const mp4 = join(outDir, `${name}.mp4`);
374
+ const poster = join(outDir, `${name}-poster.jpg`);
375
+
376
+ try {
377
+ execFileSync("ffmpeg", [
378
+ "-y", "-loglevel", "error", "-framerate", "30",
379
+ "-i", join(frameDir, "f-%05d.jpg"),
380
+ "-c:v", "libx264", "-crf", "21", "-preset", "slow",
381
+ "-pix_fmt", "yuv420p", "-movflags", "+faststart", "-an", mp4,
382
+ ]);
383
+ } catch (e) {
384
+ die(`[video-render] op=encode-failed run=${run} error=${e.message}`);
385
+ }
386
+
387
+ // Read back what was produced. Never assert the encode arguments as the result.
388
+ let probe;
389
+ try {
390
+ probe = JSON.parse(execFileSync("ffprobe", [
391
+ "-v", "error", "-select_streams", "v:0",
392
+ "-show_entries", "stream=width,height,codec_name",
393
+ "-show_entries", "format=duration", "-of", "json", mp4,
394
+ ]).toString());
395
+ } catch (e) {
396
+ die(`[video-render] op=probe-failed run=${run} error=${e.message}`);
397
+ }
398
+ const stream = probe.streams?.[0] ?? {};
399
+ const durationS = Number(probe.format?.duration ?? 0);
400
+ const fileBytes = statSync(mp4).size;
401
+ log(`[video-render] op=encode run=${run} durationS=${durationS.toFixed(2)} wxh=${stream.width}x${stream.height} codec=${stream.codec_name} fileBytes=${fileBytes}`);
402
+
403
+ try {
404
+ execFileSync("ffmpeg", ["-y", "-loglevel", "error", "-ss", String(durationS / 2),
405
+ "-i", mp4, "-frames:v", "1", "-q:v", "3", poster]);
406
+ } catch (e) {
407
+ die(`[video-render] op=poster-failed run=${run} error=${e.message}`);
408
+ }
409
+
410
+ // The agent must READ these. That is the step that catches a wrapped counter or a
411
+ // clipped headline, and no script can do it.
412
+ const stills = [];
413
+ for (const frac of [0.05, 0.35, 0.65, 0.95]) {
414
+ const at = join(stillDir, `s-${String(Math.round(frac * 100)).padStart(2, "0")}.jpg`);
415
+ try {
416
+ execFileSync("ffmpeg", ["-y", "-loglevel", "error", "-ss", String(durationS * frac),
417
+ "-i", mp4, "-frames:v", "1", "-q:v", "3", at]);
418
+ stills.push(at);
419
+ } catch { /* a still past the last keyframe is not fatal */ }
420
+ }
421
+ log(`[video-render] op=stills run=${run} count=${stills.length} paths=${stills.join(",")}`);
422
+
423
+ // ---------------------------------------------------------------- exit
424
+
425
+ rmSync(frameDir, { recursive: true, force: true });
426
+ const framesDirRemoved = !existsSync(frameDir);
427
+ log(`[video-render] op=exit run=${run} ranMs=${Date.now() - T0} framesDirRemoved=${framesDirRemoved} outputBytes=${fileBytes} mp4=${mp4} poster=${poster}`);
428
+ census("end");
429
+ process.exit(0);
@@ -9,6 +9,23 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-08-04 (0.1.559)
13
+
14
+ - Upgrading no longer deletes specialists. Every upgrade was removing any specialist that did not ship with the install, including ones written for you, and never putting them back. An upgrade now only replaces what it actually ships, and reports at boot any specialist the roster cannot dispatch.
15
+ - A specialist can now tell an instruction from its coordinator apart from text that merely claims to be one. One was refusing genuine operator instructions as suspected injection; every specialist card now states where a coordinator message comes from and what authority it carries.
16
+ - Multi-step workflows can pass data between steps. The runner was discarding each step's structured result, so a later step could never read an earlier one's output.
17
+ - A Telegram bot that receives a message and never answers is now reported. That check existed on the WhatsApp path only.
18
+ - A conversation that starts on a channel and produces nothing is no longer invisible. The assistant's own output is now captured, so a session that boots and then says nothing leaves a trace to diagnose.
19
+ - On a quotation, a gap the assistant noticed while pricing now survives into the saved figures instead of being dropped when the job is handed to the pricing engine.
20
+ - The scheduling health count now covers routines that run without the model, not just those that use it, so the shift away from model-driven runs has something to measure against.
21
+
22
+ ## 2026-08-04 (0.1.558)
23
+
24
+ - The assistant can now produce short videos and social reels. Ask it for a video and it renders an MP4 from your images, text and audio, rather than assembling a one-off pipeline each time.
25
+ - A public Telegram bot can now reply. Bots opened on the public route were being given a reply tool that was never connected, so messages arrived, the conversation stayed alive, and nothing ever came back.
26
+ - Scrolling back through a very long conversation no longer stops part-way. A single very large message could stall the scroll-back at a fixed point, putting everything above it out of reach no matter how far you scrolled; on one 15 MB conversation that was everything before the last 1.4 MB.
27
+ - The Telegram health check now separates how long ago a conversation was started from how long ago it last said anything, and can see a conversation that has not spoken yet.
28
+
12
29
  ## 2026-08-04 (0.1.557)
13
30
 
14
31
  - The status dot on a one-time routine that has already run now shows it as finished rather than live, matching the card text added in the previous release.
@@ -27,6 +27,7 @@ Load these when users ask about Maxy features or need guidance:
27
27
  - **Projects** → `references/projects-guide.md` — creating projects, tracking health, phase transitions, completing
28
28
  - **Workflows** → `references/workflows.md` — saving and running multi-step processes, declared inputs, scheduling, run history
29
29
  - **Slides** → `references/slides.md` — generating, theming, and reviewing HTML slide decks with `/slides`
30
+ - **Making videos** → `references/video-production.md` — social reels and animated explainers as real MP4 files, portrait or landscape, made on the device with no upload
30
31
  - **Calendar & booking** → `references/calendar-booking.md` — the admin calendar at `/calendar`, deploying a public booking page, setting availability, and how bookings become meetings
31
32
  - **Troubleshooting** → `references/troubleshooting.md` — common issues and how to resolve them
32
33
 
@@ -0,0 +1,37 @@
1
+ # Making videos
2
+
3
+ {{productName}} makes real video files on your own device. No upload, no external service, no subscription. Ask for one in plain words.
4
+
5
+ > "Make me three social reels about the free trial."
6
+ >
7
+ > "Can I have that in portrait and landscape?"
8
+ >
9
+ > "Record that page playing its animation."
10
+
11
+ ## What you get
12
+
13
+ An MP4 file, silent, at whatever size you need. Portrait (1080x1920) for stories and reels, landscape (1920x1080) for a website or a presentation, or any other shape you ask for. Each video comes with a poster image, which is the still that shows before someone presses play.
14
+
15
+ Ask for another shape at any time and the same content is recut for it. You do not have to describe it again.
16
+
17
+ ## Two kinds of video
18
+
19
+ **Made from scratch.** The agent designs the video itself, in your brand's colours and type, and animates it frame by frame. This is what you want for a social reel, an explainer, or anything with a message to land.
20
+
21
+ **A recording of a page.** If you already have a page that animates, the agent can record it playing. Useful when the page is the thing you want to show.
22
+
23
+ ## How long it takes
24
+
25
+ A short reel takes a few minutes. A long recording of a full page can take considerably longer. The agent tells you when it starts and comes back when it is done.
26
+
27
+ ## What to check
28
+
29
+ The agent looks at four frames from every video it makes before telling you it is finished, so a video that came out blank or with text running off the edge gets caught and re-rendered. You should still watch it. Text that is correct but reads badly is a judgement only you can make.
30
+
31
+ ## Where the files go
32
+
33
+ Finished videos land in your account's output folder, and the agent sends them to you in the chat. Ask for them on a web page and they get published to your site with everything on one page you can open on a phone.
34
+
35
+ ## Fonts
36
+
37
+ Videos use the fonts installed on your device. If you want a specific brand font and it has never been used on this device before, say so — it may need installing first.
@@ -6,7 +6,11 @@ export interface ToolResult {
6
6
  /** Present and true only on error results, so `.isError` is well-typed on the
7
7
  * ok|err union that handlers return. */
8
8
  isError?: true;
9
+ /** Task 2406 — the tool's structured return, for a workflow step's
10
+ * `{{key.field}}`. Absent on errors: the workflow engine throws on isError
11
+ * before it reads the output. */
12
+ structuredContent?: Record<string, unknown>;
9
13
  }
10
- export declare const ok: (text: string) => ToolResult;
14
+ export declare const ok: (text: string, structuredContent?: Record<string, unknown>) => ToolResult;
11
15
  export declare const err: (text: string) => ToolResult;
12
16
  //# sourceMappingURL=result.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/lib/result.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C;6CACyC;IACzC,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED,eAAO,MAAM,EAAE,GAAI,MAAM,MAAM,KAAG,UAAqD,CAAC;AACxF,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,KAAG,UAGjC,CAAC"}
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/lib/result.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C;6CACyC;IACzC,OAAO,CAAC,EAAE,IAAI,CAAC;IACf;;sCAEkC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C;AAED,eAAO,MAAM,EAAE,GAAI,MAAM,MAAM,EAAE,oBAAoB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,UAG7E,CAAC;AACH,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,KAAG,UAGjC,CAAC"}
@@ -1,4 +1,7 @@
1
- export const ok = (text) => ({ content: [{ type: "text", text }] });
1
+ export const ok = (text, structuredContent) => ({
2
+ content: [{ type: "text", text }],
3
+ ...(structuredContent ? { structuredContent } : {}),
4
+ });
2
5
  export const err = (text) => ({
3
6
  content: [{ type: "text", text }],
4
7
  isError: true,
@@ -1 +1 @@
1
- {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/lib/result.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAY,EAAc,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;AACxF,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAY,EAAc,EAAE,CAAC,CAAC;IAChD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjC,OAAO,EAAE,IAAI;CACd,CAAC,CAAC"}
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/lib/result.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAY,EAAE,iBAA2C,EAAc,EAAE,CAAC,CAAC;IAC5F,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;CACpD,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAY,EAAc,EAAE,CAAC,CAAC;IAChD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjC,OAAO,EAAE,IAAI;CACd,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-read.d.ts","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AACA,OAAO,EAAiD,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAK1F,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAmBpG"}
1
+ {"version":3,"file":"file-read.d.ts","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AACA,OAAO,EAAiD,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAK1F,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAoBpG"}
@@ -19,7 +19,8 @@ export function fileRead(args, ctx) {
19
19
  return err(`file-read: not a file: ${args.path}`);
20
20
  if (st.size > MAX_FILE)
21
21
  return err(`file-read: file too large (${st.size} bytes, max ${MAX_FILE})`);
22
- return ok(readFileSync(abs, "utf-8"));
22
+ const content = readFileSync(abs, "utf-8");
23
+ return ok(content, { path: args.path, content, bytes: st.size });
23
24
  }
24
25
  catch (e) {
25
26
  return err(`file-read: ${e.message}`);
@@ -1 +1 @@
1
- {"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAY,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAEzC,MAAM,UAAU,QAAQ,CAAC,IAAkE,EAAE,GAAQ;IACnG,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3E,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CACR,wCAAwC,CAAC,CAAC,QAAQ,KAAK,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,YAAY,CAC5G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;YAAE,OAAO,GAAG,CAAC,0BAA0B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,EAAE,CAAC,IAAI,GAAG,QAAQ;YAAE,OAAO,GAAG,CAAC,8BAA8B,EAAE,CAAC,IAAI,eAAe,QAAQ,GAAG,CAAC,CAAC;QACpG,OAAO,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAY,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAEzC,MAAM,UAAU,QAAQ,CAAC,IAAkE,EAAE,GAAQ;IACnG,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3E,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CACR,wCAAwC,CAAC,CAAC,QAAQ,KAAK,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,YAAY,CAC5G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;YAAE,OAAO,GAAG,CAAC,0BAA0B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,EAAE,CAAC,IAAI,GAAG,QAAQ;YAAE,OAAO,GAAG,CAAC,8BAA8B,EAAE,CAAC,IAAI,eAAe,QAAQ,GAAG,CAAC,CAAC;QACpG,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3C,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-write.d.ts","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiD,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG1F,wBAAgB,SAAS,CACvB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EACnF,GAAG,EAAE,GAAG,yCAmBT"}
1
+ {"version":3,"file":"file-write.d.ts","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiD,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG1F,wBAAgB,SAAS,CACvB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EACnF,GAAG,EAAE,GAAG,yCAoBT"}
@@ -16,7 +16,8 @@ export function fileWrite(args, ctx) {
16
16
  try {
17
17
  mkdirSync(dirname(abs), { recursive: true });
18
18
  writeFileSync(abs, args.content, "utf-8");
19
- return ok(`wrote ${Buffer.byteLength(args.content)} bytes to ${args.path}`);
19
+ const bytes = Buffer.byteLength(args.content);
20
+ return ok(`wrote ${bytes} bytes to ${args.path}`, { path: args.path, bytes });
20
21
  }
21
22
  catch (e) {
22
23
  return err(`file-write: ${e.message}`);
@@ -1 +1 @@
1
- {"version":3,"file":"file-write.js","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAY,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,SAAS,CACvB,IAAmF,EACnF,GAAQ;IAER,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5E,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,sBAAsB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CACR,yCAAyC,CAAC,CAAC,QAAQ,KAAK,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,YAAY,CAC7G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,OAAO,EAAE,CAAC,SAAS,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,eAAgB,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"file-write.js","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAY,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,SAAS,CACvB,IAAmF,EACnF,GAAQ;IAER,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5E,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,sBAAsB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CACR,yCAAyC,CAAC,CAAC,QAAQ,KAAK,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,YAAY,CAC7G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,OAAO,EAAE,CAAC,SAAS,KAAK,aAAa,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,eAAgB,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -138,11 +138,11 @@ Backfilling already-written `:Event` appointments into `:Meeting` is a separate
138
138
 
139
139
  ### Reading meetings back
140
140
 
141
- Both labels are readable. `schedule-list` returns meetings and events together and tags each row `kind`; a meeting renders as `MEETING — <title> (<start>) → <end> (<uid>)`. `status` filters **events only**, because a `:Meeting` carries no `eventStatus`; `from`, `to` and `upcoming` apply to meetings on `startsAt`.
141
+ Both labels are readable. `schedule-list` returns meetings and events together and tags each row `kind`; a meeting renders as `MEETING — <title> (<start>) → <end> (<uid>)`. `status` filters **events only**, because a `:Meeting` carries no `eventStatus`; `from`, `to` and `upcoming` apply to meetings on `startsAt`. `kind` selects one source: `kind: "event"` returns scheduled events only, `kind: "meeting"` returns booked meetings only, and omitting it returns both. Because `status` is event-only, `kind: "meeting"` combined with a status returns nothing rather than silently dropping one of the two filters.
142
142
 
143
143
  A meeting's handle is its `uid`. `schedule-event` returns it on creation, and `schedule-get`, `schedule-update` and `schedule-cancel` accept it wherever they accept an event id — they resolve an event first, then a meeting, so a caller never has to say which kind it holds. Updating a meeting sets `title`, `startsAt`, `endsAt` and `location` and never rewrites the `uid`; `eventStatus`, `recurrence`, `note`, `action` and `agentDispatch` have no meeting equivalent and do not cross over. A meeting DOES carry a `description`: `schedule-event` writes one, `schedule-get` returns it, and the admin Calendar edits it. `schedule-update` does not write it, so this tool's field set is narrower than the admin Calendar edit's. Cancelling a meeting **deletes** it and its attendee edges, the same as deleting it on the Calendar page — a `:Meeting` has no status to set to cancelled. A booking-sourced meeting that is deleted is rewritten by the next reconcile run.
144
144
 
145
- Until this landed, `schedule-list` matched `:Event` alone while `schedule-event` wrote `:Meeting`, so an agent could not see its own writes: a calendar-sync turn read back an empty list, re-applied the same three meetings, and told the operator over WhatsApp that they had not saved. Observability is therefore two-part. Every list call logs `[schedule-list] op=result meetings=<n> events=<n>`. The standing check is `[schedule-audit] account=<id> meetings=<n> listable=<n> invisible=<n>`, emitted per account on every `reconcile-bookings` run (every 120 s) **before** that script's Cloudflare gates, so it also reports on installs with no booking site. `listable` is what the real `scheduleList` returns, not a re-implementation of its query — that is the whole point, since a copy would agree with a broken original. `invisible=0` is the success signal; `invisible>0` means a calendar entry exists that the agent cannot see.
145
+ Until this landed, `schedule-list` matched `:Event` alone while `schedule-event` wrote `:Meeting`, so an agent could not see its own writes: a calendar-sync turn read back an empty list, re-applied the same three meetings, and told the operator over WhatsApp that they had not saved. Observability is therefore two-part. Every list call logs `[schedule-list] op=result kind=<kind|any> meetings=<n> events=<n>`. The standing check is `[schedule-audit] account=<id> meetings=<n> listable=<n> invisible=<n>`, emitted per account on every `reconcile-bookings` run (every 120 s) **before** that script's Cloudflare gates, so it also reports on installs with no booking site. `listable` is what the real `scheduleList` returns, not a re-implementation of its query — that is the whole point, since a copy would agree with a broken original. `invisible=0` is the success signal; `invisible>0` means a calendar entry exists that the agent cannot see.
146
146
 
147
147
  ### Routine run records
148
148
 
@@ -389,6 +389,8 @@ The `sessionKey` parameter on `schedule-event` automatically creates the `PART_O
389
389
 
390
390
  Export events as `.ics` files so users can add them to Apple Calendar, Google Calendar, or Outlook. Use `schedule-export-ics` with the event IDs; it writes the `.ics` under the account's `output/ics/` directory and returns the path. Deliver that file to the operator (see the `file-presentation` skill for how delivery works on this surface).
391
391
 
392
+ Both `schedule-list` and `schedule-export-ics` also return `structuredContent` — `{ events, count }` and the export result (`outputPath`, `eventCount`, `rruleMapped`, `rruleFallback`, `notFound`, `noStartDate`) — so a workflow step can address those fields directly instead of parsing the prose.
393
+
392
394
  Import `.ics` files uploaded by the user. Use `schedule-import-ics` with the attachment path. Present the extracted events for confirmation before creating them via `schedule-event`.
393
395
 
394
396
  Cron→RRULE mapping for recurring events: daily, weekly-with-days, and monthly-by-date patterns produce standard RRULE properties. Cron patterns that cannot map to RRULE (e.g. `0 8 1,15 * *`) produce individual VEVENTs per occurrence instead.