@jaimevalasek/aioson 1.3.0 → 1.5.1

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 (330) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/LICENSE +661 -21
  3. package/README.md +22 -3
  4. package/docs/en/squad-dashboard.md +372 -0
  5. package/docs/openclaw-bridge.md +308 -0
  6. package/docs/pt/README.md +62 -2
  7. package/docs/pt/advisor-spec.md +5 -5
  8. package/docs/pt/agentes-customizados.md +670 -0
  9. package/docs/pt/agentes.md +235 -23
  10. package/docs/pt/automacao-squads.md +407 -0
  11. package/docs/pt/cenarios.md +49 -5
  12. package/docs/pt/clientes-ai.md +62 -0
  13. package/docs/pt/comandos-cli.md +226 -17
  14. package/docs/pt/deyvin.md +115 -0
  15. package/docs/pt/genome-3.0-spec.md +11 -11
  16. package/docs/pt/inicio-rapido.md +63 -2
  17. package/docs/pt/memoria-contexto.md +255 -0
  18. package/docs/pt/output-strategy-delivery.md +655 -0
  19. package/docs/pt/profiler-system.md +17 -17
  20. package/docs/pt/runtime-observability.md +5 -1
  21. package/docs/pt/skills.md +175 -0
  22. package/docs/pt/squad-dashboard.md +373 -0
  23. package/docs/pt/{squad-genoma.md → squad-genome.md} +81 -75
  24. package/docs/testing/genome-2.0-matrix.md +5 -5
  25. package/docs/testing/genome-2.0-rollout.md +10 -10
  26. package/package.json +4 -4
  27. package/src/agents.js +21 -5
  28. package/src/backup-local.js +74 -0
  29. package/src/backup-provider.js +303 -0
  30. package/src/cli.js +276 -2
  31. package/src/commands/agents.js +22 -4
  32. package/src/commands/backup-local-cmd.js +25 -0
  33. package/src/commands/backup.js +533 -0
  34. package/src/commands/cloud.js +17 -17
  35. package/src/commands/context-pack.js +45 -0
  36. package/src/commands/implementation-plan.js +340 -0
  37. package/src/commands/learning.js +134 -0
  38. package/src/commands/live.js +1583 -0
  39. package/src/commands/runtime.js +1075 -2
  40. package/src/commands/scan-project.js +288 -24
  41. package/src/commands/setup-context.js +30 -2
  42. package/src/commands/skill.js +558 -0
  43. package/src/commands/squad-agent-create.js +788 -0
  44. package/src/commands/squad-daemon.js +209 -0
  45. package/src/commands/squad-dashboard.js +39 -0
  46. package/src/commands/squad-deploy.js +64 -0
  47. package/src/commands/squad-doctor.js +103 -1
  48. package/src/commands/squad-investigate.js +261 -0
  49. package/src/commands/squad-learning.js +209 -0
  50. package/src/commands/squad-mcp.js +270 -0
  51. package/src/commands/squad-pipeline.js +247 -1
  52. package/src/commands/squad-plan.js +329 -0
  53. package/src/commands/squad-processes.js +56 -0
  54. package/src/commands/squad-recovery.js +42 -0
  55. package/src/commands/squad-roi.js +291 -0
  56. package/src/commands/squad-score.js +250 -0
  57. package/src/commands/squad-status.js +38 -2
  58. package/src/commands/squad-validate.js +118 -1
  59. package/src/commands/squad-webhook.js +160 -0
  60. package/src/commands/squad-worker.js +191 -0
  61. package/src/commands/squad-worktrees.js +75 -0
  62. package/src/commands/test-agents.js +6 -1
  63. package/src/commands/web-map.js +70 -0
  64. package/src/commands/web-scrape.js +71 -0
  65. package/src/commands/workflow-next.js +8 -1
  66. package/src/commands/workflow-status.js +250 -0
  67. package/src/constants.js +88 -16
  68. package/src/context-memory.js +837 -0
  69. package/src/context-writer.js +47 -1
  70. package/src/delivery-runner.js +319 -0
  71. package/src/genome-files.js +1 -1
  72. package/src/genome-format.js +1 -1
  73. package/src/i18n/messages/en.js +333 -8
  74. package/src/i18n/messages/es.js +240 -6
  75. package/src/i18n/messages/fr.js +239 -5
  76. package/src/i18n/messages/pt-BR.js +330 -12
  77. package/src/installer.js +30 -2
  78. package/src/lib/genomes/compat.js +1 -1
  79. package/src/lib/webhook-server.js +328 -0
  80. package/src/mcp-connectors/registry.js +602 -0
  81. package/src/runtime-store.js +1037 -42
  82. package/src/session-handoff.js +77 -0
  83. package/src/squad/external-session.js +180 -0
  84. package/src/squad/inter-squad.js +74 -0
  85. package/src/squad/recovery-context.js +201 -0
  86. package/src/squad/worktree-manager.js +114 -0
  87. package/src/squad-daemon.js +490 -0
  88. package/src/squad-dashboard/api.js +223 -0
  89. package/src/squad-dashboard/attachment-handler.js +93 -0
  90. package/src/squad-dashboard/context-monitor.js +157 -0
  91. package/src/squad-dashboard/execution-logs.js +115 -0
  92. package/src/squad-dashboard/hunk-review.js +209 -0
  93. package/src/squad-dashboard/metrics.js +133 -0
  94. package/src/squad-dashboard/process-monitor.js +125 -0
  95. package/src/squad-dashboard/renderer.js +858 -0
  96. package/src/squad-dashboard/server.js +232 -0
  97. package/src/squad-dashboard/styles.js +525 -0
  98. package/src/squad-dashboard/token-tracker.js +99 -0
  99. package/src/web.js +284 -0
  100. package/src/worker-runner.js +339 -0
  101. package/template/.aioson/agents/analyst.md +40 -9
  102. package/template/.aioson/agents/architect.md +24 -5
  103. package/template/.aioson/agents/dev.md +254 -25
  104. package/template/.aioson/agents/deyvin.md +174 -0
  105. package/template/.aioson/agents/discovery-design-doc.md +25 -1
  106. package/template/.aioson/agents/{genoma.md → genome.md} +20 -20
  107. package/template/.aioson/agents/neo.md +152 -0
  108. package/template/.aioson/agents/orache.md +388 -0
  109. package/template/.aioson/agents/orchestrator.md +63 -2
  110. package/template/.aioson/agents/pair.md +5 -0
  111. package/template/.aioson/agents/pm.md +17 -5
  112. package/template/.aioson/agents/product.md +113 -29
  113. package/template/.aioson/agents/profiler-enricher.md +1 -1
  114. package/template/.aioson/agents/profiler-forge.md +9 -9
  115. package/template/.aioson/agents/profiler-researcher.md +1 -1
  116. package/template/.aioson/agents/qa.md +18 -5
  117. package/template/.aioson/agents/setup.md +138 -18
  118. package/template/.aioson/agents/sheldon.md +603 -0
  119. package/template/.aioson/agents/squad.md +866 -28
  120. package/template/.aioson/agents/tester.md +254 -0
  121. package/template/.aioson/agents/ux-ui.md +289 -34
  122. package/template/.aioson/config.md +181 -0
  123. package/template/.aioson/context/spec.md.template +17 -0
  124. package/template/.aioson/genomes/.gitkeep +0 -0
  125. package/template/.aioson/installed-skills/.gitkeep +0 -0
  126. package/template/.aioson/locales/en/agents/analyst.md +34 -4
  127. package/template/.aioson/locales/en/agents/architect.md +18 -0
  128. package/template/.aioson/locales/en/agents/dev.md +155 -11
  129. package/template/.aioson/locales/en/agents/deyvin.md +137 -0
  130. package/template/.aioson/locales/en/agents/{genoma.md → genome.md} +14 -14
  131. package/template/.aioson/locales/en/agents/neo.md +8 -0
  132. package/template/.aioson/locales/en/agents/orchestrator.md +62 -2
  133. package/template/.aioson/locales/en/agents/pair.md +5 -0
  134. package/template/.aioson/locales/en/agents/pm.md +7 -0
  135. package/template/.aioson/locales/en/agents/product.md +35 -17
  136. package/template/.aioson/locales/en/agents/qa.md +56 -0
  137. package/template/.aioson/locales/en/agents/setup.md +53 -6
  138. package/template/.aioson/locales/en/agents/sheldon.md +340 -0
  139. package/template/.aioson/locales/en/agents/squad.md +203 -15
  140. package/template/.aioson/locales/en/agents/ux-ui.md +383 -35
  141. package/template/.aioson/locales/es/agents/analyst.md +24 -4
  142. package/template/.aioson/locales/es/agents/architect.md +18 -0
  143. package/template/.aioson/locales/es/agents/dev.md +136 -9
  144. package/template/.aioson/locales/es/agents/deyvin.md +97 -0
  145. package/template/.aioson/locales/es/agents/{genoma.md → genome.md} +13 -13
  146. package/template/.aioson/locales/es/agents/neo.md +48 -0
  147. package/template/.aioson/locales/es/agents/orache.md +103 -0
  148. package/template/.aioson/locales/es/agents/orchestrator.md +62 -2
  149. package/template/.aioson/locales/es/agents/pair.md +5 -0
  150. package/template/.aioson/locales/es/agents/pm.md +7 -0
  151. package/template/.aioson/locales/es/agents/product.md +13 -3
  152. package/template/.aioson/locales/es/agents/qa.md +33 -0
  153. package/template/.aioson/locales/es/agents/setup.md +30 -6
  154. package/template/.aioson/locales/es/agents/sheldon.md +192 -0
  155. package/template/.aioson/locales/es/agents/squad.md +284 -15
  156. package/template/.aioson/locales/es/agents/ux-ui.md +34 -25
  157. package/template/.aioson/locales/fr/agents/analyst.md +24 -4
  158. package/template/.aioson/locales/fr/agents/architect.md +18 -0
  159. package/template/.aioson/locales/fr/agents/dev.md +136 -9
  160. package/template/.aioson/locales/fr/agents/deyvin.md +97 -0
  161. package/template/.aioson/locales/fr/agents/{genoma.md → genome.md} +7 -7
  162. package/template/.aioson/locales/fr/agents/neo.md +48 -0
  163. package/template/.aioson/locales/fr/agents/orache.md +104 -0
  164. package/template/.aioson/locales/fr/agents/orchestrator.md +62 -2
  165. package/template/.aioson/locales/fr/agents/pair.md +5 -0
  166. package/template/.aioson/locales/fr/agents/pm.md +7 -0
  167. package/template/.aioson/locales/fr/agents/product.md +13 -3
  168. package/template/.aioson/locales/fr/agents/qa.md +33 -0
  169. package/template/.aioson/locales/fr/agents/setup.md +30 -6
  170. package/template/.aioson/locales/fr/agents/sheldon.md +192 -0
  171. package/template/.aioson/locales/fr/agents/squad.md +279 -10
  172. package/template/.aioson/locales/fr/agents/ux-ui.md +34 -25
  173. package/template/.aioson/locales/pt-BR/agents/analyst.md +45 -4
  174. package/template/.aioson/locales/pt-BR/agents/architect.md +29 -0
  175. package/template/.aioson/locales/pt-BR/agents/dev.md +167 -15
  176. package/template/.aioson/locales/pt-BR/agents/deyvin.md +137 -0
  177. package/template/.aioson/locales/pt-BR/agents/{genoma.md → genome.md} +49 -49
  178. package/template/.aioson/locales/pt-BR/agents/neo.md +147 -0
  179. package/template/.aioson/locales/pt-BR/agents/orache.md +137 -0
  180. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +62 -2
  181. package/template/.aioson/locales/pt-BR/agents/pair.md +5 -0
  182. package/template/.aioson/locales/pt-BR/agents/pm.md +7 -0
  183. package/template/.aioson/locales/pt-BR/agents/product.md +43 -20
  184. package/template/.aioson/locales/pt-BR/agents/qa.md +67 -0
  185. package/template/.aioson/locales/pt-BR/agents/setup.md +53 -6
  186. package/template/.aioson/locales/pt-BR/agents/sheldon.md +192 -0
  187. package/template/.aioson/locales/pt-BR/agents/squad.md +591 -47
  188. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +369 -22
  189. package/template/.aioson/my-agents/.gitkeep +0 -0
  190. package/template/.aioson/rules/.gitkeep +0 -0
  191. package/template/.aioson/rules/squad/.gitkeep +0 -0
  192. package/template/.aioson/rules/squad/README.md +50 -0
  193. package/template/.aioson/schemas/genome-meta.schema.json +1 -1
  194. package/template/.aioson/schemas/genome.schema.json +1 -1
  195. package/template/.aioson/schemas/squad-blueprint.schema.json +32 -0
  196. package/template/.aioson/schemas/squad-manifest.schema.json +434 -1
  197. package/template/.aioson/skills/design/bold-editorial-ui/SKILL.md +205 -0
  198. package/template/.aioson/skills/design/bold-editorial-ui/references/art-direction.md +338 -0
  199. package/template/.aioson/skills/design/bold-editorial-ui/references/components.md +977 -0
  200. package/template/.aioson/skills/design/bold-editorial-ui/references/dashboards.md +218 -0
  201. package/template/.aioson/skills/design/bold-editorial-ui/references/design-tokens.md +326 -0
  202. package/template/.aioson/skills/design/bold-editorial-ui/references/motion.md +461 -0
  203. package/template/.aioson/skills/design/bold-editorial-ui/references/patterns.md +293 -0
  204. package/template/.aioson/skills/design/bold-editorial-ui/references/websites.md +352 -0
  205. package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +210 -0
  206. package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +319 -0
  207. package/template/.aioson/skills/design/clean-saas-ui/references/components.md +365 -0
  208. package/template/.aioson/skills/design/clean-saas-ui/references/dashboards.md +196 -0
  209. package/template/.aioson/skills/design/clean-saas-ui/references/design-tokens.md +244 -0
  210. package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +235 -0
  211. package/template/.aioson/skills/design/clean-saas-ui/references/patterns.md +215 -0
  212. package/template/.aioson/skills/design/clean-saas-ui/references/websites.md +295 -0
  213. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +203 -0
  214. package/template/.aioson/skills/design/cognitive-core-ui/references/art-direction.md +339 -0
  215. package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +407 -0
  216. package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +272 -0
  217. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +524 -0
  218. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +277 -0
  219. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +289 -0
  220. package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +437 -0
  221. package/template/.aioson/skills/design/interface-design/SKILL.md +47 -0
  222. package/template/.aioson/skills/design/interface-design/references/components-and-states.md +105 -0
  223. package/template/.aioson/skills/design/interface-design/references/design-directions.md +101 -0
  224. package/template/.aioson/skills/design/interface-design/references/handoff-and-quality.md +71 -0
  225. package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +74 -0
  226. package/template/.aioson/skills/design/interface-design/references/tokens-and-depth.md +173 -0
  227. package/template/.aioson/skills/design/premium-command-center-ui/SKILL.md +62 -0
  228. package/template/.aioson/skills/design/premium-command-center-ui/references/operations.md +74 -0
  229. package/template/.aioson/skills/design/premium-command-center-ui/references/patterns.md +116 -0
  230. package/template/.aioson/skills/design/premium-command-center-ui/references/validation.md +47 -0
  231. package/template/.aioson/skills/design/premium-command-center-ui/references/visual-system.md +215 -0
  232. package/template/.aioson/skills/design/warm-craft-ui/SKILL.md +209 -0
  233. package/template/.aioson/skills/design/warm-craft-ui/references/art-direction.md +324 -0
  234. package/template/.aioson/skills/design/warm-craft-ui/references/components.md +508 -0
  235. package/template/.aioson/skills/design/warm-craft-ui/references/dashboards.md +223 -0
  236. package/template/.aioson/skills/design/warm-craft-ui/references/design-tokens.md +374 -0
  237. package/template/.aioson/skills/design/warm-craft-ui/references/motion.md +356 -0
  238. package/template/.aioson/skills/design/warm-craft-ui/references/patterns.md +288 -0
  239. package/template/.aioson/skills/design/warm-craft-ui/references/websites.md +289 -0
  240. package/template/.aioson/skills/design-system/SKILL.md +92 -0
  241. package/template/.aioson/skills/design-system/cognitive-core-ui.skill +0 -0
  242. package/template/.aioson/skills/design-system/components/SKILL.md +274 -0
  243. package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
  244. package/template/.aioson/skills/design-system/dashboards/SKILL.md +184 -0
  245. package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
  246. package/template/.aioson/skills/design-system/foundations/SKILL.md +250 -0
  247. package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
  248. package/template/.aioson/skills/design-system/motion/SKILL.md +197 -0
  249. package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
  250. package/template/.aioson/skills/design-system/patterns/SKILL.md +231 -0
  251. package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
  252. package/template/.aioson/skills/premium-visual-design/SKILL.md +83 -0
  253. package/template/.aioson/skills/premium-visual-design/components/agent-badge.md +92 -0
  254. package/template/.aioson/skills/premium-visual-design/components/dependency-node.md +102 -0
  255. package/template/.aioson/skills/premium-visual-design/components/mention-autocomplete.md +136 -0
  256. package/template/.aioson/skills/premium-visual-design/components/notification-center.md +136 -0
  257. package/template/.aioson/skills/premium-visual-design/components/review-action-bar.md +188 -0
  258. package/template/.aioson/skills/premium-visual-design/components/team-switcher.md +131 -0
  259. package/template/.aioson/skills/premium-visual-design/patterns/agent-message-thread.md +198 -0
  260. package/template/.aioson/skills/premium-visual-design/patterns/notification-panel.md +275 -0
  261. package/template/.aioson/skills/premium-visual-design/patterns/review-workflow-ui.md +234 -0
  262. package/template/.aioson/skills/premium-visual-design/patterns/task-dependency-graph.md +147 -0
  263. package/template/.aioson/skills/premium-visual-design/tokens/status-extended.md +142 -0
  264. package/template/.aioson/skills/squad/SKILL.md +58 -0
  265. package/template/.aioson/skills/squad/domains/.gitkeep +0 -0
  266. package/template/.aioson/skills/squad/formats/.gitkeep +0 -0
  267. package/template/.aioson/skills/squad/formats/catalog.json +15 -0
  268. package/template/.aioson/skills/squad/formats/content/blog-post.md +47 -0
  269. package/template/.aioson/skills/squad/formats/content/newsletter.md +47 -0
  270. package/template/.aioson/skills/squad/formats/creative/podcast-script.md +43 -0
  271. package/template/.aioson/skills/squad/formats/creative/video-script.md +41 -0
  272. package/template/.aioson/skills/squad/formats/social/instagram-feed.md +42 -0
  273. package/template/.aioson/skills/squad/formats/social/linkedin-post.md +42 -0
  274. package/template/.aioson/skills/squad/formats/social/tiktok.md +39 -0
  275. package/template/.aioson/skills/squad/formats/social/twitter-thread.md +39 -0
  276. package/template/.aioson/skills/squad/formats/social/youtube-long.md +47 -0
  277. package/template/.aioson/skills/squad/formats/social/youtube-shorts.md +39 -0
  278. package/template/.aioson/skills/squad/patterns/.gitkeep +0 -0
  279. package/template/.aioson/skills/squad/patterns/multi-platform-pattern.md +108 -0
  280. package/template/.aioson/skills/squad/patterns/persona-based-pattern.md +98 -0
  281. package/template/.aioson/skills/squad/patterns/pipeline-pattern.md +106 -0
  282. package/template/.aioson/skills/squad/patterns/review-loop-pattern.md +81 -0
  283. package/template/.aioson/skills/squad/references/.gitkeep +0 -0
  284. package/template/.aioson/skills/squad/references/checklist-templates.md +122 -0
  285. package/template/.aioson/skills/squad/references/executor-archetypes.md +123 -0
  286. package/template/.aioson/skills/squad/references/workflow-templates.md +169 -0
  287. package/template/.aioson/skills/static/debugging-protocol.md +42 -0
  288. package/template/.aioson/skills/static/git-worktrees.md +36 -0
  289. package/template/.aioson/tasks/implementation-plan.md +307 -0
  290. package/template/.aioson/tasks/squad-create.md +1 -1
  291. package/template/.aioson/tasks/squad-design.md +28 -0
  292. package/template/.aioson/tasks/squad-execution-plan.md +279 -0
  293. package/template/.aioson/tasks/squad-export.md +1 -1
  294. package/template/.aioson/tasks/squad-investigate.md +44 -0
  295. package/template/.aioson/tasks/squad-learning-review.md +44 -0
  296. package/template/.aioson/tasks/squad-output-config.md +177 -0
  297. package/template/.aioson/tasks/squad-profile.md +48 -0
  298. package/template/.aioson/tasks/squad-review.md +61 -0
  299. package/template/.aioson/tasks/squad-task-decompose.md +66 -0
  300. package/template/.aioson/tasks/squad-validate.md +1 -1
  301. package/template/.claude/commands/aioson/agent/deyvin.md +5 -0
  302. package/template/.claude/commands/aioson/agent/discovery-design-doc.md +5 -0
  303. package/template/.claude/commands/aioson/agent/genome.md +5 -0
  304. package/template/.claude/commands/aioson/agent/neo.md +5 -0
  305. package/template/.claude/commands/aioson/agent/product.md +5 -0
  306. package/template/.claude/commands/aioson/agent/profiler-enricher.md +5 -0
  307. package/template/.claude/commands/aioson/agent/profiler-forge.md +5 -0
  308. package/template/.claude/commands/aioson/agent/profiler-researcher.md +5 -0
  309. package/template/.claude/commands/aioson/agent/squad.md +5 -0
  310. package/template/.claude/commands/aioson/agent/tester.md +5 -0
  311. package/template/.gemini/GEMINI.md +3 -0
  312. package/template/.gemini/commands/aios-deyvin.toml +6 -0
  313. package/template/.gemini/commands/aios-neo.toml +4 -0
  314. package/template/.gemini/commands/aios-pair.toml +6 -0
  315. package/template/.gemini/commands/aios-tester.toml +6 -0
  316. package/template/AGENTS.md +37 -6
  317. package/template/CLAUDE.md +34 -4
  318. package/template/OPENCODE.md +8 -2
  319. package/template/squad-searches/.gitkeep +0 -0
  320. package/template/.aioson/skills/static/interface-design.md +0 -372
  321. package/template/.aioson/skills/static/premium-command-center-ui.md +0 -190
  322. /package/template/.aioson/{genomas → docs}/.gitkeep +0 -0
  323. /package/template/.claude/commands/aioson/{analyst.md → agent/analyst.md} +0 -0
  324. /package/template/.claude/commands/aioson/{architect.md → agent/architect.md} +0 -0
  325. /package/template/.claude/commands/aioson/{dev.md → agent/dev.md} +0 -0
  326. /package/template/.claude/commands/aioson/{orchestrator.md → agent/orchestrator.md} +0 -0
  327. /package/template/.claude/commands/aioson/{pm.md → agent/pm.md} +0 -0
  328. /package/template/.claude/commands/aioson/{qa.md → agent/qa.md} +0 -0
  329. /package/template/.claude/commands/aioson/{setup.md → agent/setup.md} +0 -0
  330. /package/template/.claude/commands/aioson/{ux-ui.md → agent/ux-ui.md} +0 -0
