@code-yeongyu/senpi 2026.8.18-3 → 2026.8.19

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 (353) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/cli/args.d.ts +1 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +4 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +33 -4
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +293 -55
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  11. package/dist/core/compaction/branch-summarization.js +3 -0
  12. package/dist/core/compaction/branch-summarization.js.map +1 -1
  13. package/dist/core/compaction/compaction.d.ts +20 -3
  14. package/dist/core/compaction/compaction.d.ts.map +1 -1
  15. package/dist/core/compaction/compaction.js +182 -52
  16. package/dist/core/compaction/compaction.js.map +1 -1
  17. package/dist/core/extensions/builtin/claude-sdk-oauth/index.d.ts +3 -0
  18. package/dist/core/extensions/builtin/claude-sdk-oauth/index.d.ts.map +1 -1
  19. package/dist/core/extensions/builtin/claude-sdk-oauth/index.js +18 -1
  20. package/dist/core/extensions/builtin/claude-sdk-oauth/index.js.map +1 -1
  21. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.d.ts +1 -0
  22. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.d.ts.map +1 -1
  23. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.js +7 -1
  24. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.js.map +1 -1
  25. package/dist/core/extensions/builtin/claude-sdk-oauth/settings.d.ts +8 -0
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/settings.d.ts.map +1 -1
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/settings.js +18 -0
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/settings.js.map +1 -1
  29. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.d.ts +7 -0
  30. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.js +10 -1
  32. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.js.map +1 -1
  33. package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/cursor-cli-oauth/index.js +12 -1
  35. package/dist/core/extensions/builtin/cursor-cli-oauth/index.js.map +1 -1
  36. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts +12 -1
  37. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts.map +1 -1
  38. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js +20 -1
  39. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js.map +1 -1
  40. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.d.ts +11 -0
  41. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.d.ts.map +1 -1
  42. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js +7 -2
  43. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js.map +1 -1
  44. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +7 -2
  46. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  47. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  48. package/dist/core/extensions/builtin/index.js +2 -0
  49. package/dist/core/extensions/builtin/index.js.map +1 -1
  50. package/dist/core/extensions/builtin/loop/command-registration.d.ts +7 -0
  51. package/dist/core/extensions/builtin/loop/command-registration.d.ts.map +1 -0
  52. package/dist/core/extensions/builtin/loop/command-registration.js +13 -0
  53. package/dist/core/extensions/builtin/loop/command-registration.js.map +1 -0
  54. package/dist/core/extensions/builtin/loop/command.d.ts +41 -0
  55. package/dist/core/extensions/builtin/loop/command.d.ts.map +1 -0
  56. package/dist/core/extensions/builtin/loop/command.js +281 -0
  57. package/dist/core/extensions/builtin/loop/command.js.map +1 -0
  58. package/dist/core/extensions/builtin/loop/cron-planner.d.ts +23 -0
  59. package/dist/core/extensions/builtin/loop/cron-planner.d.ts.map +1 -0
  60. package/dist/core/extensions/builtin/loop/cron-planner.js +88 -0
  61. package/dist/core/extensions/builtin/loop/cron-planner.js.map +1 -0
  62. package/dist/core/extensions/builtin/loop/index.d.ts +134 -0
  63. package/dist/core/extensions/builtin/loop/index.d.ts.map +1 -0
  64. package/dist/core/extensions/builtin/loop/index.js +742 -0
  65. package/dist/core/extensions/builtin/loop/index.js.map +1 -0
  66. package/dist/core/extensions/builtin/loop/loopfile.d.ts +40 -0
  67. package/dist/core/extensions/builtin/loop/loopfile.d.ts.map +1 -0
  68. package/dist/core/extensions/builtin/loop/loopfile.js +102 -0
  69. package/dist/core/extensions/builtin/loop/loopfile.js.map +1 -0
  70. package/dist/core/extensions/builtin/loop/parse.d.ts +45 -0
  71. package/dist/core/extensions/builtin/loop/parse.d.ts.map +1 -0
  72. package/dist/core/extensions/builtin/loop/parse.js +92 -0
  73. package/dist/core/extensions/builtin/loop/parse.js.map +1 -0
  74. package/dist/core/extensions/builtin/loop/scheduler.d.ts +225 -0
  75. package/dist/core/extensions/builtin/loop/scheduler.d.ts.map +1 -0
  76. package/dist/core/extensions/builtin/loop/scheduler.js +708 -0
  77. package/dist/core/extensions/builtin/loop/scheduler.js.map +1 -0
  78. package/dist/core/extensions/builtin/loop/status.d.ts +24 -0
  79. package/dist/core/extensions/builtin/loop/status.d.ts.map +1 -0
  80. package/dist/core/extensions/builtin/loop/status.js +113 -0
  81. package/dist/core/extensions/builtin/loop/status.js.map +1 -0
  82. package/dist/core/extensions/builtin/loop/store.d.ts +49 -0
  83. package/dist/core/extensions/builtin/loop/store.d.ts.map +1 -0
  84. package/dist/core/extensions/builtin/loop/store.js +424 -0
  85. package/dist/core/extensions/builtin/loop/store.js.map +1 -0
  86. package/dist/core/extensions/builtin/loop/tick-prompt.d.ts +64 -0
  87. package/dist/core/extensions/builtin/loop/tick-prompt.d.ts.map +1 -0
  88. package/dist/core/extensions/builtin/loop/tick-prompt.js +183 -0
  89. package/dist/core/extensions/builtin/loop/tick-prompt.js.map +1 -0
  90. package/dist/core/extensions/builtin/loop/tools.d.ts +122 -0
  91. package/dist/core/extensions/builtin/loop/tools.d.ts.map +1 -0
  92. package/dist/core/extensions/builtin/loop/tools.js +155 -0
  93. package/dist/core/extensions/builtin/loop/tools.js.map +1 -0
  94. package/dist/core/extensions/builtin/loop/types.d.ts +206 -0
  95. package/dist/core/extensions/builtin/loop/types.d.ts.map +1 -0
  96. package/dist/core/extensions/builtin/loop/types.js +37 -0
  97. package/dist/core/extensions/builtin/loop/types.js.map +1 -0
  98. package/dist/core/extensions/index.d.ts +1 -1
  99. package/dist/core/extensions/index.d.ts.map +1 -1
  100. package/dist/core/extensions/index.js.map +1 -1
  101. package/dist/core/extensions/loader.d.ts.map +1 -1
  102. package/dist/core/extensions/loader.js +10 -5
  103. package/dist/core/extensions/loader.js.map +1 -1
  104. package/dist/core/extensions/types.d.ts +30 -3
  105. package/dist/core/extensions/types.d.ts.map +1 -1
  106. package/dist/core/extensions/types.js.map +1 -1
  107. package/dist/core/footer-data-provider.d.ts.map +1 -1
  108. package/dist/core/footer-data-provider.js +5 -10
  109. package/dist/core/footer-data-provider.js.map +1 -1
  110. package/dist/core/model-registry.d.ts +2 -0
  111. package/dist/core/model-registry.d.ts.map +1 -1
  112. package/dist/core/model-registry.js +4 -0
  113. package/dist/core/model-registry.js.map +1 -1
  114. package/dist/core/model-runtime.d.ts +7 -0
  115. package/dist/core/model-runtime.d.ts.map +1 -1
  116. package/dist/core/model-runtime.js +17 -0
  117. package/dist/core/model-runtime.js.map +1 -1
  118. package/dist/core/package-manager.d.ts.map +1 -1
  119. package/dist/core/package-manager.js +8 -4
  120. package/dist/core/package-manager.js.map +1 -1
  121. package/dist/core/provider-composer.d.ts +8 -0
  122. package/dist/core/provider-composer.d.ts.map +1 -1
  123. package/dist/core/provider-composer.js.map +1 -1
  124. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  125. package/dist/core/remote-catalog-provider.js +2 -1
  126. package/dist/core/remote-catalog-provider.js.map +1 -1
  127. package/dist/core/retry-fallback/chains.d.ts +1 -0
  128. package/dist/core/retry-fallback/chains.d.ts.map +1 -1
  129. package/dist/core/retry-fallback/chains.js +3 -0
  130. package/dist/core/retry-fallback/chains.js.map +1 -1
  131. package/dist/core/retry-fallback/controller.d.ts +2 -0
  132. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  133. package/dist/core/retry-fallback/controller.js.map +1 -1
  134. package/dist/core/retry-fallback/expansion.d.ts +8 -0
  135. package/dist/core/retry-fallback/expansion.d.ts.map +1 -1
  136. package/dist/core/retry-fallback/expansion.js +2 -0
  137. package/dist/core/retry-fallback/expansion.js.map +1 -1
  138. package/dist/core/settings-manager.d.ts +1 -1
  139. package/dist/core/settings-manager.d.ts.map +1 -1
  140. package/dist/core/settings-manager.js +14 -3
  141. package/dist/core/settings-manager.js.map +1 -1
  142. package/dist/core/skills.d.ts.map +1 -1
  143. package/dist/core/skills.js +51 -33
  144. package/dist/core/skills.js.map +1 -1
  145. package/dist/core/tools/edit.d.ts.map +1 -1
  146. package/dist/core/tools/edit.js +17 -2
  147. package/dist/core/tools/edit.js.map +1 -1
  148. package/dist/extensions/llama/index.d.ts.map +1 -1
  149. package/dist/extensions/llama/index.js +2 -0
  150. package/dist/extensions/llama/index.js.map +1 -1
  151. package/dist/extensions/llama/provider.d.ts.map +1 -1
  152. package/dist/extensions/llama/provider.js +6 -6
  153. package/dist/extensions/llama/provider.js.map +1 -1
  154. package/dist/index.d.ts +1 -1
  155. package/dist/index.d.ts.map +1 -1
  156. package/dist/index.js.map +1 -1
  157. package/dist/main.d.ts +4 -0
  158. package/dist/main.d.ts.map +1 -1
  159. package/dist/main.js +4 -4
  160. package/dist/main.js.map +1 -1
  161. package/dist/modes/interactive/components/settings-selector.js +1 -1
  162. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  163. package/dist/modes/interactive/editor-paste-transfer.d.ts +10 -1
  164. package/dist/modes/interactive/editor-paste-transfer.d.ts.map +1 -1
  165. package/dist/modes/interactive/editor-paste-transfer.js +27 -2
  166. package/dist/modes/interactive/editor-paste-transfer.js.map +1 -1
  167. package/dist/modes/interactive/interactive-mode.d.ts +69 -1
  168. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  169. package/dist/modes/interactive/interactive-mode.js +434 -63
  170. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  171. package/dist/modes/interactive/tips/catalog/dag-tips.d.ts +7 -0
  172. package/dist/modes/interactive/tips/catalog/dag-tips.d.ts.map +1 -0
  173. package/dist/modes/interactive/tips/catalog/dag-tips.js +57 -0
  174. package/dist/modes/interactive/tips/catalog/dag-tips.js.map +1 -0
  175. package/dist/modes/interactive/tips/catalog/memory-tips.d.ts +7 -0
  176. package/dist/modes/interactive/tips/catalog/memory-tips.d.ts.map +1 -0
  177. package/dist/modes/interactive/tips/catalog/memory-tips.js +100 -0
  178. package/dist/modes/interactive/tips/catalog/memory-tips.js.map +1 -0
  179. package/dist/modes/interactive/tips/registry.d.ts.map +1 -1
  180. package/dist/modes/interactive/tips/registry.js +4 -0
  181. package/dist/modes/interactive/tips/registry.js.map +1 -1
  182. package/dist/utils/management-http.d.ts +5 -3
  183. package/dist/utils/management-http.d.ts.map +1 -1
  184. package/dist/utils/management-http.js +14 -10
  185. package/dist/utils/management-http.js.map +1 -1
  186. package/docs/compaction.md +17 -0
  187. package/docs/custom-provider.md +5 -2
  188. package/docs/extensions.md +11 -2
  189. package/docs/llama-cpp.md +2 -0
  190. package/docs/models.md +8 -2
  191. package/docs/providers.md +5 -3
  192. package/docs/settings.md +22 -2
  193. package/docs/skills.md +4 -3
  194. package/examples/extensions/border-status-editor.ts +1 -1
  195. package/examples/extensions/git-checkpoint.ts +2 -2
  196. package/examples/extensions/notify.ts +3 -1
  197. package/examples/extensions/subagent/index.ts +6 -1
  198. package/examples/extensions/titlebar-spinner.ts +1 -1
  199. package/examples/rpc-extension-ui.ts +1 -1
  200. package/examples/sdk/06-extensions.ts +1 -1
  201. package/examples/sdk/README.md +1 -1
  202. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
  203. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  204. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts +3 -1
  205. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  206. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +51 -17
  207. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  208. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +4 -2
  209. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  210. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +5 -1
  211. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  212. package/node_modules/@earendil-works/pi-agent-core/dist/harness/skills.d.ts +3 -2
  213. package/node_modules/@earendil-works/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  214. package/node_modules/@earendil-works/pi-agent-core/dist/harness/skills.js +13 -3
  215. package/node_modules/@earendil-works/pi-agent-core/dist/harness/skills.js.map +1 -1
  216. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit.d.ts.map +1 -1
  217. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit.js +14 -1
  218. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/edit.js.map +1 -1
  219. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  220. package/node_modules/@earendil-works/pi-ai/README.md +5 -3
  221. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts +7 -1
  222. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  223. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +21 -4
  224. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  225. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts +2 -0
  226. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts.map +1 -1
  227. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +7 -1
  228. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js.map +1 -1
  229. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  230. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +39 -2
  231. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
  232. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.d.ts +22 -1
  233. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.d.ts.map +1 -1
  234. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.js +1 -1
  235. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.js.map +1 -1
  236. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +1 -0
  237. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  238. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +51 -2
  239. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  240. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.d.ts +2 -2
  241. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.d.ts.map +1 -1
  242. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +14 -11
  243. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js.map +1 -1
  244. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts +5 -2
  245. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts.map +1 -1
  246. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js +16 -0
  247. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js.map +1 -1
  248. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.d.ts +2 -2
  249. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.d.ts.map +1 -1
  250. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +13 -10
  251. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js.map +1 -1
  252. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.d.ts.map +1 -1
  253. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +4 -1
  254. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js.map +1 -1
  255. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  256. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +4 -1
  257. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  258. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts +1 -1
  259. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  260. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +45 -29
  261. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  262. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  263. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +8 -1
  264. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  265. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js +1 -1
  266. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.d.ts +4 -0
  268. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.d.ts.map +1 -1
  269. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +18 -9
  270. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js.map +1 -1
  271. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/device-code.d.ts +1 -0
  272. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/device-code.d.ts.map +1 -1
  273. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/device-code.js +1 -1
  274. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/device-code.js.map +1 -1
  275. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  276. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js +22 -9
  277. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js.map +1 -1
  278. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +2 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  280. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  281. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  282. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  283. package/node_modules/@earendil-works/pi-ai/dist/providers/data/anthropic.json +1 -1
  284. package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
  285. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  286. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  287. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  289. package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan-individual.json +1 -1
  290. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  291. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xai.json +1 -1
  292. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  293. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  294. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  295. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi.json +1 -1
  296. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  297. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  298. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.d.ts +1 -1
  299. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.d.ts.map +1 -1
  300. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.js +1 -5
  301. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.js.map +1 -1
  302. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +28 -4
  303. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  304. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  305. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  306. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +15 -0
  307. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  308. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.d.ts +2 -0
  309. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.d.ts.map +1 -1
  310. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.js +7 -0
  311. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.js.map +1 -1
  312. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts +2 -1
  313. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
  314. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js +2 -0
  315. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js.map +1 -1
  316. package/node_modules/@earendil-works/pi-ai/package.json +2 -3
  317. package/node_modules/@earendil-works/pi-pty/dist/loader.d.ts +6 -0
  318. package/node_modules/@earendil-works/pi-pty/dist/loader.d.ts.map +1 -1
  319. package/node_modules/@earendil-works/pi-pty/dist/loader.js +11 -0
  320. package/node_modules/@earendil-works/pi-pty/dist/loader.js.map +1 -1
  321. package/node_modules/@earendil-works/pi-pty/dist/quarantine.d.ts +15 -0
  322. package/node_modules/@earendil-works/pi-pty/dist/quarantine.d.ts.map +1 -0
  323. package/node_modules/@earendil-works/pi-pty/dist/quarantine.js +27 -0
  324. package/node_modules/@earendil-works/pi-pty/dist/quarantine.js.map +1 -0
  325. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  326. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  327. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +73 -1
  328. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  329. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +173 -17
  330. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  331. package/node_modules/@earendil-works/pi-tui/dist/components/text.d.ts.map +1 -1
  332. package/node_modules/@earendil-works/pi-tui/dist/components/text.js +5 -4
  333. package/node_modules/@earendil-works/pi-tui/dist/components/text.js.map +1 -1
  334. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts +69 -0
  335. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +1 -1
  336. package/node_modules/@earendil-works/pi-tui/dist/editor-component.js.map +1 -1
  337. package/node_modules/@earendil-works/pi-tui/dist/image-markers.d.ts +46 -0
  338. package/node_modules/@earendil-works/pi-tui/dist/image-markers.d.ts.map +1 -0
  339. package/node_modules/@earendil-works/pi-tui/dist/image-markers.js +130 -0
  340. package/node_modules/@earendil-works/pi-tui/dist/image-markers.js.map +1 -0
  341. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -0
  342. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
  343. package/node_modules/@earendil-works/pi-tui/dist/index.js +2 -0
  344. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
  345. package/node_modules/@earendil-works/pi-tui/dist/paste-markers.d.ts +3 -0
  346. package/node_modules/@earendil-works/pi-tui/dist/paste-markers.d.ts.map +1 -1
  347. package/node_modules/@earendil-works/pi-tui/dist/paste-markers.js +23 -7
  348. package/node_modules/@earendil-works/pi-tui/dist/paste-markers.js.map +1 -1
  349. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.d.ts.map +1 -1
  350. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js +5 -1
  351. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js.map +1 -1
  352. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  353. package/package.json +7 -7
