@selesai/code 0.8.12 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/core/agent-session.js +41 -29
  3. package/dist/core/resource-loader.d.ts +1 -1
  4. package/dist/core/resource-loader.js +2 -2
  5. package/dist/defaults/models.json +15 -3
  6. package/dist/defaults/settings.json +10 -8
  7. package/dist/extensions/model-prompt-injector/config.json +6 -0
  8. package/dist/extensions/package.json +2 -1
  9. package/dist/extensions/pi-subagents/README.md +4 -4
  10. package/dist/extensions/pi-subagents/docs/configuration.md +3 -54
  11. package/dist/extensions/pi-subagents/docs/extension-api.md +4 -15
  12. package/dist/extensions/pi-subagents/docs/missions.md +2 -39
  13. package/dist/extensions/pi-subagents/docs/observability.md +2 -42
  14. package/dist/extensions/pi-subagents/docs/tool-reference.md +1 -2
  15. package/dist/extensions/pi-subagents/docs/watchdog.md +0 -57
  16. package/dist/extensions/pi-subagents/docs/workflows.md +2 -0
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +4 -49
  18. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
  19. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +1 -2
  20. package/dist/extensions/pi-subagents/src/extension/config.ts +3 -15
  21. package/dist/extensions/pi-subagents/src/extension/index.ts +1 -53
  22. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +4 -10
  23. package/dist/extensions/pi-subagents/src/extension/rpc.ts +1 -2
  24. package/dist/extensions/pi-subagents/src/extension/schemas.ts +1 -12
  25. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -2
  26. package/dist/extensions/pi-subagents/src/policy/authority.ts +0 -2
  27. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +33 -102
  28. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -2
  29. package/dist/extensions/pi-subagents/src/shared/types.ts +1 -1
  30. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +4 -58
  31. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +2 -8
  32. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +1 -2
  33. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +16 -3
  34. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -95
  35. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +0 -15
  36. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +0 -59
  37. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +0 -59
  38. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +0 -47
  39. package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +0 -1
  40. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +2 -2
  41. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +5 -5
  42. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +0 -8
  43. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +2 -2
  44. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +2 -2
  45. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +4 -4
  46. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +21 -1
  47. package/dist/modes/interactive/components/model-selector.d.ts +10 -1
  48. package/dist/modes/interactive/components/model-selector.js +10 -6
  49. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  50. package/dist/modes/interactive/components/settings-selector.js +3 -0
  51. package/dist/modes/interactive/interactive-mode.js +7 -0
  52. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  53. package/dist/skills/pi-subagents/references/execution-controls.md +2 -32
  54. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
  55. package/dist/skills/pi-subagents/references/prompting-and-roles.md +3 -3
  56. package/docs/custom-provider.md +1 -0
  57. package/package.json +3 -3
  58. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +0 -803
  59. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +0 -691
  60. package/dist/extensions/pi-subagents/src/tui/fleet.ts +0 -1314
  61. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +0 -3
  62. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +0 -2
  63. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +0 -17
  64. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +0 -57
  65. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +0 -16
  66. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +0 -15
  67. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +0 -18
  68. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +0 -9
  69. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +0 -10
  70. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +0 -10
  71. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +0 -20
  72. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +0 -3
  73. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +0 -96
  74. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +0 -119
  75. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +0 -85
  76. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +0 -620
  77. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +0 -5
  78. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +0 -56
  79. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +0 -32
  80. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +0 -49
  81. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +0 -93
  82. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +0 -128
  83. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +0 -7
  84. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +0 -38
  85. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +0 -3
  86. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +0 -15
  87. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +0 -9
  88. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +0 -29
  89. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +0 -2
  90. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +0 -22
  91. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +0 -37
  92. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +0 -34
  93. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +0 -12
  94. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +0 -23
  95. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +0 -171
  96. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +0 -1263
  97. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +0 -20
  98. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +0 -54
  99. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +0 -22
  100. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +0 -13
  101. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +0 -358
  102. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +0 -77
  103. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +0 -512
  104. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +0 -43
  105. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +0 -121
  106. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +0 -82
  107. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +0 -17
  108. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +0 -7
  109. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +0 -210
  110. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +0 -15
  111. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +0 -54
  112. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +0 -4
  113. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +0 -55
  114. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +0 -2
  115. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +0 -5
  116. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +0 -30
  117. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +0 -206
  118. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +0 -15
  119. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +0 -106
  120. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +0 -19
  121. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +0 -356
  122. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +0 -297
  123. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +0 -60
  124. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +0 -15
  125. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +0 -18
  126. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +0 -28
  127. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +0 -3
  128. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +0 -10
  129. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +0 -68
  130. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +0 -106
  131. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +0 -51
  132. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +0 -6
  133. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +0 -35
  134. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +0 -37
  135. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +0 -40
  136. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +0 -37
  137. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +0 -52
  138. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +0 -10
  139. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +0 -35
  140. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +0 -24
  141. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +0 -14
  142. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +0 -70
  143. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +0 -26
  144. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +0 -36
  145. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +0 -19
  146. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +0 -3
  147. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +0 -61
  148. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +0 -23
  149. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +0 -3
  150. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +0 -10
  151. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +0 -42
  152. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +0 -35
  153. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +0 -12
  154. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +0 -33
  155. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +0 -9
  156. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +0 -34
  157. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +0 -22
  158. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +0 -34
  159. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +0 -16
  160. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +0 -16
  161. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -16
  162. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +0 -102
  163. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +0 -14
  164. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +0 -21
  165. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +0 -19
  166. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +0 -20
  167. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +0 -23
  168. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +0 -12
  169. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +0 -15
  170. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
  171. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +0 -22
  172. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +0 -23
  173. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +0 -26
  174. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +0 -25
  175. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +0 -32
  176. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +0 -34
  177. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +0 -13
  178. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +0 -52
  179. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +0 -54
  180. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +0 -33
  181. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -42
  182. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +0 -23
  183. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +0 -95
  184. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +0 -77
  185. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +0 -10
  186. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -17
  187. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +0 -28
  188. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +0 -11
  189. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +0 -11
  190. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +0 -63
  191. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +0 -94
  192. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +0 -23
  193. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +0 -30
  194. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +0 -14
  195. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +0 -24
  196. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +0 -394
  197. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +0 -12
  198. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +0 -29
  199. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +0 -120
  200. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +0 -28
  201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +0 -17
  202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +0 -246
  203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +0 -20
  204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +0 -457
  205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +0 -8
  206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +0 -3
  207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +0 -2
  208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +0 -22
  209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +0 -18
  210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +0 -11
  211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +0 -11
  212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +0 -4
  213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +0 -4
  214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +0 -5
  215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +0 -8
  216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +0 -5
  217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +0 -26
  218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +0 -7
  219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +0 -10
  220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +0 -18
  221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +0 -30
  222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +0 -2
  223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +0 -16
  224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +0 -3
  225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +0 -3
  226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +0 -9
  227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +0 -31
  228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +0 -21
  229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +0 -2
  230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +0 -31
  231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +0 -5
  232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +0 -12
  233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +0 -3
  234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +0 -15
  235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +0 -3
  236. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +0 -1004
  237. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +0 -1511
  238. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +0 -634
  239. package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +0 -67