@@ -13,8 +13,14 @@ const {
13
13
  attachArtifact,
14
14
  upsertContentItem,
15
15
  getStatusSnapshot,
16
- logAgentEvent
16
+ logAgentEvent,
17
+ appendRunEvent,
18
+ readAgentSession,
19
+ clearAgentSession
17
20
  } = require('../runtime-store');
21
+ const { runAutoDelivery } = require('../delivery-runner');
22
+ const { writeHandoff, buildRuntimeLogHandoff } = require('../session-handoff');
23
+ const { backupAiosonDocs, isDocCreatingAgent } = require('../backup-local');
18
24
 
19
25
  const ALLOWED_LAYOUTS = new Set(['document', 'tabs', 'accordion', 'stack', 'mixed']);
20
26
  const DEFAULT_TEXT_FIELDS = ['content', 'text', 'body', 'lyrics', 'markdown'];
@@ -31,6 +37,152 @@ function requireOption(options, key, t) {
31
37
  return String(value).trim();
32
38
  }
33
39
 
40
+ function normalizeAgentHandle(value) {
41
+ const text = String(value || '').trim();
42
+ if (!text) return '';
43
+ return text.startsWith('@') ? text : `@${text}`;
44
+ }
45
+
46
+ function makeDirectSessionKey(agentName) {
47
+ return `direct-session:${Date.now()}:${String(agentName || '').replace(/^@/, '')}`;
48
+ }
49
+
50
+ function parseWatchSeconds(value) {
51
+ if (value === undefined || value === null || value === false) return null;
52
+ if (value === true || value === '') return 2;
53
+
54
+ const parsed = Number(value);
55
+ if (!Number.isFinite(parsed) || parsed <= 0) return 2;
56
+ return parsed;
57
+ }
58
+
59
+ function sleep(ms) {
60
+ return new Promise((resolve) => setTimeout(resolve, ms));
61
+ }
62
+
63
+ async function collectRuntimeSessionSnapshot(db, runtimeDir, agentName, options = {}) {
64
+ const normalizedAgent = normalizeAgentHandle(agentName);
65
+ const eventLimit = Math.max(1, Math.min(Number(options.limit) || 8, 20));
66
+ const session = await readAgentSession(runtimeDir, normalizedAgent);
67
+ const activeSession = session && !session.finished ? session : null;
68
+
69
+ let run = null;
70
+ if (activeSession && activeSession.runKey) {
71
+ run = db.prepare(`
72
+ SELECT
73
+ run_key, task_key, agent_name, agent_kind, squad_slug, session_key, source,
74
+ title, status, summary, output_path, started_at, updated_at, finished_at
75
+ FROM agent_runs
76
+ WHERE run_key = ?
77
+ LIMIT 1
78
+ `).get(activeSession.runKey);
79
+ }
80
+
81
+ if (!run) {
82
+ run = db.prepare(`
83
+ SELECT
84
+ run_key, task_key, agent_name, agent_kind, squad_slug, session_key, source,
85
+ title, status, summary, output_path, started_at, updated_at, finished_at
86
+ FROM agent_runs
87
+ WHERE agent_name = ?
88
+ ORDER BY updated_at DESC, started_at DESC
89
+ LIMIT 1
90
+ `).get(normalizedAgent);
91
+ }
92
+
93
+ const task = run && run.task_key
94
+ ? db.prepare(`
95
+ SELECT
96
+ task_key, squad_slug, session_key, title, goal, status, created_by, created_at, updated_at, finished_at
97
+ FROM tasks
98
+ WHERE task_key = ?
99
+ LIMIT 1
100
+ `).get(run.task_key)
101
+ : null;
102
+
103
+ const recentEvents = run
104
+ ? db.prepare(`
105
+ SELECT event_type, phase, status, message, created_at
106
+ FROM execution_events
107
+ WHERE run_key = ?
108
+ ORDER BY created_at DESC, id DESC
109
+ LIMIT ?
110
+ `).all(run.run_key, eventLimit).reverse()
111
+ : [];
112
+
113
+ const open = Boolean(activeSession && run && (run.status === 'running' || run.status === 'queued'));
114
+ const state = open ? 'open' : (run ? 'closed' : 'idle');
115
+
116
+ return {
117
+ agent: normalizedAgent,
118
+ state,
119
+ open,
120
+ sessionKey: activeSession?.sessionKey || run?.session_key || task?.session_key || null,
121
+ startedAt: activeSession?.startedAt || run?.started_at || task?.created_at || null,
122
+ updatedAt: run?.updated_at || task?.updated_at || null,
123
+ session: activeSession,
124
+ run,
125
+ task,
126
+ recentEvents
127
+ };
128
+ }
129
+
130
+ async function getRuntimeSessionSnapshot(targetDir, agentName, t, options = {}) {
131
+ const { dbPath, runtimeDir } = resolveRuntimePaths(targetDir);
132
+
133
+ if (!(await runtimeStoreExists(targetDir))) {
134
+ throw new Error(t('runtime.store_missing', { path: dbPath }));
135
+ }
136
+
137
+ const { db } = await openRuntimeDb(targetDir, { mustExist: true });
138
+ try {
139
+ const snapshot = await collectRuntimeSessionSnapshot(db, runtimeDir, agentName, options);
140
+ return {
141
+ ok: true,
142
+ targetDir,
143
+ dbPath,
144
+ ...snapshot
145
+ };
146
+ } finally {
147
+ db.close();
148
+ }
149
+ }
150
+
151
+ function printRuntimeSessionSnapshot(snapshot, logger) {
152
+ logger.log(`Direct session: ${snapshot.agent}`);
153
+ logger.log(`State: ${snapshot.state}`);
154
+
155
+ if (snapshot.sessionKey) {
156
+ logger.log(`Session: ${snapshot.sessionKey}`);
157
+ }
158
+
159
+ if (snapshot.task) {
160
+ logger.log(`Task: ${snapshot.task.task_key} | status: ${snapshot.task.status} | work: ${snapshot.task.title || '—'}`);
161
+ }
162
+
163
+ if (snapshot.run) {
164
+ logger.log(`Run: ${snapshot.run.run_key} | status: ${snapshot.run.status} | work: ${snapshot.run.title || snapshot.run.summary || '—'}`);
165
+ }
166
+
167
+ if (snapshot.startedAt) {
168
+ logger.log(`Started: ${snapshot.startedAt}`);
169
+ }
170
+
171
+ if (snapshot.updatedAt) {
172
+ logger.log(`Updated: ${snapshot.updatedAt}`);
173
+ }
174
+
175
+ if (snapshot.recentEvents.length === 0) {
176
+ logger.log('Recent events: none');
177
+ return;
178
+ }
179
+
180
+ logger.log('Recent events:');
181
+ for (const event of snapshot.recentEvents) {
182
+ logger.log(`- ${event.created_at} | ${event.event_type} | ${event.message || '—'}`);
183
+ }
184
+ }
185
+
34
186
  async function readJsonIfExists(filePath) {
35
187
  try {
36
188
  const raw = await fs.readFile(filePath, 'utf8');
@@ -418,6 +570,14 @@ async function ingestContentCandidate(db, targetDir, absolutePath, options = {})
418
570
  createdByAgent: options.agent || content.createdByAgent || content.created_by_agent || null
419
571
  });