@@ -0,0 +1,281 @@
1
+ /**
2
+ * `/loop` command handler: native argument parsing and the user-facing surfaces.
3
+ *
4
+ * Parsing is pure native code (`parse.ts`) - the command never expands into a
5
+ * model-facing parsing prompt. Starting a loop always goes through the extension's
6
+ * controller, which persists and arms the schedule BEFORE dispatching the first tick,
7
+ * so a failed first turn can never silently lose the job.
8
+ *
9
+ * Headless rejection: print/json modes have no interactive session, so the command
10
+ * refuses with a one-line message and arms nothing.
11
+ */
12
+ import { parseLoopArgs } from "./parse.js";
13
+ export const LOOP_ARGUMENT_HINT = "[interval] [prompt] | stop [id|all] | status | pause | resume";
14
+ export const LOOP_COMMAND_DESCRIPTION = "Repeat a prompt on a fixed interval or a self-paced schedule (e.g. /loop 5m check the deploy)";
15
+ const LOOP_SUBCOMMANDS = ["stop", "status", "pause", "resume"];
16
+ const LOOP_HEADLESS_REJECTION = "/loop needs an interactive session; it is not available in print mode, so nothing was armed.";
17
+ /** Headless modes: single-shot output streams with no interactive transcript. */
18
+ function isHeadlessMode(mode) {
19
+ return mode === "print" || mode === "json";
20
+ }
21
+ /**
22
+ * Argument completions for the subcommand keywords, modeled on the todo extension's
23
+ * verb completions: prefix-filtered, `null` when nothing matches.
24
+ */
25
+ export function completeLoopArguments(argumentPrefix) {
26
+ const prefix = argumentPrefix.trim().toLowerCase();
27
+ const matches = LOOP_SUBCOMMANDS.filter((subcommand) => subcommand.startsWith(prefix));
28
+ if (matches.length === 0)
29
+ return null;
30
+ return matches.map((subcommand) => ({ value: subcommand, label: subcommand }));
31
+ }
32
+ function formatExpiry(expiresAt) {
33
+ return expiresAt === undefined ? "after 7 days" : new Date(expiresAt).toISOString();
34
+ }
35
+ /** Fixed-loop confirmation: id, cron, effective cadence, rounding notice, expiry, stop command. */
36
+ export function formatFixedLoopConfirmation(outcome, requestedRaw) {
37
+ const cadence = outcome.effectiveCadence ?? "on schedule";
38
+ const parenthetical = outcome.roundingNotice === undefined ? `every ${cadence}` : `every ${cadence}; requested ${requestedRaw}`;
39
+ const lines = [`Loop ${outcome.loopId} scheduled as \`${outcome.cronExpression ?? ""}\` (${parenthetical}).`];
40
+ if (outcome.roundingNotice !== undefined)
41
+ lines.push(outcome.roundingNotice);
42
+ lines.push(`It expires automatically at ${formatExpiry(outcome.expiresAt)} (7 days).`);
43
+ lines.push(`Stop it with \`/loop stop ${outcome.loopId}\`.`);
44
+ lines.push("Running the first tick now.");
45
+ return lines.join("\n");
46
+ }
47
+ /** Dynamic-loop confirmation, including the supersession line when one replaced another. */
48
+ export function formatDynamicLoopConfirmation(outcome) {
49
+ const lines = [
50
+ `Loop ${outcome.loopId} started in dynamic mode: the model paces each iteration with \`schedule_wakeup\`.`,
51
+ ];
52
+ if (outcome.supersededLoopId !== undefined)
53
+ lines.push(`Superseded dynamic loop ${outcome.supersededLoopId}.`);
54
+ lines.push(`It expires automatically at ${formatExpiry(outcome.expiresAt)} (7 days).`);
55
+ lines.push(`Stop it with \`/loop stop ${outcome.loopId}\` or a \`schedule_wakeup\` call with \`{ stop: true }\`.`);
56
+ lines.push("Running the first iteration now.");
57
+ return lines.join("\n");
58
+ }
59
+ /** Bare-invocation confirmation; the mode comes from the entry the controller created. */
60
+ export function formatBareLoopConfirmation(outcome, entry) {
61
+ const mode = entry?.kind === "fixed" ? `fixed, every ${entry.effectiveInterval.human}` : "dynamic pacing";
62
+ const lines = [`Loop ${outcome.loopId} started (${mode}).`];
63
+ lines.push(`It expires automatically at ${formatExpiry(outcome.expiresAt)} (7 days).`);
64
+ lines.push(`Stop it with \`/loop stop ${outcome.loopId}\`.`);
65
+ lines.push("Running the first tick now.");
66
+ return lines.join("\n");
67
+ }
68
+ function describeLoopEntry(entry) {
69
+ const mode = entry.kind === "fixed"
70
+ ? `fixed, \`${entry.cronExpression}\` (every ${entry.effectiveInterval.human})`
71
+ : "dynamic";
72
+ const paused = entry.phase === "suspended" ? " · paused" : "";
73
+ return `- ${entry.id} (${mode}) · expires ${formatExpiry(entry.expiresAt)}${paused}`;
74
+ }
75
+ /**
76
+ * The `/loop status` listing: the live status line (countdown + stop affordance) plus
77
+ * one row per active loop.
78
+ */
79
+ export function formatLoopStatusListing(statusLine, state) {
80
+ const active = Object.values(state.entries).filter((entry) => entry.phase !== "ended");
81
+ if (active.length === 0)
82
+ return "No active loops.";
83
+ const lines = statusLine === undefined ? [] : [statusLine];
84
+ lines.push("Active loops:");
85
+ for (const entry of active)
86
+ lines.push(describeLoopEntry(entry));
87
+ return lines.join("\n");
88
+ }
89
+ function activeLoopIds(state) {
90
+ return Object.values(state.entries)
91
+ .filter((entry) => entry.phase !== "ended")
92
+ .map((entry) => entry.id);
93
+ }
94
+ /**
95
+ * Resolves a stop/pause/resume target. An implicit target names the single active loop;
96
+ * with several armed loops the command must not guess, so it asks for disambiguation.
97
+ */
98
+ function resolveCommandTarget(target, state) {
99
+ if (target.type === "all")
100
+ return { action: "apply", value: "all" };
101
+ if (target.type === "id")
102
+ return { action: "apply", value: target.id };
103
+ const ids = activeLoopIds(state);
104
+ if (ids.length === 0)
105
+ return { action: "none" };
106
+ if (ids.length === 1)
107
+ return { action: "apply", value: ids[0] };
108
+ return { action: "ambiguous", ids };
109
+ }
110
+ function notifyAmbiguous(ctx, verb, ids) {
111
+ const lines = ["Multiple loops are active:", ...ids.map((id) => `- ${id}`)];
112
+ lines.push(`Use \`/loop ${verb} <id>\` or \`/loop ${verb} all\`.`);
113
+ ctx.ui.notify(lines.join("\n"), "warning");
114
+ }
115
+ function notifyUnknownId(ctx, id, state) {
116
+ const ids = activeLoopIds(state);
117
+ const lines = [`No active loop with id "${id}".`];
118
+ if (ids.length > 0)
119
+ lines.push(`Active loops: ${ids.join(", ")}.`);
120
+ ctx.ui.notify(lines.join("\n"), "warning");
121
+ }
122
+ async function runStop(ctx, deps, target) {
123
+ const resolution = resolveCommandTarget(target, deps.controller.getState());
124
+ if (resolution.action === "none") {
125
+ ctx.ui.notify("No active loops.", "warning");
126
+ return;
127
+ }
128
+ if (resolution.action === "ambiguous") {
129
+ notifyAmbiguous(ctx, "stop", resolution.ids);
130
+ return;
131
+ }
132
+ const affected = await deps.controller.stop(resolution.value, "user-stop");
133
+ if (resolution.value === "all") {
134
+ if (affected.length === 0) {
135
+ ctx.ui.notify("No active loops.", "warning");
136
+ return;
137
+ }
138
+ const noun = affected.length === 1 ? "loop" : "loops";
139
+ ctx.ui.notify(`Stopped ${affected.length} ${noun}: ${affected.join(", ")}.`, "info");
140
+ return;
141
+ }
142
+ if (affected.length === 0) {
143
+ notifyUnknownId(ctx, resolution.value, deps.controller.getState());
144
+ return;
145
+ }
146
+ ctx.ui.notify(`Stopped loop ${resolution.value}.`, "info");
147
+ }
148
+ async function runPause(ctx, deps, target) {
149
+ const resolution = resolveCommandTarget(target, deps.controller.getState());
150
+ if (resolution.action === "none") {
151
+ ctx.ui.notify("No active loops.", "warning");
152
+ return;
153
+ }
154
+ if (resolution.action === "ambiguous") {
155
+ notifyAmbiguous(ctx, "pause", resolution.ids);
156
+ return;
157
+ }
158
+ const affected = await deps.controller.pause(resolution.value);
159
+ if (resolution.value === "all") {
160
+ if (affected.length === 0) {
161
+ ctx.ui.notify("No pausable loops.", "warning");
162
+ return;
163
+ }
164
+ const noun = affected.length === 1 ? "loop" : "loops";
165
+ ctx.ui.notify(`Paused ${affected.length} ${noun}: ${affected.join(", ")}.`, "info");
166
+ return;
167
+ }
168
+ if (affected.length === 0) {
169
+ ctx.ui.notify(`Loop ${resolution.value} is not pausable (already paused or ended).`, "warning");
170
+ return;
171
+ }
172
+ ctx.ui.notify(`Paused loop ${resolution.value}.`, "info");
173
+ }
174
+ async function runResume(ctx, deps, target) {
175
+ const resolution = resolveCommandTarget(target, deps.controller.getState());
176
+ if (resolution.action === "none") {
177
+ ctx.ui.notify("No active loops.", "warning");
178
+ return;
179
+ }
180
+ if (resolution.action === "ambiguous") {
181
+ notifyAmbiguous(ctx, "resume", resolution.ids);
182
+ return;
183
+ }
184
+ const affected = await deps.controller.resume(resolution.value);
185
+ if (resolution.value === "all") {
186
+ if (affected.length === 0) {
187
+ ctx.ui.notify("No paused loops to resume.", "warning");
188
+ return;
189
+ }
190
+ const noun = affected.length === 1 ? "loop" : "loops";
191
+ ctx.ui.notify(`Resumed ${affected.length} ${noun}: ${affected.join(", ")}.`, "info");
192
+ return;
193
+ }
194
+ if (affected.length === 0) {
195
+ ctx.ui.notify(`Loop ${resolution.value} is not paused.`, "warning");
196
+ return;
197
+ }
198
+ ctx.ui.notify(`Resumed loop ${resolution.value}.`, "info");
199
+ }
200
+ async function reportCreateOutcome(ctx, deps, outcome, format) {
201
+ const entry = deps.controller.getState().entries[outcome.loopId];
202
+ ctx.ui.notify(format(entry), "info");
203
+ }
204
+ /** The `/loop` command entry point. */
205
+ export async function runLoopCommand(rawArgs, ctx, deps) {
206
+ if (isHeadlessMode(ctx.mode)) {
207
+ ctx.ui.notify(LOOP_HEADLESS_REJECTION, "warning");
208
+ // Headless hosts install a no-op UI context, so `notify` alone leaves the refusal
209
+ // invisible (empty output, exit 0). Write to stderr as well so the message is actually
210
+ // seen; real stdout stays reserved for `-p` result data.
211
+ process.stderr.write(`${LOOP_HEADLESS_REJECTION}\n`);
212
+ return;
213
+ }
214
+ const parsed = parseLoopArgs(rawArgs);
215
+ try {
216
+ switch (parsed.kind) {
217
+ case "invalid": {
218
+ ctx.ui.notify(parsed.usage, "warning");
219
+ return;
220
+ }
221
+ case "fixed": {
222
+ const outcome = await deps.controller.startFixed({
223
+ originalArgs: parsed.originalArgs,
224
+ prompt: parsed.prompt,
225
+ requestedInterval: parsed.interval,
226
+ });
227
+ if (!outcome.ok) {
228
+ ctx.ui.notify(outcome.message, "error");
229
+ return;
230
+ }
231
+ await reportCreateOutcome(ctx, deps, outcome, () => formatFixedLoopConfirmation(outcome, parsed.interval.raw));
232
+ return;
233
+ }
234
+ case "dynamic": {
235
+ const outcome = await deps.controller.startDynamic({
236
+ originalArgs: parsed.originalArgs,
237
+ prompt: parsed.prompt,
238
+ });
239
+ if (!outcome.ok) {
240
+ ctx.ui.notify(outcome.message, "error");
241
+ return;
242
+ }
243
+ await reportCreateOutcome(ctx, deps, outcome, () => formatDynamicLoopConfirmation(outcome));
244
+ return;
245
+ }
246
+ case "bare": {
247
+ const outcome = await deps.controller.startBare({
248
+ originalArgs: parsed.originalArgs,
249
+ ...(parsed.interval === undefined ? {} : { interval: parsed.interval }),
250
+ });
251
+ if (!outcome.ok) {
252
+ ctx.ui.notify(outcome.message, "error");
253
+ return;
254
+ }
255
+ await reportCreateOutcome(ctx, deps, outcome, (entry) => formatBareLoopConfirmation(outcome, entry));
256
+ return;
257
+ }
258
+ case "stop": {
259
+ await runStop(ctx, deps, parsed.target);
260
+ return;
261
+ }
262
+ case "pause": {
263
+ await runPause(ctx, deps, parsed.target);
264
+ return;
265
+ }
266
+ case "resume": {
267
+ await runResume(ctx, deps, parsed.target);
268
+ return;
269
+ }
270
+ case "status": {
271
+ ctx.ui.notify(formatLoopStatusListing(deps.controller.statusLine(), deps.controller.getState()), "info");
272
+ return;
273
+ }
274
+ }
275
+ }
276
+ catch (error) {
277
+ // A failed command must surface, not vanish: notify and stop.
278
+ ctx.ui.notify(`/loop command failed: ${error instanceof Error ? error.message : String(error)}`, "error");
279
+ }
280
+ }
281
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/command.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `/loop` command handler: native argument parsing and the user-facing surfaces.\n *\n * Parsing is pure native code (`parse.ts`) - the command never expands into a\n * model-facing parsing prompt. Starting a loop always goes through the extension's\n * controller, which persists and arms the schedule BEFORE dispatching the first tick,\n * so a failed first turn can never silently lose the job.\n *\n * Headless rejection: print/json modes have no interactive session, so the command\n * refuses with a one-line message and arms nothing.\n */\n\nimport type { ExtensionCommandContext } from \"../../types.ts\";\nimport type { LoopController, LoopCreateOk } from \"./index.ts\";\nimport { type LoopTarget, parseLoopArgs } from \"./parse.ts\";\nimport type { CronEntry, LoopId, LoopState } from \"./types.ts\";\n\nexport const LOOP_ARGUMENT_HINT = \"[interval] [prompt] | stop [id|all] | status | pause | resume\";\nexport const LOOP_COMMAND_DESCRIPTION =\n\t\"Repeat a prompt on a fixed interval or a self-paced schedule (e.g. /loop 5m check the deploy)\";\n\nconst LOOP_SUBCOMMANDS = [\"stop\", \"status\", \"pause\", \"resume\"] as const;\n\nconst LOOP_HEADLESS_REJECTION =\n\t\"/loop needs an interactive session; it is not available in print mode, so nothing was armed.\";\n\nexport interface LoopCommandDeps {\n\treadonly controller: LoopController;\n}\n\n/** Headless modes: single-shot output streams with no interactive transcript. */\nfunction isHeadlessMode(mode: ExtensionCommandContext[\"mode\"]): boolean {\n\treturn mode === \"print\" || mode === \"json\";\n}\n\n/**\n * Argument completions for the subcommand keywords, modeled on the todo extension's\n * verb completions: prefix-filtered, `null` when nothing matches.\n */\nexport function completeLoopArguments(argumentPrefix: string): Array<{ value: string; label: string }> | null {\n\tconst prefix = argumentPrefix.trim().toLowerCase();\n\tconst matches = LOOP_SUBCOMMANDS.filter((subcommand) => subcommand.startsWith(prefix));\n\tif (matches.length === 0) return null;\n\treturn matches.map((subcommand) => ({ value: subcommand, label: subcommand }));\n}\n\nfunction formatExpiry(expiresAt: number | undefined): string {\n\treturn expiresAt === undefined ? \"after 7 days\" : new Date(expiresAt).toISOString();\n}\n\n/** Fixed-loop confirmation: id, cron, effective cadence, rounding notice, expiry, stop command. */\nexport function formatFixedLoopConfirmation(outcome: LoopCreateOk, requestedRaw: string): string {\n\tconst cadence = outcome.effectiveCadence ?? \"on schedule\";\n\tconst parenthetical =\n\t\toutcome.roundingNotice === undefined ? `every ${cadence}` : `every ${cadence}; requested ${requestedRaw}`;\n\tconst lines = [`Loop ${outcome.loopId} scheduled as \\`${outcome.cronExpression ?? \"\"}\\` (${parenthetical}).`];\n\tif (outcome.roundingNotice !== undefined) lines.push(outcome.roundingNotice);\n\tlines.push(`It expires automatically at ${formatExpiry(outcome.expiresAt)} (7 days).`);\n\tlines.push(`Stop it with \\`/loop stop ${outcome.loopId}\\`.`);\n\tlines.push(\"Running the first tick now.\");\n\treturn lines.join(\"\\n\");\n}\n\n/** Dynamic-loop confirmation, including the supersession line when one replaced another. */\nexport function formatDynamicLoopConfirmation(outcome: LoopCreateOk): string {\n\tconst lines = [\n\t\t`Loop ${outcome.loopId} started in dynamic mode: the model paces each iteration with \\`schedule_wakeup\\`.`,\n\t];\n\tif (outcome.supersededLoopId !== undefined) lines.push(`Superseded dynamic loop ${outcome.supersededLoopId}.`);\n\tlines.push(`It expires automatically at ${formatExpiry(outcome.expiresAt)} (7 days).`);\n\tlines.push(`Stop it with \\`/loop stop ${outcome.loopId}\\` or a \\`schedule_wakeup\\` call with \\`{ stop: true }\\`.`);\n\tlines.push(\"Running the first iteration now.\");\n\treturn lines.join(\"\\n\");\n}\n\n/** Bare-invocation confirmation; the mode comes from the entry the controller created. */\nexport function formatBareLoopConfirmation(outcome: LoopCreateOk, entry: CronEntry | undefined): string {\n\tconst mode = entry?.kind === \"fixed\" ? `fixed, every ${entry.effectiveInterval.human}` : \"dynamic pacing\";\n\tconst lines = [`Loop ${outcome.loopId} started (${mode}).`];\n\tlines.push(`It expires automatically at ${formatExpiry(outcome.expiresAt)} (7 days).`);\n\tlines.push(`Stop it with \\`/loop stop ${outcome.loopId}\\`.`);\n\tlines.push(\"Running the first tick now.\");\n\treturn lines.join(\"\\n\");\n}\n\nfunction describeLoopEntry(entry: CronEntry): string {\n\tconst mode =\n\t\tentry.kind === \"fixed\"\n\t\t\t? `fixed, \\`${entry.cronExpression}\\` (every ${entry.effectiveInterval.human})`\n\t\t\t: \"dynamic\";\n\tconst paused = entry.phase === \"suspended\" ? \" · paused\" : \"\";\n\treturn `- ${entry.id} (${mode}) · expires ${formatExpiry(entry.expiresAt)}${paused}`;\n}\n\n/**\n * The `/loop status` listing: the live status line (countdown + stop affordance) plus\n * one row per active loop.\n */\nexport function formatLoopStatusListing(statusLine: string | undefined, state: LoopState): string {\n\tconst active = Object.values(state.entries).filter((entry) => entry.phase !== \"ended\");\n\tif (active.length === 0) return \"No active loops.\";\n\tconst lines = statusLine === undefined ? [] : [statusLine];\n\tlines.push(\"Active loops:\");\n\tfor (const entry of active) lines.push(describeLoopEntry(entry));\n\treturn lines.join(\"\\n\");\n}\n\nfunction activeLoopIds(state: LoopState): readonly LoopId[] {\n\treturn Object.values(state.entries)\n\t\t.filter((entry) => entry.phase !== \"ended\")\n\t\t.map((entry) => entry.id);\n}\n\ntype TargetResolution =\n\t| { readonly action: \"apply\"; readonly value: LoopId | \"all\" }\n\t| { readonly action: \"none\" }\n\t| { readonly action: \"ambiguous\"; readonly ids: readonly LoopId[] };\n\n/**\n * Resolves a stop/pause/resume target. An implicit target names the single active loop;\n * with several armed loops the command must not guess, so it asks for disambiguation.\n */\nfunction resolveCommandTarget(target: LoopTarget, state: LoopState): TargetResolution {\n\tif (target.type === \"all\") return { action: \"apply\", value: \"all\" };\n\tif (target.type === \"id\") return { action: \"apply\", value: target.id };\n\tconst ids = activeLoopIds(state);\n\tif (ids.length === 0) return { action: \"none\" };\n\tif (ids.length === 1) return { action: \"apply\", value: ids[0] };\n\treturn { action: \"ambiguous\", ids };\n}\n\nfunction notifyAmbiguous(\n\tctx: ExtensionCommandContext,\n\tverb: \"stop\" | \"pause\" | \"resume\",\n\tids: readonly LoopId[],\n): void {\n\tconst lines = [\"Multiple loops are active:\", ...ids.map((id) => `- ${id}`)];\n\tlines.push(`Use \\`/loop ${verb} <id>\\` or \\`/loop ${verb} all\\`.`);\n\tctx.ui.notify(lines.join(\"\\n\"), \"warning\");\n}\n\nfunction notifyUnknownId(ctx: ExtensionCommandContext, id: string, state: LoopState): void {\n\tconst ids = activeLoopIds(state);\n\tconst lines = [`No active loop with id \"${id}\".`];\n\tif (ids.length > 0) lines.push(`Active loops: ${ids.join(\", \")}.`);\n\tctx.ui.notify(lines.join(\"\\n\"), \"warning\");\n}\n\nasync function runStop(ctx: ExtensionCommandContext, deps: LoopCommandDeps, target: LoopTarget): Promise<void> {\n\tconst resolution = resolveCommandTarget(target, deps.controller.getState());\n\tif (resolution.action === \"none\") {\n\t\tctx.ui.notify(\"No active loops.\", \"warning\");\n\t\treturn;\n\t}\n\tif (resolution.action === \"ambiguous\") {\n\t\tnotifyAmbiguous(ctx, \"stop\", resolution.ids);\n\t\treturn;\n\t}\n\tconst affected = await deps.controller.stop(resolution.value, \"user-stop\");\n\tif (resolution.value === \"all\") {\n\t\tif (affected.length === 0) {\n\t\t\tctx.ui.notify(\"No active loops.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tconst noun = affected.length === 1 ? \"loop\" : \"loops\";\n\t\tctx.ui.notify(`Stopped ${affected.length} ${noun}: ${affected.join(\", \")}.`, \"info\");\n\t\treturn;\n\t}\n\tif (affected.length === 0) {\n\t\tnotifyUnknownId(ctx, resolution.value, deps.controller.getState());\n\t\treturn;\n\t}\n\tctx.ui.notify(`Stopped loop ${resolution.value}.`, \"info\");\n}\n\nasync function runPause(ctx: ExtensionCommandContext, deps: LoopCommandDeps, target: LoopTarget): Promise<void> {\n\tconst resolution = resolveCommandTarget(target, deps.controller.getState());\n\tif (resolution.action === \"none\") {\n\t\tctx.ui.notify(\"No active loops.\", \"warning\");\n\t\treturn;\n\t}\n\tif (resolution.action === \"ambiguous\") {\n\t\tnotifyAmbiguous(ctx, \"pause\", resolution.ids);\n\t\treturn;\n\t}\n\tconst affected = await deps.controller.pause(resolution.value);\n\tif (resolution.value === \"all\") {\n\t\tif (affected.length === 0) {\n\t\t\tctx.ui.notify(\"No pausable loops.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tconst noun = affected.length === 1 ? \"loop\" : \"loops\";\n\t\tctx.ui.notify(`Paused ${affected.length} ${noun}: ${affected.join(\", \")}.`, \"info\");\n\t\treturn;\n\t}\n\tif (affected.length === 0) {\n\t\tctx.ui.notify(`Loop ${resolution.value} is not pausable (already paused or ended).`, \"warning\");\n\t\treturn;\n\t}\n\tctx.ui.notify(`Paused loop ${resolution.value}.`, \"info\");\n}\n\nasync function runResume(ctx: ExtensionCommandContext, deps: LoopCommandDeps, target: LoopTarget): Promise<void> {\n\tconst resolution = resolveCommandTarget(target, deps.controller.getState());\n\tif (resolution.action === \"none\") {\n\t\tctx.ui.notify(\"No active loops.\", \"warning\");\n\t\treturn;\n\t}\n\tif (resolution.action === \"ambiguous\") {\n\t\tnotifyAmbiguous(ctx, \"resume\", resolution.ids);\n\t\treturn;\n\t}\n\tconst affected = await deps.controller.resume(resolution.value);\n\tif (resolution.value === \"all\") {\n\t\tif (affected.length === 0) {\n\t\t\tctx.ui.notify(\"No paused loops to resume.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tconst noun = affected.length === 1 ? \"loop\" : \"loops\";\n\t\tctx.ui.notify(`Resumed ${affected.length} ${noun}: ${affected.join(\", \")}.`, \"info\");\n\t\treturn;\n\t}\n\tif (affected.length === 0) {\n\t\tctx.ui.notify(`Loop ${resolution.value} is not paused.`, \"warning\");\n\t\treturn;\n\t}\n\tctx.ui.notify(`Resumed loop ${resolution.value}.`, \"info\");\n}\n\nasync function reportCreateOutcome(\n\tctx: ExtensionCommandContext,\n\tdeps: LoopCommandDeps,\n\toutcome: LoopCreateOk,\n\tformat: (entry: CronEntry | undefined) => string,\n): Promise<void> {\n\tconst entry = deps.controller.getState().entries[outcome.loopId];\n\tctx.ui.notify(format(entry), \"info\");\n}\n\n/** The `/loop` command entry point. */\nexport async function runLoopCommand(\n\trawArgs: string,\n\tctx: ExtensionCommandContext,\n\tdeps: LoopCommandDeps,\n): Promise<void> {\n\tif (isHeadlessMode(ctx.mode)) {\n\t\tctx.ui.notify(LOOP_HEADLESS_REJECTION, \"warning\");\n\t\t// Headless hosts install a no-op UI context, so `notify` alone leaves the refusal\n\t\t// invisible (empty output, exit 0). Write to stderr as well so the message is actually\n\t\t// seen; real stdout stays reserved for `-p` result data.\n\t\tprocess.stderr.write(`${LOOP_HEADLESS_REJECTION}\\n`);\n\t\treturn;\n\t}\n\n\tconst parsed = parseLoopArgs(rawArgs);\n\ttry {\n\t\tswitch (parsed.kind) {\n\t\t\tcase \"invalid\": {\n\t\t\t\tctx.ui.notify(parsed.usage, \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"fixed\": {\n\t\t\t\tconst outcome = await deps.controller.startFixed({\n\t\t\t\t\toriginalArgs: parsed.originalArgs,\n\t\t\t\t\tprompt: parsed.prompt,\n\t\t\t\t\trequestedInterval: parsed.interval,\n\t\t\t\t});\n\t\t\t\tif (!outcome.ok) {\n\t\t\t\t\tctx.ui.notify(outcome.message, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait reportCreateOutcome(ctx, deps, outcome, () =>\n\t\t\t\t\tformatFixedLoopConfirmation(outcome, parsed.interval.raw),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"dynamic\": {\n\t\t\t\tconst outcome = await deps.controller.startDynamic({\n\t\t\t\t\toriginalArgs: parsed.originalArgs,\n\t\t\t\t\tprompt: parsed.prompt,\n\t\t\t\t});\n\t\t\t\tif (!outcome.ok) {\n\t\t\t\t\tctx.ui.notify(outcome.message, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait reportCreateOutcome(ctx, deps, outcome, () => formatDynamicLoopConfirmation(outcome));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"bare\": {\n\t\t\t\tconst outcome = await deps.controller.startBare({\n\t\t\t\t\toriginalArgs: parsed.originalArgs,\n\t\t\t\t\t...(parsed.interval === undefined ? {} : { interval: parsed.interval }),\n\t\t\t\t});\n\t\t\t\tif (!outcome.ok) {\n\t\t\t\t\tctx.ui.notify(outcome.message, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait reportCreateOutcome(ctx, deps, outcome, (entry) => formatBareLoopConfirmation(outcome, entry));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"stop\": {\n\t\t\t\tawait runStop(ctx, deps, parsed.target);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"pause\": {\n\t\t\t\tawait runPause(ctx, deps, parsed.target);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"resume\": {\n\t\t\t\tawait runResume(ctx, deps, parsed.target);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcase \"status\": {\n\t\t\t\tctx.ui.notify(formatLoopStatusListing(deps.controller.statusLine(), deps.controller.getState()), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\t// A failed command must surface, not vanish: notify and stop.\n\t\tctx.ui.notify(`/loop command failed: ${error instanceof Error ? error.message : String(error)}`, \"error\");\n\t}\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import type { EffectiveInterval, RequestedInterval } from "./types.ts";
2
+ export type { EffectiveInterval, RequestedInterval };
3
+ /**
4
+ * Converts a user-requested interval into an effective cadence the scheduler
5
+ * can express with the restricted display cron forms. Seconds are coarsened to
6
+ * minutes; large minute/hour values roll up to hours/days with half-up
7
+ * rounding. Whenever the effective value differs from the request, the result
8
+ * carries a notice naming both the requested and effective cadence.
9
+ */
10
+ export declare function normalizeInterval(requested: RequestedInterval): EffectiveInterval & {
11
+ intervalMs: number;
12
+ };
13
+ /**
14
+ * Returns a restricted 5-field cron expression for DISPLAY ONLY. The scheduler
15
+ * uses intervalMs + absolute nextFireAt; this string is shown to the user.
16
+ */
17
+ export declare function describeCron(effective: Pick<EffectiveInterval, "value" | "unit">): string;
18
+ /**
19
+ * Computes the absolute timestamp of the next scheduled fire. Pure: takes
20
+ * nowMs as a parameter and never reads the clock internally.
21
+ */
22
+ export declare function computeNextFireAt(nowMs: number, intervalMs: number): number;
23
+ //# sourceMappingURL=cron-planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-planner.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/cron-planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAyB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAI9F,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;AAUrD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAwD1G;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM,CASzF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3E"}
@@ -0,0 +1,88 @@
1
+ const MS_PER_MINUTE = 60_000;
2
+ const MS_PER_HOUR = 60 * MS_PER_MINUTE;
3
+ const MS_PER_DAY = 24 * MS_PER_HOUR;
4
+ function pluralize(count, singular, plural) {
5
+ return count === 1 ? `${count} ${singular}` : `${count} ${plural}`;
6
+ }
7
+ /**
8
+ * Converts a user-requested interval into an effective cadence the scheduler
9
+ * can express with the restricted display cron forms. Seconds are coarsened to
10
+ * minutes; large minute/hour values roll up to hours/days with half-up
11
+ * rounding. Whenever the effective value differs from the request, the result
12
+ * carries a notice naming both the requested and effective cadence.
13
+ */
14
+ export function normalizeInterval(requested) {
15
+ let value;
16
+ let unit;
17
+ let rounded;
18
+ switch (requested.unit) {
19
+ case "s": {
20
+ value = Math.max(1, Math.ceil(requested.value / 60));
21
+ unit = "m";
22
+ rounded = true;
23
+ break;
24
+ }
25
+ case "m": {
26
+ if (requested.value >= 60) {
27
+ value = Math.round(requested.value / 60);
28
+ unit = "h";
29
+ rounded = true;
30
+ }
31
+ else {
32
+ value = requested.value;
33
+ unit = "m";
34
+ rounded = false;
35
+ }
36
+ break;
37
+ }
38
+ case "h": {
39
+ if (requested.value >= 24) {
40
+ value = Math.round(requested.value / 24);
41
+ unit = "d";
42
+ rounded = true;
43
+ }
44
+ else {
45
+ value = requested.value;
46
+ unit = "h";
47
+ rounded = false;
48
+ }
49
+ break;
50
+ }
51
+ case "d": {
52
+ value = requested.value;
53
+ unit = "d";
54
+ rounded = false;
55
+ break;
56
+ }
57
+ }
58
+ const human = unit === "m"
59
+ ? pluralize(value, "minute", "minutes")
60
+ : unit === "h"
61
+ ? pluralize(value, "hour", "hours")
62
+ : pluralize(value, "day", "days");
63
+ const intervalMs = unit === "m" ? value * MS_PER_MINUTE : unit === "h" ? value * MS_PER_HOUR : value * MS_PER_DAY;
64
+ const roundingNotice = rounded ? `Requested ${requested.raw} rounds to ${human}.` : undefined;
65
+ return { value, unit, human, rounded, roundingNotice, intervalMs };
66
+ }
67
+ /**
68
+ * Returns a restricted 5-field cron expression for DISPLAY ONLY. The scheduler
69
+ * uses intervalMs + absolute nextFireAt; this string is shown to the user.
70
+ */
71
+ export function describeCron(effective) {
72
+ switch (effective.unit) {
73
+ case "m":
74
+ return `*/${effective.value} * * * *`;
75
+ case "h":
76
+ return `0 */${effective.value} * * *`;
77
+ case "d":
78
+ return `0 0 */${effective.value} * *`;
79
+ }
80
+ }
81
+ /**
82
+ * Computes the absolute timestamp of the next scheduled fire. Pure: takes
83
+ * nowMs as a parameter and never reads the clock internally.
84
+ */
85
+ export function computeNextFireAt(nowMs, intervalMs) {
86
+ return nowMs + intervalMs;
87
+ }
88
+ //# sourceMappingURL=cron-planner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-planner.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/cron-planner.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EffectiveInterval, EffectiveIntervalUnit, RequestedInterval } from \"./types.ts\";\n\n// `types.ts` is the single type home for the whole extension; these are re-exported so\n// existing consumers of the planner keep importing the canonical declarations.\nexport type { EffectiveInterval, RequestedInterval };\n\nconst MS_PER_MINUTE = 60_000;\nconst MS_PER_HOUR = 60 * MS_PER_MINUTE;\nconst MS_PER_DAY = 24 * MS_PER_HOUR;\n\nfunction pluralize(count: number, singular: string, plural: string): string {\n\treturn count === 1 ? `${count} ${singular}` : `${count} ${plural}`;\n}\n\n/**\n * Converts a user-requested interval into an effective cadence the scheduler\n * can express with the restricted display cron forms. Seconds are coarsened to\n * minutes; large minute/hour values roll up to hours/days with half-up\n * rounding. Whenever the effective value differs from the request, the result\n * carries a notice naming both the requested and effective cadence.\n */\nexport function normalizeInterval(requested: RequestedInterval): EffectiveInterval & { intervalMs: number } {\n\tlet value: number;\n\tlet unit: EffectiveIntervalUnit;\n\tlet rounded: boolean;\n\n\tswitch (requested.unit) {\n\t\tcase \"s\": {\n\t\t\tvalue = Math.max(1, Math.ceil(requested.value / 60));\n\t\t\tunit = \"m\";\n\t\t\trounded = true;\n\t\t\tbreak;\n\t\t}\n\t\tcase \"m\": {\n\t\t\tif (requested.value >= 60) {\n\t\t\t\tvalue = Math.round(requested.value / 60);\n\t\t\t\tunit = \"h\";\n\t\t\t\trounded = true;\n\t\t\t} else {\n\t\t\t\tvalue = requested.value;\n\t\t\t\tunit = \"m\";\n\t\t\t\trounded = false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase \"h\": {\n\t\t\tif (requested.value >= 24) {\n\t\t\t\tvalue = Math.round(requested.value / 24);\n\t\t\t\tunit = \"d\";\n\t\t\t\trounded = true;\n\t\t\t} else {\n\t\t\t\tvalue = requested.value;\n\t\t\t\tunit = \"h\";\n\t\t\t\trounded = false;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase \"d\": {\n\t\t\tvalue = requested.value;\n\t\t\tunit = \"d\";\n\t\t\trounded = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst human =\n\t\tunit === \"m\"\n\t\t\t? pluralize(value, \"minute\", \"minutes\")\n\t\t\t: unit === \"h\"\n\t\t\t\t? pluralize(value, \"hour\", \"hours\")\n\t\t\t\t: pluralize(value, \"day\", \"days\");\n\n\tconst intervalMs = unit === \"m\" ? value * MS_PER_MINUTE : unit === \"h\" ? value * MS_PER_HOUR : value * MS_PER_DAY;\n\n\tconst roundingNotice = rounded ? `Requested ${requested.raw} rounds to ${human}.` : undefined;\n\n\treturn { value, unit, human, rounded, roundingNotice, intervalMs };\n}\n\n/**\n * Returns a restricted 5-field cron expression for DISPLAY ONLY. The scheduler\n * uses intervalMs + absolute nextFireAt; this string is shown to the user.\n */\nexport function describeCron(effective: Pick<EffectiveInterval, \"value\" | \"unit\">): string {\n\tswitch (effective.unit) {\n\t\tcase \"m\":\n\t\t\treturn `*/${effective.value} * * * *`;\n\t\tcase \"h\":\n\t\t\treturn `0 */${effective.value} * * *`;\n\t\tcase \"d\":\n\t\t\treturn `0 0 */${effective.value} * *`;\n\t}\n}\n\n/**\n * Computes the absolute timestamp of the next scheduled fire. Pure: takes\n * nowMs as a parameter and never reads the clock internally.\n */\nexport function computeNextFireAt(nowMs: number, intervalMs: number): number {\n\treturn nowMs + intervalMs;\n}\n"]}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * `/loop` extension factory and lifecycle wiring.
3
+ *
4
+ * This module owns the only impure parts of the feature: real timers, the session store,
5
+ * the extension event wiring, and message dispatch. Every decision it makes is delegated
6
+ * to the pure modules beside it (`scheduler.ts`, `tick-prompt.ts`, `cron-planner.ts`,
7
+ * `loopfile.ts`, `status.ts`), so the runtime here is a thin, testable adapter.
8
+ *
9
+ * Load-bearing rules implemented here:
10
+ * - A tick NEVER steers. Idle dispatch goes through `sendUserMessage(..., {
11
+ * expandPromptTemplates: true })` so a slash payload reaches the real command path;
12
+ * a busy session receives the tick as a follow-up instead.
13
+ * - Shutdown SUSPENDS: timers are cancelled, the ticker disposed, and the snapshot is
14
+ * persisted without a terminal reason (every senpi shutdown reason is resumable).
15
+ * - Keepalive is applied only to an attributed dynamic iteration, never after an ordinary
16
+ * user turn, and never after a user abort (an abort PAUSES the loop instead).
17
+ * - A store failure is not swallowed: the affected loops end with `error` and the user is
18
+ * told, because a schedule that cannot be persisted must not keep running.
19
+ */
20
+ import type { EntryRenderer, ExtensionContext, ExtensionFactory } from "../../types.ts";
21
+ import { type LoopFileResult } from "./loopfile.ts";
22
+ import { type LoopClock, type LoopIdFactory, type LoopTimerPort } from "./scheduler.ts";
23
+ import { type LoopMode, type TickDelivery } from "./tick-prompt.ts";
24
+ import type { DeliveryId, EpochMs, LoopEndReason, LoopId, LoopSentinel, LoopState, LoopStoreRef, RequestedInterval } from "./types.ts";
25
+ /** Custom entry type carrying one dispatched tick, used for attribution and folding. */
26
+ export declare const LOOP_TICK_ENTRY_TYPE = "loop-tick";
27
+ export interface LoopTickEntryData {
28
+ readonly loopId: LoopId;
29
+ readonly deliveryId: DeliveryId;
30
+ readonly scheduledForAt: EpochMs;
31
+ readonly mode: LoopMode;
32
+ readonly delivery: TickDelivery;
33
+ readonly sentinel?: LoopSentinel;
34
+ /** Consecutive quiet dynamic iterations at the moment this tick was dispatched. */
35
+ readonly noopStreak: number;
36
+ /** True when this tick continues a quiet streak and should render folded. */
37
+ readonly folded: boolean;
38
+ }
39
+ export interface LoopCreateOk {
40
+ readonly ok: true;
41
+ readonly loopId: LoopId;
42
+ /** Present when a new dynamic loop replaced a previous one. */
43
+ readonly supersededLoopId?: LoopId;
44
+ /** Human-readable rounding notice when the requested cadence was normalized. */
45
+ readonly roundingNotice?: string;
46
+ readonly cronExpression?: string;
47
+ readonly effectiveCadence?: string;
48
+ readonly expiresAt?: EpochMs;
49
+ }
50
+ export interface LoopCreateRejected {
51
+ readonly ok: false;
52
+ readonly message: string;
53
+ }
54
+ export type LoopCreateOutcome = LoopCreateOk | LoopCreateRejected;
55
+ export interface LoopStoreFailure {
56
+ readonly endReason: LoopEndReason;
57
+ readonly message: string;
58
+ readonly loopIds: readonly LoopId[];
59
+ }
60
+ export interface StartFixedRequest {
61
+ readonly originalArgs: string;
62
+ readonly prompt: string;
63
+ readonly requestedInterval: RequestedInterval;
64
+ }
65
+ export interface StartDynamicRequest {
66
+ readonly originalArgs: string;
67
+ readonly prompt: string;
68
+ }
69
+ export interface StartBareRequest {
70
+ readonly originalArgs: string;
71
+ readonly interval?: RequestedInterval;
72
+ }
73
+ export interface ControllerScheduleWakeupRequest {
74
+ readonly loopId: LoopId;
75
+ readonly requestedDelaySeconds: number;
76
+ readonly delaySeconds: number;
77
+ readonly reason: string;
78
+ readonly prompt: string;
79
+ readonly noop: boolean;
80
+ }
81
+ /**
82
+ * Runtime surface of a live loop extension. The `/loop` command module drives the
83
+ * extension exclusively through this interface, so command parsing never reaches into
84
+ * scheduler or store internals.
85
+ */
86
+ export interface LoopController {
87
+ startFixed(request: StartFixedRequest): Promise<LoopCreateOutcome>;
88
+ startDynamic(request: StartDynamicRequest): Promise<LoopCreateOutcome>;
89
+ startBare(request: StartBareRequest): Promise<LoopCreateOutcome>;
90
+ /** Runs the due decision for a loop at the current clock reading and dispatches it. */
91
+ fireDue(loopId: LoopId): Promise<void>;
92
+ scheduleWakeup(request: ControllerScheduleWakeupRequest): Promise<void>;
93
+ stop(target: LoopId | "all", detail: string): Promise<readonly LoopId[]>;
94
+ pause(target: LoopId | "all"): Promise<readonly LoopId[]>;
95
+ resume(target: LoopId | "all"): Promise<readonly LoopId[]>;
96
+ getState(): LoopState;
97
+ statusLine(): string | undefined;
98
+ /** Set when a store read/write failed and the affected loops were ended with `error`. */
99
+ lastStoreFailure(): LoopStoreFailure | undefined;
100
+ /** True when this loop was retired because its state could not be read or written. */
101
+ isEndedWithError(loopId: LoopId): boolean;
102
+ }
103
+ export interface LoopExtensionDeps {
104
+ readonly clock?: LoopClock;
105
+ /** Factory so each session gets its own timer port; defaults to real timeouts. */
106
+ readonly timers?: () => LoopTimerPort;
107
+ readonly ids?: LoopIdFactory;
108
+ readonly storeRef?: (ctx: ExtensionContext) => LoopStoreRef;
109
+ readonly resolveLoopFile?: (options: {
110
+ cwd: string;
111
+ homeDir: string;
112
+ }) => Promise<LoopFileResult>;
113
+ readonly readState?: (ref: LoopStoreRef) => Promise<LoopState | null>;
114
+ readonly writeState?: (ref: LoopStoreRef, state: LoopState) => Promise<void>;
115
+ /**
116
+ * Published at factory time so a command module (or a test) can drive this instance.
117
+ * The controller is bound to the extension's own session state, so it stays valid across
118
+ * session starts within one extension generation.
119
+ */
120
+ readonly onControllerReady?: (controller: LoopController) => void;
121
+ }
122
+ export declare const renderLoopTickEntry: EntryRenderer<LoopTickEntryData>;
123
+ /**
124
+ * Real timer port: exactly one live timeout per key, replaced on re-arm.
125
+ *
126
+ * Follows the generation-counter discipline of `cache-keepalive/index.ts`: cancelling or
127
+ * re-arming a key bumps its generation, so a callback whose generation is stale returns
128
+ * without firing even if it was already scheduled on the event loop.
129
+ */
130
+ export declare function createNodeTimerPort(): LoopTimerPort;
131
+ export declare function createLoopExtension(deps?: LoopExtensionDeps): ExtensionFactory;
132
+ declare const _default: ExtensionFactory;
133
+ export default _default;
134
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/index.ts"],"names":[],"mappings":"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"}