@@ -1,634 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
- import { afterEach, describe, it } from "node:test";
6
- import type { ExtensionContext } from "@selesai/code";
7
- import {
8
- SCHEDULED_RUN_ACTIONS,
9
- createScheduledRunManager,
10
- isScheduledRunAction,
11
- listScheduledRunSummaries,
12
- parseScheduleInterval,
13
- parseScheduledRunTime,
14
- scheduledRunStorePath,
15
- scheduledRunsEnabled,
16
- type ScheduledRunManager,
17
- } from "../../src/runs/background/scheduled-runs.ts";
18
- import type { ExtensionConfig } from "../../src/shared/types.ts";
19
-
20
- type Timer = { callback: () => void; delay: number };
21
- class FakeTimers {
22
- readonly values = new Map<number, Timer>();
23
- private id = 0;
24
- setTimeout = (callback: () => void, delay: number) => {
25
- const id = ++this.id;
26
- this.values.set(id, { callback, delay });
27
- return id as unknown as ReturnType<typeof setTimeout>;
28
- };
29
- clearTimeout = (id: ReturnType<typeof setTimeout>) => void this.values.delete(id as unknown as number);
30
- fireAll(): void {
31
- const pending = [...this.values.entries()];
32
- for (const [id, timer] of pending) {
33
- this.values.delete(id);
34
- timer.callback();
35
- }
36
- }
37
- }
38
-
39
- type Launch = {
40
- params: Record<string, unknown>;
41
- ctx: ExtensionContext;
42
- resolve(result: { content: Array<{ type: "text"; text: string }>; details: Record<string, unknown>; isError?: boolean }): void;
43
- };
44
-
45
- type Harness = {
46
- manager: ScheduledRunManager;
47
- ctx: ExtensionContext;
48
- clock: { now: number };
49
- timers: FakeTimers;
50
- launches: Launch[];
51
- root: string;
52
- };
53
-
54
- const roots: string[] = [];
55
- afterEach(() => {
56
- for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
57
- });
58
-
59
- function context(cwd: string, sessionId = "session-a"): ExtensionContext {
60
- return {
61
- cwd,
62
- sessionManager: {
63
- getSessionId: () => sessionId,
64
- getSessionFile: () => path.join(cwd, `${sessionId}.jsonl`),
65
- },
66
- } as unknown as ExtensionContext;
67
- }
68
-
69
- function harness(options: { cwd?: string; sessionId?: string; now?: number; config?: ExtensionConfig; randomId?: () => string } = {}): Harness {
70
- const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-schedule-test-"));
71
- roots.push(root);
72
- const project = options.cwd ?? path.join(root, "project");
73
- fs.mkdirSync(project, { recursive: true });
74
- const ctx = context(project, options.sessionId);
75
- const clock = { now: options.now ?? Date.parse("2030-01-01T00:00:00Z") };
76
- const timers = new FakeTimers();
77
- const launches: Launch[] = [];
78
- let id = 0;
79
- const manager = createScheduledRunManager({
80
- config: options.config ?? { scheduledRuns: { enabled: true } },
81
- storeRoot: path.join(root, "stores"),
82
- now: () => clock.now,
83
- randomId: options.randomId ?? (() => `id-${++id}`),
84
- timers,
85
- launch: (params, launchCtx) => new Promise((resolve) => launches.push({ params: params as Record<string, unknown>, ctx: launchCtx, resolve: resolve as Launch["resolve"] })) as never,
86
- });
87
- manager.bindSession(ctx);
88
- return { manager, ctx, clock, timers, launches, root };
89
- }
90
-
91
- function text(result: Awaited<ReturnType<ScheduledRunManager["handleToolCall"]>>): string {
92
- return result.content[0]?.type === "text" ? result.content[0].text : "";
93
- }
94
-
95
- async function flush(): Promise<void> {
96
- for (let i = 0; i < 8; i++) await Promise.resolve();
97
- }
98
-
99
- describe("schedule helpers", () => {
100
- it("recognizes only the dot-action schedule API", () => {
101
- assert.deepEqual(SCHEDULED_RUN_ACTIONS, ["schedule.create", "schedule.list", "schedule.show", "schedule.history", "schedule.pause", "schedule.resume", "schedule.run", "schedule.run-due", "schedule.delete"]);
102
- assert.equal(isScheduledRunAction("schedule.create"), true);
103
- assert.equal(isScheduledRunAction("schedule"), false);
104
- });
105
-
106
- it("uses a stable project store independent of session id", () => {
107
- const root = path.join("tmp", "schedules");
108
- assert.equal(scheduledRunStorePath("project", "a", root), scheduledRunStorePath("project", "b", root));
109
- assert.notEqual(scheduledRunStorePath("project", "a", root), scheduledRunStorePath("other", "a", root));
110
- assert.equal(scheduledRunStorePath("/project"), path.join(path.resolve("/project"), ".pi-subagents", "schedules"));
111
- });
112
-
113
- it("parses one-shot and fixed interval forms strictly", () => {
114
- const now = Date.parse("2030-01-01T00:00:00Z");
115
- assert.equal(parseScheduledRunTime("+10m", now), now + 600_000);
116
- assert.equal(parseScheduledRunTime("2030-01-02T00:00:00Z", now), now + 86_400_000);
117
- assert.equal(parseScheduledRunTime("2030-01-02T09:00:00+05:30", now), Date.parse("2030-01-02T09:00:00+05:30"));
118
- assert.equal(parseScheduleInterval("30m"), 1_800_000);
119
- assert.equal(parseScheduleInterval("2w"), 1_209_600_000);
120
- assert.throws(() => parseScheduleInterval("day"), /fixed intervals/);
121
- assert.throws(() => parseScheduledRunTime("2030-01-01T00:00:00", now), /timezone/);
122
- });
123
-
124
- it("honors the explicit feature opt-out", () => {
125
- assert.equal(scheduledRunsEnabled({}), true);
126
- assert.equal(scheduledRunsEnabled({ scheduledRuns: { enabled: false } }), false);
127
- });
128
- });
129
-
130
- describe("project schedule management", () => {
131
- it("creates a project one-shot schedule and restores it in another session", async () => {
132
- const first = harness();
133
- const result = await first.manager.handleToolCall({ action: "schedule.create", id: "night-review", name: "Night review", at: "+10m", workflowScript: "return runs.run('main', { agent: 'reviewer', task: 'Review the diff' })" }, first.ctx);
134
- assert.equal(result.isError, undefined);
135
- assert.match(text(result), /Created schedule night-review/);
136
- assert.equal(first.timers.values.size, 1);
137
- const records = listScheduledRunSummaries(first.ctx.cwd, path.join(first.root, "stores"));
138
- assert.equal(records[0]?.name, "Night review");
139
- assert.equal(records[0]?.cwd, first.ctx.cwd);
140
-
141
- first.manager.stop();
142
- const secondTimers = new FakeTimers();
143
- const second = createScheduledRunManager({
144
- config: { scheduledRuns: { enabled: true } },
145
- storeRoot: path.join(first.root, "stores"),
146
- now: () => first.clock.now,
147
- timers: secondTimers,
148
- launch: async () => ({ content: [{ type: "text", text: "unused" }], details: { mode: "management", results: [] } }),
149
- });
150
- second.bindSession(context(first.ctx.cwd, "session-b"));
151
- assert.equal(secondTimers.values.size, 1, "a different session restores the project schedule");
152
- const shown = await second.handleToolCall({ action: "schedule.show", id: "night-review" }, context(first.ctx.cwd, "session-b"));
153
- assert.match(text(shown), /Night review/);
154
- });
155
-
156
- it("stores explicit cwd schedules in the target project", async () => {
157
- const h = harness();
158
- const target = path.join(h.root, "other-project");
159
- fs.mkdirSync(target);
160
- h.manager.bindSession(context(target, "target-session"));
161
- h.manager.bindSession(h.ctx);
162
- await h.manager.handleToolCall({ action: "schedule.create", id: "other", cwd: target, every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
163
- assert.equal(listScheduledRunSummaries(h.ctx.cwd, path.join(h.root, "stores")).length, 0);
164
- assert.equal(listScheduledRunSummaries(target, path.join(h.root, "stores"))[0]?.cwd, target);
165
- const listed = await h.manager.handleToolCall({ action: "schedule.list", cwd: target }, h.ctx);
166
- assert.match(text(listed), /other/);
167
- });
168
-
169
- it("rejects direct schedule targets and requires workflowScript", async () => {
170
- const h = harness();
171
- const result = await h.manager.handleToolCall({ action: "schedule.create", id: "direct", every: "1h", agent: "worker", task: "Review" }, h.ctx);
172
- assert.equal(result.isError, true);
173
- assert.match(text(result), /requires workflowScript/);
174
- });
175
-
176
- it("fails closed on persisted legacy agent targets", () => {
177
- const h = harness();
178
- h.manager.stop();
179
- const root = scheduledRunStorePath(h.ctx.cwd, undefined, path.join(h.root, "stores"));
180
- const dir = path.join(root, "legacy");
181
- fs.mkdirSync(dir, { recursive: true });
182
- fs.writeFileSync(path.join(dir, "schedule.json"), JSON.stringify({
183
- schemaVersion: 1,
184
- id: "legacy",
185
- name: "Legacy direct target",
186
- cwd: h.ctx.cwd,
187
- trigger: { kind: "interval", every: "1h", everyMs: 3_600_000, anchorAt: new Date(h.clock.now).toISOString(), nextRunAt: new Date(h.clock.now + 3_600_000).toISOString() },
188
- target: { agent: "worker", task: "Review backlog" },
189
- overlap: "skip",
190
- catchUp: "latest",
191
- paused: false,
192
- createdAt: new Date(h.clock.now).toISOString(),
193
- updatedAt: new Date(h.clock.now).toISOString(),
194
- }), "utf-8");
195
- const manager = createScheduledRunManager({
196
- config: { scheduledRuns: { enabled: true } },
197
- storeRoot: path.join(h.root, "stores"),
198
- now: () => h.clock.now,
199
- timers: h.timers,
200
- launch: async () => ({ content: [{ type: "text", text: "unused" }], details: { mode: "management", results: [] } }),
201
- });
202
-
203
- assert.throws(() => manager.bindSession(context(h.ctx.cwd, "session-b")), /removed legacy agent target.*target\.workflowScript/i);
204
- });
205
-
206
- it("supports workflowScript targets and rejects unsafe or deferred shapes", async () => {
207
- const h = harness();
208
- const workflow = await h.manager.handleToolCall({ action: "schedule.create", id: "workflow", every: "6h", workflowScript: "return await runs.run('review', {agent:'reviewer'})" }, h.ctx);
209
- assert.equal(workflow.isError, undefined);
210
- assert.match(text(workflow), /workflowScript -> agent reviewer/);
211
- const dynamic = await h.manager.handleToolCall({ action: "schedule.create", id: "dynamic", every: "6h", workflowScript: "const agent = 'worker'; return runs.run('main', { agent })" }, h.ctx);
212
- assert.equal(dynamic.isError, undefined);
213
- assert.match(text(dynamic), /workflowScript \(dynamic\)/);
214
- for (const params of [
215
- { action: "schedule.create", id: "../escape", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" },
216
- { action: "schedule.create", id: "both", at: "+1h", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" },
217
- { action: "schedule.create", id: "calendar", every: "day", at: "09:00", timezone: "UTC", workflowScript: "return runs.run('main', { agent: 'worker' })" },
218
- { action: "schedule.create", id: "two-targets", every: "1h", agent: "worker", workflowScript: "return 1" },
219
- { action: "schedule.create", id: "fork", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })", context: "fork" },
220
- { action: "schedule.create", id: "mission-id", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })", missionId: "mission-1" },
221
- { action: "schedule.create", id: "mission-off", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })", mission: false },
222
- ] as const) {
223
- const result = await h.manager.handleToolCall(params, h.ctx);
224
- assert.equal(result.isError, true, JSON.stringify(params));
225
- }
226
- });
227
-
228
- it("pauses, resumes, lists, and deletes an inactive schedule", async () => {
229
- const h = harness();
230
- await h.manager.handleToolCall({ action: "schedule.create", id: "maintenance", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
231
- assert.match(text(await h.manager.handleToolCall({ action: "schedule.list" }, h.ctx)), /maintenance/);
232
- assert.match(text(await h.manager.handleToolCall({ action: "schedule.pause", id: "maintenance" }, h.ctx)), /Paused/);
233
- assert.equal(h.timers.values.size, 0);
234
- assert.match(text(await h.manager.handleToolCall({ action: "schedule.resume", id: "maintenance" }, h.ctx)), /Resumed/);
235
- assert.equal(h.timers.values.size, 1);
236
- assert.match(text(await h.manager.handleToolCall({ action: "schedule.delete", id: "maintenance" }, h.ctx)), /Deleted/);
237
- assert.match(text(await h.manager.handleToolCall({ action: "schedule.list" }, h.ctx)), /No project schedules/);
238
- });
239
-
240
- it("ignores schedules deleted by another session while a timer is still armed", async () => {
241
- const first = harness();
242
- await first.manager.handleToolCall({ action: "schedule.create", id: "stale", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, first.ctx);
243
- const secondTimers = new FakeTimers();
244
- const second = createScheduledRunManager({
245
- config: { scheduledRuns: { enabled: true } },
246
- storeRoot: path.join(first.root, "stores"),
247
- now: () => first.clock.now,
248
- timers: secondTimers,
249
- launch: async () => ({ content: [{ type: "text", text: "unused" }], details: { mode: "management", results: [] } }),
250
- });
251
- const secondCtx = context(first.ctx.cwd, "session-b");
252
- second.bindSession(secondCtx);
253
- assert.equal(first.timers.values.size, 1);
254
- assert.equal(secondTimers.values.size, 1);
255
-
256
- assert.match(text(await second.handleToolCall({ action: "schedule.delete", id: "stale" }, secondCtx)), /Deleted/);
257
- assert.equal(secondTimers.values.size, 0);
258
- assert.equal(first.timers.values.size, 1);
259
- first.clock.now += 3_600_000;
260
- const warnings: string[] = [];
261
- const originalWarn = console.warn;
262
- console.warn = (message?: unknown) => void warnings.push(String(message));
263
- try {
264
- first.timers.fireAll();
265
- await flush();
266
- } finally {
267
- console.warn = originalWarn;
268
- }
269
- assert.deepEqual(warnings, []);
270
- assert.equal(first.launches.length, 0);
271
- assert.equal(first.timers.values.size, 0);
272
- assert.match(text(await first.manager.handleToolCall({ action: "schedule.list" }, first.ctx)), /No project schedules/);
273
- });
274
-
275
- it("reports corrupt project schedule records instead of dropping them", async () => {
276
- const h = harness();
277
- const root = scheduledRunStorePath(h.ctx.cwd, undefined, path.join(h.root, "stores"));
278
- const dir = path.join(root, "broken");
279
- fs.mkdirSync(dir, { recursive: true });
280
- fs.writeFileSync(path.join(dir, "schedule.json"), "{ bad", "utf-8");
281
- const result = await h.manager.handleToolCall({ action: "schedule.list" }, h.ctx);
282
- assert.equal(result.isError, true);
283
- assert.match(text(result), /Failed to read schedule record/);
284
- });
285
-
286
- it("rejects schedule directories that escape through a symlink", async () => {
287
- const h = harness();
288
- const root = scheduledRunStorePath(h.ctx.cwd, undefined, path.join(h.root, "stores"));
289
- const outside = path.join(h.root, "outside");
290
- fs.mkdirSync(root, { recursive: true });
291
- fs.mkdirSync(outside);
292
- fs.symlinkSync(outside, path.join(root, "escaped"), process.platform === "win32" ? "junction" : "dir");
293
- const result = await h.manager.handleToolCall({ action: "schedule.create", id: "escaped", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
294
- assert.equal(result.isError, true);
295
- assert.match(text(result), /must be a real directory/);
296
- assert.equal(fs.existsSync(path.join(outside, "schedule.json")), false);
297
- });
298
-
299
- it("rejects a default project schedule root that escapes through .pi-subagents", async () => {
300
- const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-schedule-root-link-"));
301
- roots.push(root);
302
- const project = path.join(root, "project");
303
- const outside = path.join(root, "outside");
304
- fs.mkdirSync(project);
305
- fs.mkdirSync(outside);
306
- const ctx = context(project);
307
- const manager = createScheduledRunManager({
308
- config: { scheduledRuns: { enabled: true } },
309
- launch: async () => ({ content: [{ type: "text", text: "unused" }], details: { mode: "management", results: [] } }),
310
- });
311
- manager.bindSession(ctx);
312
- fs.mkdirSync(path.join(project, ".selesai"));
313
- fs.symlinkSync(outside, path.join(project, ".pi-subagents"), process.platform === "win32" ? "junction" : "dir");
314
-
315
- const result = await manager.handleToolCall({ action: "schedule.create", id: "escaped-root", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, ctx);
316
- assert.equal(result.isError, true);
317
- assert.match(text(result), /resolves outside the real project/);
318
- assert.equal(fs.existsSync(path.join(outside, "schedules")), false);
319
- });
320
- });
321
-
322
- describe("recurring schedule execution", () => {
323
- it("launches a fixed interval from its planned time and records durable history/events", async () => {
324
- const h = harness();
325
- await h.manager.handleToolCall({ action: "schedule.create", id: "hourly", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker', task: 'Maintain backlog' })" }, h.ctx);
326
- h.clock.now += 3_600_000;
327
- h.timers.fireAll();
328
- assert.equal(h.launches.length, 1);
329
- assert.deepEqual(h.launches[0]?.params, { workflowScript: "return runs.run('main', { agent: 'worker', task: 'Maintain backlog' })", async: true, context: "fresh", cwd: h.ctx.cwd, mission: false });
330
- h.launches[0]!.resolve({ content: [{ type: "text", text: "Async worker" }], details: { mode: "single", results: [], asyncId: "async-1", asyncDir: "/tmp/async-1" } });
331
- await flush();
332
- assert.deepEqual([...h.manager.observedCompletionRunIds()], ["async-1"]);
333
-
334
- let history = await h.manager.handleToolCall({ action: "schedule.history", id: "hourly" }, h.ctx);
335
- assert.match(text(history), /running.*async async-1/);
336
- const scheduleRoot = scheduledRunStorePath(h.ctx.cwd, undefined, path.join(h.root, "stores"));
337
- const dir = path.join(scheduleRoot, "hourly");
338
- assert.equal(fs.existsSync(path.join(dir, "history.json")), true);
339
- assert.equal(fs.existsSync(path.join(dir, "events.jsonl")), true);
340
- assert.equal(fs.readdirSync(path.join(dir, "runs")).length, 1);
341
-
342
- h.manager.handleAsyncCompletion({ runId: "async-1", success: true, summary: "Done" });
343
- assert.deepEqual([...h.manager.observedCompletionRunIds()], []);
344
- history = await h.manager.handleToolCall({ action: "schedule.history", id: "hourly" }, h.ctx);
345
- assert.match(text(history), /completed.*async async-1/);
346
- assert.equal(fs.existsSync(path.join(dir, "active.lock")), false);
347
- const shown = await h.manager.handleToolCall({ action: "schedule.show", id: "hourly" }, h.ctx);
348
- assert.match(text(shown), /2030-01-01T02:00:00.000Z/, "next occurrence advances from the planned time without completion drift");
349
- });
350
-
351
- it("delays retrying an overdue recurring schedule after an unexpected timer failure", async () => {
352
- const h = harness({ randomId: () => { throw new Error("persistent id failure"); } });
353
- await h.manager.handleToolCall({ action: "schedule.create", id: "hourly", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
354
- h.clock.now += 3_600_000;
355
- const warnings: string[] = [];
356
- const originalWarn = console.warn;
357
- console.warn = (message?: unknown) => void warnings.push(String(message));
358
- try {
359
- h.timers.fireAll();
360
- await flush();
361
- } finally {
362
- console.warn = originalWarn;
363
- }
364
-
365
- assert.equal(h.launches.length, 0);
366
- assert.equal(h.timers.values.size, 1);
367
- assert.equal([...h.timers.values.values()][0]?.delay, 3_600_000);
368
- assert.match(warnings[0] ?? "", /failed to fire: persistent id failure/);
369
- const shown = await h.manager.handleToolCall({ action: "schedule.show", id: "hourly" }, h.ctx);
370
- assert.match(text(shown), /State: scheduled/);
371
- assert.match(text(shown), /2030-01-01T01:00:00.000Z/);
372
- });
373
-
374
- it("re-arms overdue catch-up-none schedules when missed-run recovery fails", async () => {
375
- const h = harness({ randomId: () => { throw new Error("persistent id failure"); } });
376
- await h.manager.handleToolCall({ action: "schedule.create", id: "none", every: "1h", catchUp: "none", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
377
- h.clock.now += 2 * 3_600_000;
378
- const warnings: string[] = [];
379
- const originalWarn = console.warn;
380
- console.warn = (message?: unknown) => void warnings.push(String(message));
381
- try {
382
- h.timers.fireAll();
383
- await flush();
384
- } finally {
385
- console.warn = originalWarn;
386
- }
387
-
388
- assert.equal(h.launches.length, 0);
389
- assert.equal(h.timers.values.size, 1);
390
- assert.equal([...h.timers.values.values()][0]?.delay, 3_600_000);
391
- assert.match(warnings[0] ?? "", /failed to fire: persistent id failure/);
392
- assert.match(warnings[1] ?? "", /could not be restored after fire failure: persistent id failure/);
393
- const shown = await h.manager.handleToolCall({ action: "schedule.show", id: "none" }, h.ctx);
394
- assert.match(text(shown), /2030-01-01T01:00:00.000Z/);
395
- });
396
-
397
- it("does not immediately retry an overdue one-shot after an unexpected timer failure", async () => {
398
- const h = harness({ randomId: () => { throw new Error("persistent id failure"); } });
399
- await h.manager.handleToolCall({ action: "schedule.create", id: "once", at: "+1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
400
- h.clock.now += 3_600_000;
401
- const warnings: string[] = [];
402
- const originalWarn = console.warn;
403
- console.warn = (message?: unknown) => void warnings.push(String(message));
404
- try {
405
- h.timers.fireAll();
406
- await flush();
407
- } finally {
408
- console.warn = originalWarn;
409
- }
410
-
411
- assert.equal(h.launches.length, 0);
412
- assert.equal(h.timers.values.size, 0);
413
- assert.match(warnings[0] ?? "", /failed to fire: persistent id failure/);
414
- const shown = await h.manager.handleToolCall({ action: "schedule.show", id: "once" }, h.ctx);
415
- assert.match(text(shown), /State: scheduled/);
416
- assert.match(text(shown), /2030-01-01T01:00:00.000Z/);
417
- });
418
-
419
- it("run-due launches the latest missed occurrence while catchUp none records a miss", async () => {
420
- const latest = harness();
421
- await latest.manager.handleToolCall({ action: "schedule.create", id: "latest", every: "1h", catchUp: "latest", workflowScript: "return runs.run('main', { agent: 'worker' })" }, latest.ctx);
422
- latest.manager.stop();
423
- latest.clock.now += 3 * 3_600_000;
424
- latest.manager.bindSession(latest.ctx);
425
- const duePromise = latest.manager.handleToolCall({ action: "schedule.run-due" }, latest.ctx);
426
- await flush();
427
- assert.equal(latest.launches.length, 1);
428
- latest.launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "late-1" } });
429
- assert.match(text(await duePromise), /Processed 1 due schedule/);
430
- assert.match(text(await latest.manager.handleToolCall({ action: "schedule.history", id: "latest" }, latest.ctx)), /2030-01-01T03:00:00.000Z/, "latest catch-up selects the latest missed slot");
431
-
432
- const none = harness();
433
- await none.manager.handleToolCall({ action: "schedule.create", id: "none", every: "1h", catchUp: "none", workflowScript: "return runs.run('main', { agent: 'worker' })" }, none.ctx);
434
- none.manager.stop();
435
- none.clock.now += 3 * 3_600_000;
436
- none.manager.bindSession(none.ctx);
437
- assert.match(text(await none.manager.handleToolCall({ action: "schedule.history", id: "none" }, none.ctx)), /missed/);
438
- assert.equal(none.launches.length, 0);
439
- });
440
-
441
- it("keeps project timers, contexts, and completion ownership across session_start bindings", async () => {
442
- const h = harness();
443
- await h.manager.handleToolCall({ action: "schedule.create", id: "project-a", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
444
-
445
- const projectB = path.join(h.root, "project-b");
446
- fs.mkdirSync(projectB);
447
- const projectBCtx = context(projectB, "session-b");
448
- const sourceSessionManager = h.ctx.sessionManager as { getSessionId(): string | null; getSessionFile(): string | null };
449
- const getSourceSessionId = sourceSessionManager.getSessionId;
450
- const getSourceSessionFile = sourceSessionManager.getSessionFile;
451
- sourceSessionManager.getSessionId = () => "session-b";
452
- sourceSessionManager.getSessionFile = () => path.join(projectB, "session-b.jsonl");
453
- h.manager.bindSession(projectBCtx);
454
- await h.manager.handleToolCall({ action: "schedule.create", id: "project-b", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, projectBCtx);
455
- assert.equal(h.timers.values.size, 2, "both project timers remain armed after session_start binds project B");
456
-
457
- h.clock.now += 3_600_000;
458
- h.timers.fireAll();
459
- await flush();
460
- assert.equal(h.launches.length, 2);
461
- assert.equal(h.launches[0]!.ctx.cwd, h.ctx.cwd);
462
- assert.equal(h.launches[0]!.ctx.sessionManager.getSessionId(), "session-a");
463
- assert.equal(h.launches[0]!.ctx.sessionManager.getSessionFile(), path.join(h.ctx.cwd, "session-a.jsonl"));
464
- assert.equal(h.launches[1]!.ctx.cwd, projectB);
465
- assert.equal(h.launches[1]!.ctx.sessionManager.getSessionId(), "session-b");
466
- assert.equal(h.launches[1]!.ctx.sessionManager.getSessionFile(), path.join(projectB, "session-b.jsonl"));
467
- sourceSessionManager.getSessionId = getSourceSessionId;
468
- sourceSessionManager.getSessionFile = getSourceSessionFile;
469
- h.launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "async-a" } });
470
- h.launches[1]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "async-b" } });
471
- await flush();
472
-
473
- h.manager.handleAsyncCompletion({ runId: "async-a", success: true });
474
- const history = await h.manager.handleToolCall({ action: "schedule.history", id: "project-a" }, h.ctx);
475
- assert.match(text(history), /completed.*async async-a/);
476
- const projectARoot = scheduledRunStorePath(h.ctx.cwd, undefined, path.join(h.root, "stores"));
477
- assert.equal(fs.existsSync(path.join(projectARoot, "project-a", "active.lock")), false);
478
- });
479
-
480
- it("uses only a bound target session for an explicit cross-project cwd", async () => {
481
- const h = harness();
482
- const target = path.join(h.root, "explicit-target");
483
- fs.mkdirSync(target);
484
- const unbound = await h.manager.handleToolCall({ action: "schedule.create", id: "targeted", cwd: target, every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
485
- assert.equal(unbound.isError, true);
486
- assert.match(text(unbound), /until that project has been opened/);
487
-
488
- const targetCtx = context(target, "target-session");
489
- h.manager.bindSession(targetCtx);
490
- h.manager.bindSession(h.ctx);
491
- await h.manager.handleToolCall({ action: "schedule.create", id: "targeted", cwd: target, every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
492
- h.clock.now += 3_600_000;
493
- h.timers.fireAll();
494
- await flush();
495
- assert.equal(h.launches.length, 1);
496
- assert.equal(h.launches[0]!.ctx.cwd, target);
497
- assert.equal(h.launches[0]!.ctx.sessionManager.getSessionId(), "target-session");
498
- assert.equal(h.launches[0]!.ctx.sessionManager.getSessionFile(), path.join(target, "target-session.jsonl"));
499
- assert.equal(h.launches[0]!.params.cwd, target);
500
- h.launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "target-async" } });
501
- await flush();
502
- });
503
-
504
- it("reconciles owner completion after an unrelated store becomes malformed", async () => {
505
- const h = harness();
506
- const badProject = path.join(h.root, "bad-project");
507
- fs.mkdirSync(badProject);
508
- const badCtx = context(badProject, "bad-session");
509
- const ownerCtx = context(h.ctx.cwd, "owner-session");
510
- const timers = new FakeTimers();
511
- const launches: Launch[] = [];
512
- let id = 0;
513
- const manager = createScheduledRunManager({
514
- config: { scheduledRuns: { enabled: true } },
515
- storeRoot: path.join(h.root, "isolated-stores"),
516
- now: () => h.clock.now,
517
- randomId: () => `isolated-${++id}`,
518
- timers,
519
- launch: (params, launchCtx) => new Promise((resolve) => launches.push({ params: params as Record<string, unknown>, ctx: launchCtx, resolve: resolve as Launch["resolve"] })) as never,
520
- });
521
- manager.bindSession(badCtx);
522
- manager.bindSession(ownerCtx);
523
- await manager.handleToolCall({ action: "schedule.create", id: "owner", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, ownerCtx);
524
- const manual = manager.handleToolCall({ action: "schedule.run", id: "owner" }, ownerCtx);
525
- await flush();
526
- launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "owner-async" } });
527
- await manual;
528
-
529
- const badDir = path.join(scheduledRunStorePath(badProject, undefined, path.join(h.root, "isolated-stores")), "broken");
530
- fs.mkdirSync(badDir, { recursive: true });
531
- fs.writeFileSync(path.join(badDir, "schedule.json"), "{ bad", "utf-8");
532
- const ownerRoot = scheduledRunStorePath(ownerCtx.cwd, undefined, path.join(h.root, "isolated-stores"));
533
- const badSiblingDir = path.join(ownerRoot, "broken-sibling");
534
- fs.mkdirSync(badSiblingDir, { recursive: true });
535
- fs.writeFileSync(path.join(badSiblingDir, "schedule.json"), "{ bad", "utf-8");
536
- const originalError = console.error;
537
- console.error = () => {};
538
- try {
539
- manager.handleAsyncCompletion({ runId: "owner-async", success: true });
540
- } finally {
541
- console.error = originalError;
542
- }
543
- const history = await manager.handleToolCall({ action: "schedule.history", id: "owner" }, ownerCtx);
544
- assert.match(text(history), /completed.*async owner-async/);
545
- assert.equal(fs.existsSync(path.join(ownerRoot, "owner", "active.lock")), false);
546
- });
547
-
548
- it("reconciles a terminal async status after a new session binds", async () => {
549
- const h = harness();
550
- await h.manager.handleToolCall({ action: "schedule.create", id: "restart", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
551
- h.clock.now += 3_600_000;
552
- h.timers.fireAll();
553
- const asyncDir = path.join(h.root, "async-restart");
554
- fs.mkdirSync(asyncDir);
555
- h.launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "async-restart", asyncDir } });
556
- await flush();
557
- h.manager.stop();
558
- fs.writeFileSync(path.join(asyncDir, "status.json"), JSON.stringify({ runId: "async-restart", mode: "single", state: "complete", startedAt: h.clock.now, endedAt: h.clock.now }), "utf-8");
559
-
560
- const next = createScheduledRunManager({
561
- config: { scheduledRuns: { enabled: true } },
562
- storeRoot: path.join(h.root, "stores"),
563
- now: () => h.clock.now,
564
- randomId: () => "reconciled-id",
565
- timers: new FakeTimers(),
566
- launch: async () => ({ content: [{ type: "text", text: "unused" }], details: { mode: "management", results: [] } }),
567
- });
568
- const nextCtx = context(h.ctx.cwd, "session-b");
569
- next.bindSession(nextCtx);
570
- const history = await next.handleToolCall({ action: "schedule.history", id: "restart" }, nextCtx);
571
- assert.match(text(history), /completed.*async async-restart/);
572
- });
573
-
574
- it("records elapsed overlap and catch-up-none slots without an immediate rerun", async () => {
575
- const latest = harness();
576
- await latest.manager.handleToolCall({ action: "schedule.create", id: "overlap", every: "1h", catchUp: "latest", workflowScript: "return runs.run('main', { agent: 'worker' })" }, latest.ctx);
577
- latest.clock.now += 3_600_000;
578
- latest.timers.fireAll();
579
- latest.launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "long-run" } });
580
- await flush();
581
- latest.clock.now += 3 * 3_600_000;
582
- latest.timers.fireAll();
583
- assert.equal(latest.launches.length, 1, "an overlapping occurrence is skipped instead of queued");
584
- latest.manager.handleAsyncCompletion({ runId: "long-run", success: true });
585
- const latestHistory = await latest.manager.handleToolCall({ action: "schedule.history", id: "overlap" }, latest.ctx);
586
- assert.match(text(latestHistory), /skipped/);
587
- assert.match(text(latestHistory), /completed.*async long-run/);
588
- assert.match(text(await latest.manager.handleToolCall({ action: "schedule.show", id: "overlap" }, latest.ctx)), /2030-01-01T05:00:00.000Z/);
589
-
590
- const none = harness();
591
- await none.manager.handleToolCall({ action: "schedule.create", id: "none-overlap", every: "1h", catchUp: "none", workflowScript: "return runs.run('main', { agent: 'worker' })" }, none.ctx);
592
- none.clock.now += 3_600_000;
593
- none.timers.fireAll();
594
- none.launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "none-long-run" } });
595
- await flush();
596
- none.clock.now += 3 * 3_600_000;
597
- none.timers.fireAll();
598
- assert.equal(none.launches.length, 1);
599
- none.manager.handleAsyncCompletion({ runId: "none-long-run", success: true });
600
- assert.match(text(await none.manager.handleToolCall({ action: "schedule.history", id: "none-overlap" }, none.ctx)), /skipped/);
601
- assert.match(text(await none.manager.handleToolCall({ action: "schedule.show", id: "none-overlap" }, none.ctx)), /2030-01-01T05:00:00.000Z/);
602
- });
603
-
604
- it("manual run uses the normal async target and overlap skip prevents a second launch", async () => {
605
- const h = harness();
606
- await h.manager.handleToolCall({ action: "schedule.create", id: "manual", every: "1h", workflowScript: "return 1" }, h.ctx);
607
- const firstPromise = h.manager.handleToolCall({ action: "schedule.run", id: "manual" }, h.ctx);
608
- await flush();
609
- assert.equal(h.launches.length, 1);
610
- h.launches[0]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "workflow", results: [], asyncId: "workflow-1" } });
611
- assert.match(text(await firstPromise), /async workflow-1/);
612
- const second = await h.manager.handleToolCall({ action: "schedule.run", id: "manual" }, h.ctx);
613
- assert.match(text(second), /skipped/);
614
- assert.equal(h.launches.length, 1);
615
- });
616
-
617
- it("distinguishes failed launch from failed async completion", async () => {
618
- const h = harness();
619
- await h.manager.handleToolCall({ action: "schedule.create", id: "failures", every: "1h", workflowScript: "return runs.run('main', { agent: 'worker' })" }, h.ctx);
620
- const first = h.manager.handleToolCall({ action: "schedule.run", id: "failures" }, h.ctx);
621
- await flush();
622
- h.launches[0]!.resolve({ content: [{ type: "text", text: "spawn failed" }], details: { mode: "management", results: [] }, isError: true });
623
- assert.match(text(await first), /failed_launch/);
624
-
625
- const second = h.manager.handleToolCall({ action: "schedule.run", id: "failures" }, h.ctx);
626
- await flush();
627
- h.launches[1]!.resolve({ content: [{ type: "text", text: "Async" }], details: { mode: "single", results: [], asyncId: "async-fail" } });
628
- await second;
629
- h.manager.handleAsyncCompletion({ id: "async-fail", success: false, summary: "child failed" });
630
- const history = await h.manager.handleToolCall({ action: "schedule.history", id: "failures" }, h.ctx);
631
- assert.match(text(history), /failed_run.*async async-fail/);
632
- assert.match(text(history), /failed_launch/);
633
- });
634
- });