420
572
 
573
+ // Fire auto-delivery if configured (non-blocking)
574
+ runAutoDelivery(db, {
575
+ projectDir: targetDir,
576
+ squadSlug,
577
+ contentKey: content.contentKey,
578
+ contentPayload: content
579
+ }).catch(() => {}); // Swallow errors — delivery failure should not break ingestion
580
+
421
581
  return { indexed: true, kind: 'content-json', contentKey: content.contentKey };
422
582
  }
423
583
 
@@ -458,6 +618,14 @@ async function ingestContentCandidate(db, targetDir, absolutePath, options = {})
458
618
  createdByAgent: options.agent || null
459
619
  });
460
620
 
621
+ // Fire auto-delivery if configured (non-blocking)
622
+ runAutoDelivery(db, {
623
+ projectDir: targetDir,
624
+ squadSlug,
625
+ contentKey: payload.contentKey,
626
+ contentPayload: payload
627
+ }).catch(() => {}); // Swallow errors — delivery failure should not break ingestion
628
+
461
629
  return { indexed: true, kind: path.extname(absolutePath).toLowerCase(), contentKey: payload.contentKey };
462
630
  }
463
631
 
@@ -820,6 +988,11 @@ async function runRuntimeStatus({ args, options = {}, logger, t }) {
820
988
  recentTasks: snapshot.recentTasks,
821
989
  activeRuns: snapshot.activeRuns,
822
990
  recentRuns: snapshot.recentRuns,
991
+ activeLiveSessions: snapshot.activeLiveSessions,
992
+ activeMicroTasks: snapshot.activeMicroTasks,
993
+ recentLiveSessions: snapshot.recentLiveSessions,
994
+ recentMicroTasks: snapshot.recentMicroTasks,
995
+ recentHandoffs: snapshot.recentHandoffs,
823
996
  recentArtifacts: snapshot.recentArtifacts,
824
997
  recentContentItems: snapshot.recentContentItems,
825
998
  recentExecutionEvents: snapshot.recentExecutionEvents
@@ -874,6 +1047,49 @@ async function runRuntimeStatus({ args, options = {}, logger, t }) {
874
1047
  );
875
1048
  }
876
1049
  }
1050
+ if (snapshot.activeLiveSessions.length > 0) {
1051
+ logger.log(t('runtime.status_live_sessions_title'));
1052
+ for (const task of snapshot.activeLiveSessions) {
1053
+ logger.log(
1054
+ t('runtime.status_live_session_line', {
1055
+ task: task.task_key,
1056
+ agent: task.latest_agent_name || task.created_by || '—',
1057
+ status: task.status,
1058
+ plan: task.plan_steps_total > 0 ? `${task.plan_steps_done}/${task.plan_steps_total}` : '—',
1059
+ micro: `${task.completed_child_task_count || 0}/${task.child_task_count || 0}`,
1060
+ handoffs: task.handoff_count || 0,
1061
+ title: task.title || '—'
1062
+ })
1063
+ );
1064
+ }
1065
+ }
1066
+ if (snapshot.activeMicroTasks.length > 0) {
1067
+ logger.log(t('runtime.status_micro_tasks_title'));
1068
+ for (const task of snapshot.activeMicroTasks) {
1069
+ logger.log(
1070
+ t('runtime.status_micro_task_line', {
1071
+ task: task.task_key,
1072
+ parent: task.parent_task_key || '—',
1073
+ status: task.status,
1074
+ title: task.title || task.goal || '—'
1075
+ })
1076
+ );
1077
+ }
1078
+ }
1079
+ if (snapshot.recentHandoffs.length > 0) {
1080
+ logger.log(t('runtime.status_handoffs_title'));
1081
+ for (const event of snapshot.recentHandoffs.slice(0, 5)) {
1082
+ logger.log(
1083
+ t('runtime.status_handoff_line', {
1084
+ created: event.created_at,
1085
+ from: event.handoff_from || event.agent_name || '—',
1086
+ to: event.handoff_to || '—',
1087
+ session: event.session_key || '—',
1088
+ message: event.message || '—'
1089
+ })
1090
+ );
1091
+ }
1092
+ }
877
1093
  }
878
1094
 
879
1095
  return payload;
@@ -911,6 +1127,16 @@ async function runRuntimeLog({ args, options = {}, logger, t }) {
911
1127
  meta: options.meta ? (() => { try { return JSON.parse(options.meta); } catch { return { raw: options.meta }; } })() : undefined
912
1128
  });
913
1129
 
1130
+ // Generate session handoff on --finish
1131
+ if (options.finish) {
1132
+ const handoffData = buildRuntimeLogHandoff(
1133
+ agentName,
1134
+ options.message || '',
1135
+ options.summary || ''
1136
+ );
1137
+ await writeHandoff(targetDir, handoffData);
1138
+ }
1139
+
914
1140
  if (!options.json) {
915
1141
  const isFinish = Boolean(options.finish);
916
1142
  logger.log(isFinish
@@ -933,6 +1159,842 @@ async function runRuntimeLog({ args, options = {}, logger, t }) {
933
1159
  }
934
1160
  }
935
1161
 
1162
+
1163
+ /**
1164
+ * aioson agent:done . --agent=<name> --summary="..." [--title="..."] [--status=completed|failed]
1165
+ *
1166
+ * Safe self-registration for official agents invoked directly (not via workflow:next or live:start).
1167
+ * - If an active live session exists for the agent: appends a completion event without closing the session.
1168
+ * - If no session exists: creates a standalone task+run and immediately marks it completed.
1169
+ *
1170
+ * Intended to be called ONCE at the very end of an agent session, after delivering the main artifact.
1171
+ */
1172
+ async function runAgentDone({ args, options = {}, logger, t }) {
1173
+ const targetDir = resolveTargetDir(args);
1174
+ const agentName = String(options.agent || '').trim();
1175
+ if (!agentName) {
1176
+ throw new Error('--agent is required');
1177
+ }
1178
+ const normalizedAgent = agentName.startsWith('@') ? agentName : `@${agentName}`;
1179
+ const summary = String(options.summary || options.message || `${normalizedAgent} session completed`).trim();
1180
+ const title = options.title ? String(options.title).trim() : null;
1181
+ const status = options.status || 'completed';
1182
+
1183
+ const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
1184
+
1185
+ try {
1186
+ const session = await readAgentSession(runtimeDir, normalizedAgent);
1187
+ const hasActiveSession = session && !session.finished && session.runKey;
1188
+
1189
+ if (hasActiveSession) {
1190
+ // Live or tracked session is already open — only append a completion note.
1191
+ // Do NOT close the session: live:handoff or live:close owns the lifecycle.
1192
+ appendRunEvent(db, {
1193
+ runKey: session.runKey,
1194
+ eventType: 'agent_done',
1195
+ phase: 'live',
1196
+ status: 'running',
1197
+ message: summary
1198
+ });
1199
+
1200
+ if (!options.json) {
1201
+ logger.log(`agent:done — ${normalizedAgent} | live session active, event logged | run: ${session.runKey} (${dbPath})`);
1202
+ }
1203
+
1204
+ if (isDocCreatingAgent(normalizedAgent)) {
1205
+ backupAiosonDocs(targetDir).catch(() => {});
1206
+ }
1207
+
1208
+ return { ok: true, targetDir, dbPath, agent: normalizedAgent, mode: 'live_event', runKey: session.runKey };
1209
+ }
1210
+
1211
+ // No active session — create a standalone task+run and immediately complete it.
1212
+ const { runKey, taskKey } = await logAgentEvent(db, runtimeDir, {
1213
+ agentName: normalizedAgent,
1214
+ message: summary,
1215
+ type: 'completed',
1216
+ taskTitle: title || normalizedAgent,
1217
+ finish: true,
1218
+ status,
1219
+ summary
1220
+ });
1221
+
1222
+ if (!options.json) {
1223
+ logger.log(`agent:done — ${normalizedAgent} | task: ${taskKey} | run: ${runKey} (${dbPath})`);
1224
+ }
1225
+
1226
+ if (isDocCreatingAgent(normalizedAgent)) {
1227
+ backupAiosonDocs(targetDir).catch(() => {});
1228
+ }
1229
+
1230
+ return { ok: true, targetDir, dbPath, agent: normalizedAgent, mode: 'standalone', runKey, taskKey };
1231
+ } finally {
1232
+ db.close();
1233
+ }
1234
+ }
1235
+
1236
+
1237
+ async function runRuntimeSessionStart({ args, options = {}, logger, t }) {
1238
+ const targetDir = resolveTargetDir(args);
1239
+ const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
1240
+
1241
+ try {
1242
+ const agentName = normalizeAgentHandle(requireOption(options, 'agent', t));
1243
+ const existingSnapshot = await collectRuntimeSessionSnapshot(db, runtimeDir, agentName, { limit: options.limit });
1244
+
1245
+ if (existingSnapshot.session && !existingSnapshot.open) {
1246
+ await clearAgentSession(runtimeDir, agentName);
1247
+ }
1248
+
1249
+ if (existingSnapshot.open) {
1250
+ if (!options.json) {
1251
+ logger.log(`Direct session already active: ${agentName} | task: ${existingSnapshot.task?.task_key || '—'} | run: ${existingSnapshot.run?.run_key || '—'} (${dbPath})`);
1252
+ }
1253
+ return {
1254
+ ok: true,
1255
+ targetDir,
1256
+ dbPath,
1257
+ agent: agentName,
1258
+ taskKey: existingSnapshot.task?.task_key || existingSnapshot.session?.taskKey || null,
1259
+ runKey: existingSnapshot.run?.run_key || existingSnapshot.session?.runKey || null,
1260
+ sessionKey: existingSnapshot.sessionKey,
1261
+ status: existingSnapshot.run?.status || 'running',
1262
+ reused: true,
1263
+ open: true
1264
+ };
1265
+ }
1266
+
1267
+ const sessionKey = options.session ? String(options.session).trim() : makeDirectSessionKey(agentName);
1268
+ const title = options.title ? String(options.title).trim() : `Direct session ${agentName}`;
1269
+ const message = options.message ? String(options.message).trim() : `Session started for ${agentName}`;
1270
+ const { runKey, taskKey } = await logAgentEvent(db, runtimeDir, {
1271
+ agentName,
1272
+ message,
1273
+ type: options.type || 'session.start',
1274
+ taskTitle: title,
1275
+ sessionKey,
1276
+ meta: options.meta ? (() => { try { return JSON.parse(options.meta); } catch { return { raw: options.meta }; } })() : undefined
1277
+ });
1278
+
1279
+ if (!options.json) {
1280
+ logger.log(`Direct session started: ${agentName} | task: ${taskKey} | run: ${runKey} (${dbPath})`);
1281
+ }
1282
+
1283
+ return {
1284
+ ok: true,
1285
+ targetDir,
1286
+ dbPath,
1287
+ agent: agentName,
1288
+ taskKey,
1289
+ runKey,
1290
+ sessionKey,
1291
+ status: 'running',
1292
+ reused: false,
1293
+ open: true
1294
+ };
1295
+ } finally {
1296
+ db.close();
1297
+ }
1298
+ }
1299
+
1300
+ async function runRuntimeSessionLog({ args, options = {}, logger, t }) {
1301
+ const targetDir = resolveTargetDir(args);
1302
+ const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
1303
+
1304
+ try {
1305
+ const agentName = normalizeAgentHandle(requireOption(options, 'agent', t));
1306
+ const message = requireOption(options, 'message', t);
1307
+ const existingSnapshot = await collectRuntimeSessionSnapshot(db, runtimeDir, agentName, { limit: options.limit });
1308
+
1309
+ if (existingSnapshot.session && !existingSnapshot.open) {
1310
+ await clearAgentSession(runtimeDir, agentName);
1311
+ }
1312
+
1313
+ const autoStarted = !existingSnapshot.open;
1314
+ const sessionKey = existingSnapshot.sessionKey || (options.session ? String(options.session).trim() : makeDirectSessionKey(agentName));
1315
+ const title = options.title ? String(options.title).trim() : `Direct session ${agentName}`;
1316
+ const { runKey, taskKey } = await logAgentEvent(db, runtimeDir, {
1317
+ agentName,
1318
+ message,
1319
+ type: options.type || 'session.log',
1320
+ taskTitle: title,
1321
+ sessionKey,
1322
+ meta: options.meta ? (() => { try { return JSON.parse(options.meta); } catch { return { raw: options.meta }; } })() : undefined
1323
+ });
1324
+
1325
+ if (!options.json) {
1326
+ logger.log(`Direct session log recorded: ${agentName} | run: ${runKey} (${dbPath})`);
1327
+ }
1328
+
1329
+ return {
1330
+ ok: true,
1331
+ targetDir,
1332
+ dbPath,
1333
+ agent: agentName,
1334
+ taskKey,
1335
+ runKey,
1336
+ sessionKey,
1337
+ status: 'running',
1338
+ autoStarted,
1339
+ open: true
1340
+ };
1341
+ } finally {
1342
+ db.close();
1343
+ }
1344
+ }
1345
+
1346
+ async function runRuntimeSessionFinish({ args, options = {}, logger, t }) {
1347
+ const targetDir = resolveTargetDir(args);
1348
+ const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
1349
+
1350
+ try {
1351
+ const agentName = normalizeAgentHandle(requireOption(options, 'agent', t));
1352
+ const existingSnapshot = await collectRuntimeSessionSnapshot(db, runtimeDir, agentName, { limit: options.limit });
1353
+
1354
+ if (!existingSnapshot.open) {
1355
+ throw new Error(`No active direct session for ${agentName}.`);
1356
+ }
1357
+
1358
+ const summary = options.summary ? String(options.summary).trim() : '';
1359
+ const message = options.message ? String(options.message).trim() : (summary || `Session finished for ${agentName}`);
1360
+ const { runKey, taskKey } = await logAgentEvent(db, runtimeDir, {
1361
+ agentName,
1362
+ message,
1363
+ type: options.type || 'session.finish',
1364
+ finish: true,
1365
+ status: options.status || 'completed',
1366
+ summary,
1367
+ meta: options.meta ? (() => { try { return JSON.parse(options.meta); } catch { return { raw: options.meta }; } })() : undefined
1368
+ });
1369
+
1370
+ if (!options.json) {
1371
+ logger.log(`Direct session finished: ${agentName} | run: ${runKey} (${dbPath})`);
1372
+ }
1373
+
1374
+ return {
1375
+ ok: true,
1376
+ targetDir,
1377
+ dbPath,
1378
+ agent: agentName,
1379
+ taskKey,
1380
+ runKey,
1381
+ sessionKey: existingSnapshot.sessionKey,
1382
+ status: options.status || 'completed',
1383
+ finished: true,
1384
+ open: false
1385
+ };
1386
+ } finally {
1387
+ db.close();
1388
+ }
1389
+ }
1390
+
1391
+ async function runRuntimeSessionStatus({ args, options = {}, logger, t }) {
1392
+ const targetDir = resolveTargetDir(args);
1393
+ const agentName = normalizeAgentHandle(requireOption(options, 'agent', t));
1394
+ const watchSeconds = parseWatchSeconds(options.watch);
1395
+
1396
+ if (watchSeconds && options.json) {
1397
+ throw new Error('--watch cannot be combined with --json.');
1398
+ }
1399
+
1400
+ if (!watchSeconds) {
1401
+ const snapshot = await getRuntimeSessionSnapshot(targetDir, agentName, t, { limit: options.limit });
1402
+ if (!options.json) {
1403
+ printRuntimeSessionSnapshot(snapshot, logger);
1404
+ }
1405
+ return snapshot;
1406
+ }
1407
+
1408
+ while (true) {
1409
+ const snapshot = await getRuntimeSessionSnapshot(targetDir, agentName, t, { limit: options.limit });
1410
+ if (process.stdout && process.stdout.isTTY) {
1411
+ process.stdout.write('\x1Bc');
1412
+ }
1413
+ printRuntimeSessionSnapshot(snapshot, logger);
1414
+ await sleep(Math.round(watchSeconds * 1000));
1415
+ }
1416
+ }
1417
+
1418
+ async function runDeliver({ args, options = {}, logger, t }) {
1419
+ const targetDir = resolveTargetDir(args);
1420
+ const squadSlug = requireOption(options, 'squad', t);
1421
+ const contentKey = options['content-key'] || options.contentKey || null;
1422
+ const triggerType = options.trigger || 'manual';
1423
+
1424
+ const { db, dbPath } = await withRuntimeDb(targetDir, t);
1425
+
1426
+ try {
1427
+ const { runManualDelivery } = require('../delivery-runner');
1428
+
1429
+ // Optionally load content payload from DB
1430
+ let contentPayload = null;
1431
+ if (contentKey) {
1432
+ const row = db.prepare('SELECT payload_json FROM content_items WHERE content_key = ? AND squad_slug = ?').get(contentKey, squadSlug);
1433
+ if (row && row.payload_json) {
1434
+ try { contentPayload = JSON.parse(row.payload_json); } catch { /* ignore */ }
1435
+ }
1436
+ }
1437
+
1438
+ const result = await runManualDelivery(db, {
1439
+ projectDir: targetDir,
1440
+ squadSlug,
1441
+ contentKey,
1442
+ triggerType,
1443
+ contentPayload
1444
+ });
1445
+
1446
+ if (!result.delivered) {
1447
+ logger.log(`Delivery skipped: ${result.reason}`);
1448
+ return { ok: false, ...result };
1449
+ }
1450
+
1451
+ for (const r of result.results || []) {
1452
+ const status = r.ok ? 'OK' : 'FAIL';
1453
+ logger.log(` ${status} ${r.webhookSlug} — ${r.statusCode || 'no response'} (${r.attempts} attempt${r.attempts > 1 ? 's' : ''})`);
1454
+ if (r.error) logger.log(` Error: ${r.error}`);
1455
+ }
1456
+
1457
+ logger.log(`\nDelivery ${result.allOk ? 'completed' : 'completed with errors'}.`);
1458
+ return { ok: result.allOk, ...result };
1459
+ } finally {
1460
+ db.close();
1461
+ }
1462
+ }
1463
+
1464
+ async function findManifestPath(projectDir, slug) {
1465
+ const candidates = [
1466
+ path.join(projectDir, '.aioson', 'squads', slug, 'squad.manifest.json'),
1467
+ path.join(projectDir, 'agents', slug, 'squad.manifest.json')
1468
+ ];
1469
+ for (const p of candidates) {
1470
+ try { await fs.stat(p); return p; } catch { continue; }
1471
+ }
1472
+ return null;
1473
+ }
1474
+
1475
+ async function runOutputStrategyExport({ args, options = {}, logger, t }) {
1476
+ const projectDir = resolveTargetDir(args);
1477
+ const slug = requireOption(options, 'squad', t);
1478
+ const manifestPath = await findManifestPath(projectDir, slug);
1479
+
1480
+ if (!manifestPath) {
1481
+ logger.error(`Manifest not found for squad "${slug}"`);
1482
+ return { ok: false, error: 'Manifest not found' };
1483
+ }
1484
+
1485
+ const raw = await fs.readFile(manifestPath, 'utf8');
1486
+ const manifest = JSON.parse(raw);
1487
+ const strategy = manifest.outputStrategy || null;
1488
+
1489
+ if (!strategy) {
1490
+ logger.log(`Squad "${slug}" has no outputStrategy configured.`);
1491
+ return { ok: false, error: 'No outputStrategy found' };
1492
+ }
1493
+
1494
+ const exportsDir = path.join(projectDir, '.aioson', 'squads', 'exports');
1495
+ await fs.mkdir(exportsDir, { recursive: true });
1496
+ const outFile = path.join(exportsDir, `${slug}.output-strategy.json`);
1497
+ await fs.writeFile(outFile, JSON.stringify(strategy, null, 2) + '\n', 'utf8');
1498
+
1499
+ const relOut = path.relative(projectDir, outFile).replace(/\\/g, '/');
1500
+ logger.log(`Exported outputStrategy from "${slug}" → ${relOut}`);
1501
+ return { ok: true, file: relOut, strategy };
1502
+ }
1503
+
1504
+ async function runOutputStrategyImport({ args, options = {}, logger, t }) {
1505
+ const projectDir = resolveTargetDir(args);
1506
+ const slug = requireOption(options, 'squad', t);
1507
+ const fromSlug = options.from || null;
1508
+ const fromFile = options.file || null;
1509
+
1510
+ if (!fromSlug && !fromFile) {
1511
+ logger.error('Usage: aioson output-strategy:import --squad=<target> --from=<source-slug> | --file=<path>');
1512
+ return { ok: false, error: 'Provide --from or --file' };
1513
+ }
1514
+
1515
+ // Load source strategy
1516
+ let strategy;
1517
+ if (fromFile) {
1518
+ const absFile = path.resolve(projectDir, fromFile);
1519
+ const raw = await fs.readFile(absFile, 'utf8');
1520
+ strategy = JSON.parse(raw);
1521
+ } else {
1522
+ const srcPath = await findManifestPath(projectDir, fromSlug);
1523
+ if (!srcPath) {
1524
+ logger.error(`Source squad "${fromSlug}" manifest not found`);
1525
+ return { ok: false, error: 'Source manifest not found' };
1526
+ }
1527
+ const srcManifest = JSON.parse(await fs.readFile(srcPath, 'utf8'));
1528
+ strategy = srcManifest.outputStrategy || null;
1529
+ if (!strategy) {
1530
+ logger.error(`Source squad "${fromSlug}" has no outputStrategy`);
1531
+ return { ok: false, error: 'Source has no outputStrategy' };
1532
+ }
1533
+ }
1534
+
1535
+ // Write to target
1536
+ const targetPath = await findManifestPath(projectDir, slug);
1537
+ if (!targetPath) {
1538
+ logger.error(`Target squad "${slug}" manifest not found`);
1539
+ return { ok: false, error: 'Target manifest not found' };
1540
+ }
1541
+
1542
+ const targetManifest = JSON.parse(await fs.readFile(targetPath, 'utf8'));
1543
+ targetManifest.outputStrategy = strategy;
1544
+ await fs.writeFile(targetPath, JSON.stringify(targetManifest, null, 2) + '\n', 'utf8');
1545
+
1546
+ logger.log(`Imported outputStrategy into "${slug}" from ${fromSlug || fromFile}`);
1547
+ return { ok: true, squad: slug, source: fromSlug || fromFile };
1548
+ }
1549
+
1550
+ /**
1551
+ * aioson devlog:sync [targetDir]
1552
+ *
1553
+ * Parses aioson-logs/devlog-*.md files, imports them into SQLite as
1554
+ * task + run + events, then renames each file to .synced so it is not
1555
+ * re-imported on subsequent runs.
1556
+ */
1557
+ async function runDevlogSync({ args, options = {}, logger, t }) {
1558
+ const targetDir = resolveTargetDir(args);
1559
+ const logsDir = path.join(targetDir, 'aioson-logs');
1560
+
1561
+ let entries;
1562
+ try {
1563
+ entries = await fs.readdir(logsDir);
1564
+ } catch {
1565
+ logger.log('No aioson-logs/ directory found — nothing to sync.');
1566
+ return { ok: true, synced: 0 };
1567
+ }
1568
+
1569
+ const devlogFiles = entries
1570
+ .filter(f => f.startsWith('devlog-') && f.endsWith('.md'))
1571
+ .sort();
1572
+
1573
+ if (devlogFiles.length === 0) {
1574
+ logger.log('No devlog files to sync.');
1575
+ return { ok: true, synced: 0 };
1576
+ }
1577
+
1578
+ const { db, dbPath } = await openRuntimeDb(targetDir);
1579
+ let synced = 0;
1580
+ const parsedDevlogs = [];
1581
+
1582
+ try {
1583
+ for (const file of devlogFiles) {
1584
+ const filePath = path.join(logsDir, file);
1585
+ const raw = await fs.readFile(filePath, 'utf8');
1586
+
1587
+ // Parse YAML frontmatter
1588
+ const fm = parseFrontmatter(raw);
1589
+ const agent = fm.agent || 'unknown';
1590
+ const summary = fm.summary || file;
1591
+ const sessionStart = fm.session_start || null;
1592
+ const sessionEnd = fm.session_end || null;
1593
+ const status = fm.status || 'completed';
1594
+ const body = raw.replace(/^---[\s\S]*?---\s*/, '');
1595
+
1596
+ parsedDevlogs.push({ filename: file, agent, summary, sessionStart, sessionEnd, status, body });
1597
+
1598
+ // Create task + run
1599
+ const taskKey = startTask(db, {
1600
+ title: `devlog: ${summary}`,
1601
+ squadSlug: null,
1602
+ status: status === 'partial' ? 'running' : 'completed',
1603
+ createdBy: agent
1604
+ });
1605
+
1606
+ const runKey = startRun(db, {
1607
+ taskKey,
1608
+ agentName: agent,
1609
+ agentKind: 'devlog',
1610
+ squadSlug: null,
1611
+ title: `@${agent} devlog`,
1612
+ message: summary
1613
+ });
1614
+
1615
+ // Extract body sections as events
1616
+ const sections = body.split(/^## /m).filter(Boolean);
1617
+ for (const section of sections) {
1618
+ const firstLine = section.split('\n')[0].trim();
1619
+ const content = section.slice(firstLine.length).trim();
1620
+ if (content) {
1621
+ appendRunEvent(db, {
1622
+ runKey,
1623
+ eventType: 'devlog',
1624
+ phase: firstLine.toLowerCase().replace(/\s+/g, '_'),
1625
+ status: 'completed',
1626
+ message: `## ${firstLine}\n${content}`,
1627
+ createdAt: sessionEnd || new Date().toISOString()
1628
+ });
1629
+ }
1630
+ }
1631
+
1632
+ // Close the run
1633
+ updateRun(db, runKey, {
1634
+ status: status === 'partial' ? 'running' : 'completed',
1635
+ summary,
1636
+ finishedAt: sessionEnd || new Date().toISOString()
1637
+ });
1638
+
1639
+ if (status !== 'partial') {
1640
+ updateTask(db, taskKey, {
1641
+ status: 'completed',
1642
+ finishedAt: sessionEnd || new Date().toISOString()
1643
+ });
1644
+ }
1645
+
1646
+ // Rename to .synced
1647
+ await fs.rename(filePath, filePath.replace(/\.md$/, '.synced.md'));
1648
+ synced++;
1649
+ logger.log(` Synced: ${file} → task=${taskKey} run=${runKey}`);
1650
+ }
1651
+
1652
+ logger.log(`Synced ${synced} devlog(s) into ${dbPath}`);
1653
+
1654
+ // Cloud sync
1655
+ if (options.cloud) {
1656
+ const cloudResult = await syncDevlogsToCloud(targetDir, parsedDevlogs, options, logger);
1657
+ return { ok: true, synced, dbPath, cloud: cloudResult };
1658
+ }
1659
+
1660
+ return { ok: true, synced, dbPath };
1661
+ } finally {
1662
+ db.close();
1663
+ }
1664
+ }
1665
+
1666
+ /**
1667
+ * Sends parsed devlogs to the cloud endpoint.
1668
+ * Reads cloud config from .aioson/install.json or --url / --token options.
1669
+ */
1670
+ async function syncDevlogsToCloud(targetDir, devlogs, options, logger) {
1671
+ const cloudUrl = options.url || options['cloud-url'] || await resolveCloudUrl(targetDir);
1672
+ const cloudToken = options.token || options['cloud-token'] || await resolveCloudToken(targetDir);
1673
+
1674
+ if (!cloudUrl) {
1675
+ logger.error('Cloud URL not configured. Use --url or set cloudBaseUrl in dashboard project settings.');
1676
+ return { ok: false, error: 'missing_cloud_url' };
1677
+ }
1678
+ if (!cloudToken) {
1679
+ logger.error('Cloud token not configured. Use --token or set cloudApiToken in dashboard project settings.');
1680
+ return { ok: false, error: 'missing_cloud_token' };
1681
+ }
1682
+
1683
+ const endpoint = `${cloudUrl.replace(/\/+$/, '')}/api/publish/runtime`;
1684
+ const payload = {
1685
+ tasks: [],
1686
+ devlogs: devlogs.map(d => ({
1687
+ filename: d.filename,
1688
+ agent: d.agent,
1689
+ sessionStart: d.sessionStart,
1690
+ sessionEnd: d.sessionEnd,
1691
+ status: d.status,
1692
+ summary: d.summary,
1693
+ body: d.body
1694
+ }))
1695
+ };
1696
+
1697
+ logger.log(` Pushing ${devlogs.length} devlog(s) to ${endpoint}...`);
1698
+
1699
+ const response = await fetch(endpoint, {
1700
+ method: 'POST',
1701
+ headers: {
1702
+ 'accept': 'application/json',
1703
+ 'content-type': 'application/json',
1704
+ 'authorization': `Bearer ${cloudToken}`
1705
+ },
1706
+ body: JSON.stringify(payload),
1707
+ signal: AbortSignal.timeout(15000)
1708
+ });
1709
+
1710
+ const text = await response.text();
1711
+ let result;
1712
+ try { result = JSON.parse(text); } catch { result = { ok: false, error: text }; }
1713
+
1714
+ if (result.ok) {
1715
+ logger.log(` Cloud sync OK: ${result.devlogsStored || 0} devlog(s) stored.`);
1716
+ } else {
1717
+ logger.error(` Cloud sync failed: ${result.error || response.status}`);
1718
+ }
1719
+
1720
+ return result;
1721
+ }
1722
+
1723
+ async function resolveCloudUrl(targetDir) {
1724
+ try {
1725
+ const raw = await fs.readFile(path.join(targetDir, '.aioson/install.json'), 'utf8');
1726
+ const meta = JSON.parse(raw);
1727
+ return meta.cloudBaseUrl || null;
1728
+ } catch { return null; }
1729
+ }
1730
+
1731
+ async function resolveCloudToken(targetDir) {
1732
+ try {
1733
+ const raw = await fs.readFile(path.join(targetDir, '.aioson/install.json'), 'utf8');
1734
+ const meta = JSON.parse(raw);
1735
+ return meta.cloudApiToken || null;
1736
+ } catch { return null; }
1737
+ }
1738
+
1739
+ /**
1740
+ * Minimal YAML frontmatter parser (no external deps).
1741
+ * Returns an object with frontmatter keys, or {} if none.
1742
+ */
1743
+ function parseFrontmatter(content) {
1744
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
1745
+ if (!match) return {};
1746
+ const result = {};
1747
+ for (const line of match[1].split('\n')) {
1748
+ const idx = line.indexOf(':');
1749
+ if (idx === -1) continue;
1750
+ const key = line.slice(0, idx).trim();
1751
+ let val = line.slice(idx + 1).trim();
1752
+ // Strip surrounding quotes
1753
+ if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
1754
+ val = val.slice(1, -1);
1755
+ }
1756
+ result[key] = val;
1757
+ }
1758
+ return result;
1759
+ }
1760
+
1761
+ /**
1762
+ * Parses a duration string like "24h", "30m", "7d" into milliseconds.
1763
+ * Falls back to treating the raw value as hours.
1764
+ */
1765
+ function parseDurationMs(value, defaultHours = 24) {
1766
+ const text = String(value || '').trim().toLowerCase();
1767
+ if (!text) return defaultHours * 60 * 60 * 1000;
1768
+
1769
+ const match = text.match(/^(\d+(?:\.\d+)?)\s*([hmd]?)$/);
1770
+ if (!match) return defaultHours * 60 * 60 * 1000;
1771
+
1772
+ const n = parseFloat(match[1]);
1773
+ const unit = match[2] || 'h';
1774
+ if (unit === 'd') return n * 24 * 60 * 60 * 1000;
1775
+ if (unit === 'm') return n * 60 * 1000;
1776
+ return n * 60 * 60 * 1000; // hours (default)
1777
+ }
1778
+
1779
+ /**
1780
+ * aioson agent:recover [targetDir] [--older-than=<duration>] [--dry-run]
1781
+ *
1782
+ * Detects and closes agent sessions that were abandoned (Claude Code closed before
1783
+ * agent:done was called, or live:start session was never closed).
1784
+ *
1785
+ * Sources checked:
1786
+ * 1. Session files in .aioson/.sessions/ with finished=false older than threshold.
1787
+ * 2. agent_runs rows with status='running'/'queued' and started_at older than threshold
1788
+ * that have no corresponding live session file (orphaned DB records).
1789
+ *
1790
+ * --older-than Duration threshold. Accepts: 24h (default), 8h, 30m, 7d.
1791
+ * --dry-run Report what would be recovered without making any changes.
1792
+ * --json Output JSON result.
1793
+ */
1794
+ async function runAgentRecover({ args, options = {}, logger }) {
1795
+ const targetDir = resolveTargetDir(args);
1796
+ const dryRun = Boolean(options['dry-run'] || options.dryRun);
1797
+ const olderThanMs = parseDurationMs(options['older-than'] || options.olderThan, 24);
1798
+ const cutoffMs = Date.now() - olderThanMs;
1799
+ const cutoffIso = new Date(cutoffMs).toISOString();
1800
+ const now = new Date().toISOString();
1801
+
1802
+ const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
1803
+
1804
+ const recovered = [];
1805
+ const skipped = [];
1806
+
1807
+ try {
1808
+ // ── 1. Scan session files ─────────────────────────────────────────────────
1809
+ const sessionsDir = path.join(runtimeDir, '.sessions');
1810
+ let sessionFiles = [];
1811
+ try {
1812
+ const entries = await fs.readdir(sessionsDir);
1813
+ sessionFiles = entries.filter((f) => f.endsWith('.json'));
1814
+ } catch {
1815
+ // .sessions dir may not exist — that's fine
1816
+ }
1817
+
1818
+ for (const file of sessionFiles) {
1819
+ const filePath = path.join(sessionsDir, file);
1820
+ let session;
1821
+ try {
1822
+ session = JSON.parse(await fs.readFile(filePath, 'utf8'));
1823
+ } catch {
1824
+ continue;
1825
+ }
1826
+
1827
+ if (session.finished) continue;
1828
+
1829
+ const startedAt = session.startedAt ? new Date(session.startedAt).getTime() : 0;
1830
+ if (startedAt > cutoffMs) {
1831
+ skipped.push({ source: 'session_file', file, reason: 'within_threshold', startedAt: session.startedAt });
1832
+ continue;
1833
+ }
1834
+
1835
+ const agentName = file.replace(/\.json$/, '');
1836
+ const runKey = session.runKey || null;
1837
+ const taskKey = session.taskKey || null;
1838
+
1839
+ if (!dryRun) {
1840
+ // Mark run as abandoned
1841
+ if (runKey) {
1842
+ const runRow = db.prepare('SELECT run_key, status FROM agent_runs WHERE run_key = ?').get(runKey);
1843
+ if (runRow && (runRow.status === 'running' || runRow.status === 'queued')) {
1844
+ db.prepare(`
1845
+ UPDATE agent_runs
1846
+ SET status = 'abandoned', summary = 'Recovered: session abandoned without close', updated_at = ?, finished_at = ?
1847
+ WHERE run_key = ?
1848
+ `).run(now, now, runKey);
1849
+ }
1850
+ }
1851
+ // Mark task as abandoned
1852
+ if (taskKey) {
1853
+ const taskRow = db.prepare('SELECT task_key, status FROM tasks WHERE task_key = ?').get(taskKey);
1854
+ if (taskRow && (taskRow.status === 'running' || taskRow.status === 'queued')) {
1855
+ db.prepare(`
1856
+ UPDATE tasks
1857
+ SET status = 'abandoned', updated_at = ?, finished_at = ?
1858
+ WHERE task_key = ?
1859
+ `).run(now, now, taskKey);
1860
+ }
1861
+ }
1862
+ // Remove session file
1863
+ try { await fs.unlink(filePath); } catch { /* noop */ }
1864
+ }
1865
+
1866
+ recovered.push({ source: 'session_file', agent: agentName, runKey, taskKey, startedAt: session.startedAt });
1867
+ }
1868
+
1869
+ // ── 2. Scan DB for orphaned running runs (no session file) ────────────────
1870
+ const orphanedRuns = db.prepare(`
1871
+ SELECT run_key, task_key, agent_name, started_at
1872
+ FROM agent_runs
1873
+ WHERE status IN ('running', 'queued')
1874
+ AND source = 'direct'
1875
+ AND started_at < ?
1876
+ `).all(cutoffIso);
1877
+
1878
+ for (const run of orphanedRuns) {
1879
+ // Skip if already recovered via session file
1880
+ if (recovered.some((r) => r.runKey === run.run_key)) continue;
1881
+
1882
+ if (!dryRun) {
1883
+ db.prepare(`
1884
+ UPDATE agent_runs
1885
+ SET status = 'abandoned', summary = 'Recovered: orphaned run with no session file', updated_at = ?, finished_at = ?
1886
+ WHERE run_key = ?
1887
+ `).run(now, now, run.run_key);
1888
+
1889
+ if (run.task_key) {
1890
+ const taskRow = db.prepare('SELECT task_key, status FROM tasks WHERE task_key = ?').get(run.task_key);
1891
+ if (taskRow && (taskRow.status === 'running' || taskRow.status === 'queued')) {
1892
+ db.prepare(`
1893
+ UPDATE tasks
1894
+ SET status = 'abandoned', updated_at = ?, finished_at = ?
1895
+ WHERE task_key = ?
1896
+ `).run(now, now, run.task_key);
1897
+ }
1898
+ }
1899
+ }
1900
+
1901
+ recovered.push({ source: 'orphaned_run', agent: run.agent_name, runKey: run.run_key, taskKey: run.task_key, startedAt: run.started_at });
1902
+ }
1903
+
1904
+ // ── Output ────────────────────────────────────────────────────────────────
1905
+ const olderThanLabel = options['older-than'] || options.olderThan || '24h';
1906
+ if (recovered.length === 0) {
1907
+ logger.log(`agent:recover — no abandoned sessions found older than ${olderThanLabel} (${dbPath})`);
1908
+ } else {
1909
+ const verb = dryRun ? '[dry-run] would recover' : 'recovered';
1910
+ logger.log(`agent:recover — ${verb} ${recovered.length} abandoned session(s) older than ${olderThanLabel} (${dbPath})`);
1911
+ for (const r of recovered) {
1912
+ logger.log(` ${r.agent} started: ${r.startedAt || '?'} run: ${r.runKey || '—'} [${r.source}]`);
1913
+ }
1914
+ }
1915
+ if (skipped.length > 0) {
1916
+ logger.log(` skipped ${skipped.length} session(s) within threshold.`);
1917
+ }
1918
+
1919
+ return { ok: true, targetDir, dbPath, dryRun, cutoff: cutoffIso, recovered, skipped };
1920
+ } finally {
1921
+ db.close();
1922
+ }
1923
+ }
1924
+
1925
+
1926
+ /**
1927
+ * aioson runtime:prune [targetDir] --older-than=<days>
1928
+ *
1929
+ * Removes execution_events, agent_events, and completed agent_runs
1930
+ * older than the specified number of days. Tasks are kept but their
1931
+ * events are cleaned up.
1932
+ */
1933
+ async function runRuntimePrune({ args, options = {}, logger, t }) {
1934
+ const targetDir = resolveTargetDir(args);
1935
+ const days = parseInt(options['older-than'] || options.olderThan || '30', 10);
1936
+
1937
+ if (isNaN(days) || days < 1) {
1938
+ logger.error('Usage: aioson runtime:prune --older-than=<days> (minimum 1)');
1939
+ return { ok: false, error: 'Invalid --older-than value' };
1940
+ }
1941
+
1942
+ const { db, dbPath } = await withRuntimeDb(targetDir, t);
1943
+
1944
+ try {
1945
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
1946
+
1947
+ const execEvents = db.prepare(
1948
+ `DELETE FROM execution_events WHERE created_at < ?`
1949
+ ).run(cutoff);
1950
+
1951
+ const agentEvents = db.prepare(
1952
+ `DELETE FROM agent_events WHERE created_at < ?`
1953
+ ).run(cutoff);
1954
+
1955
+ const runs = db.prepare(
1956
+ `DELETE FROM agent_runs WHERE status IN ('completed', 'failed') AND finished_at < ?`
1957
+ ).run(cutoff);
1958
+
1959
+ const tasks = db.prepare(
1960
+ `DELETE FROM tasks WHERE status IN ('completed', 'failed') AND finished_at < ?`
1961
+ ).run(cutoff);
1962
+
1963
+ const deliveryLogs = db.prepare(
1964
+ `DELETE FROM delivery_log WHERE created_at < ?`
1965
+ ).run(cutoff);
1966
+
1967
+ // Reclaim disk space
1968
+ db.pragma('wal_checkpoint(TRUNCATE)');
1969
+
1970
+ const total = execEvents.changes + agentEvents.changes + runs.changes + tasks.changes + deliveryLogs.changes;
1971
+
1972
+ logger.log(`Pruned ${total} records older than ${days} days from ${dbPath}:`);
1973
+ logger.log(` execution_events: ${execEvents.changes}`);
1974
+ logger.log(` agent_events: ${agentEvents.changes}`);
1975
+ logger.log(` agent_runs: ${runs.changes}`);
1976
+ logger.log(` tasks: ${tasks.changes}`);
1977
+ logger.log(` delivery_log: ${deliveryLogs.changes}`);
1978
+
1979
+ return {
1980
+ ok: true,
1981
+ dbPath,
1982
+ days,
1983
+ cutoff,
1984
+ deleted: {
1985
+ execution_events: execEvents.changes,
1986
+ agent_events: agentEvents.changes,
1987
+ agent_runs: runs.changes,
1988
+ tasks: tasks.changes,
1989
+ delivery_log: deliveryLogs.changes,
1990
+ total
1991
+ }
1992
+ };
1993
+ } finally {
1994
+ db.close();
1995
+ }
1996
+ }
1997
+
936
1998
  module.exports = {
937
1999
  runRuntimeInit,
938
2000
  runRuntimeIngest,
@@ -944,5 +2006,16 @@ module.exports = {
944
2006
  runRuntimeTaskFail,
945
2007
  runRuntimeFail,
946
2008
  runRuntimeStatus,
947
- runRuntimeLog
2009
+ runRuntimeLog,
2010
+ runAgentDone,
2011
+ runAgentRecover,
2012
+ runRuntimeSessionStart,
2013
+ runRuntimeSessionLog,
2014
+ runRuntimeSessionFinish,
2015
+ runRuntimeSessionStatus,
2016
+ runDeliver,
2017
+ runOutputStrategyExport,
2018
+ runOutputStrategyImport,
2019
+ runDevlogSync,
2020
+ runRuntimePrune
948
2021
  };