@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,424 @@
1
+ /**
2
+ * Atomic, versioned, per-session sidecar store for `/loop` state.
3
+ *
4
+ * Mirrors the goal store's persistence discipline (temp file + rename, per-file mutation
5
+ * serialization, strict versioned parsing) because scheduler state must never be half
6
+ * written or silently reset: a corrupt file arms nothing and surfaces a typed error so
7
+ * the extension can end affected loops with `error` instead of guessing.
8
+ *
9
+ * Session custom entries are deliberately NOT the authoritative store: they are branch
10
+ * nodes, so a globally scanned "latest" entry can come from an abandoned branch, while
11
+ * loop schedules are session-scoped.
12
+ */
13
+ import { randomUUID } from "node:crypto";
14
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
15
+ import { dirname, join } from "node:path";
16
+ import { EFFECTIVE_INTERVAL_UNITS, isRecord, LOOP_END_REASONS, LOOP_PHASES, LOOP_SENTINELS, LOOP_STATE_VERSION, LOOP_WAKE_SOURCE_KINDS, REQUESTED_INTERVAL_UNITS, } from "./types.js";
17
+ export class InvalidLoopStoreError extends Error {
18
+ constructor(message, options) {
19
+ super(message, options);
20
+ this.name = "InvalidLoopStoreError";
21
+ }
22
+ }
23
+ export class UnsupportedLoopStoreVersionError extends Error {
24
+ constructor(message) {
25
+ super(message);
26
+ this.name = "UnsupportedLoopStoreVersionError";
27
+ }
28
+ }
29
+ const mutationTails = new Map();
30
+ const snapshots = new Map();
31
+ export function encodedSessionId(ref) {
32
+ return encodeURIComponent(ref.sessionId);
33
+ }
34
+ export function loopStateFilePath(ref) {
35
+ return join(ref.baseDir, `${encodedSessionId(ref)}.json`);
36
+ }
37
+ export function emptyLoopState(sessionId) {
38
+ return { version: LOOP_STATE_VERSION, sessionId, entries: {}, activeDynamicId: null, updatedAt: 0 };
39
+ }
40
+ /**
41
+ * Reads persisted state, or null when this session has never persisted any loop.
42
+ * Throws {@link InvalidLoopStoreError} / {@link UnsupportedLoopStoreVersionError} on
43
+ * unusable state so callers fail closed rather than resetting a user's schedule.
44
+ */
45
+ export async function readLoopState(ref) {
46
+ let raw;
47
+ try {
48
+ raw = await readFile(loopStateFilePath(ref), "utf8");
49
+ }
50
+ catch (error) {
51
+ if (isFileSystemError(error, "ENOENT"))
52
+ return null;
53
+ throw error;
54
+ }
55
+ const state = parseLoopState(raw, ref);
56
+ snapshots.set(loopStateFilePath(ref), state);
57
+ return state;
58
+ }
59
+ /** Like {@link readLoopState}, but returns an empty state when nothing is persisted. */
60
+ export async function loadLoopState(ref) {
61
+ return (await readLoopState(ref)) ?? emptyLoopState(ref.sessionId);
62
+ }
63
+ /**
64
+ * Last successfully loaded or written state for this ref, for synchronous consumers
65
+ * (status line, tick attribution). Undefined until the store has been touched.
66
+ */
67
+ export function snapshotLoopState(ref) {
68
+ return snapshots.get(loopStateFilePath(ref));
69
+ }
70
+ /**
71
+ * Serializes read-modify-write cycles through a per-file promise tail so command,
72
+ * timer, tool, and lifecycle writes cannot interleave and lose an update.
73
+ */
74
+ export function mutateLoopState(ref, mutation) {
75
+ return enqueue(ref, async () => {
76
+ const current = await loadLoopState(ref);
77
+ const next = await mutation(current);
78
+ await writeLoopState(ref, next);
79
+ return next;
80
+ });
81
+ }
82
+ /** Persists a complete state atomically. Prefer {@link mutateLoopState} for updates. */
83
+ export async function writeLoopState(ref, state) {
84
+ const filePath = loopStateFilePath(ref);
85
+ await mkdir(dirname(filePath), { recursive: true });
86
+ await writeAtomic(filePath, `${JSON.stringify(state, null, 2)}\n`);
87
+ snapshots.set(filePath, state);
88
+ }
89
+ /** Drops cached snapshots; used when a session ends or tests reset global state. */
90
+ export function clearLoopStateSnapshot(ref) {
91
+ snapshots.delete(loopStateFilePath(ref));
92
+ }
93
+ function enqueue(ref, operation) {
94
+ const key = loopStateFilePath(ref);
95
+ const previous = mutationTails.get(key) ?? Promise.resolve();
96
+ const run = previous.then(operation);
97
+ const tail = run.then(() => undefined, () => undefined);
98
+ mutationTails.set(key, tail);
99
+ void tail.then(() => {
100
+ if (mutationTails.get(key) === tail)
101
+ mutationTails.delete(key);
102
+ });
103
+ return run;
104
+ }
105
+ async function writeAtomic(filePath, contents) {
106
+ const tempPath = join(dirname(filePath), `.loop-${randomUUID()}.tmp`);
107
+ try {
108
+ await writeFile(tempPath, contents, { encoding: "utf8", mode: 0o600 });
109
+ await rename(tempPath, filePath);
110
+ }
111
+ catch (error) {
112
+ try {
113
+ await rm(tempPath, { force: true });
114
+ }
115
+ catch (cleanupError) {
116
+ throw new AggregateError([error, cleanupError], "loop store write failed and its temporary file could not be removed");
117
+ }
118
+ throw error;
119
+ }
120
+ }
121
+ function parseLoopState(raw, ref) {
122
+ let parsed;
123
+ try {
124
+ parsed = JSON.parse(raw);
125
+ }
126
+ catch (error) {
127
+ throw new InvalidLoopStoreError("loop store contains unparseable JSON", { cause: error });
128
+ }
129
+ if (!isRecord(parsed))
130
+ throw new InvalidLoopStoreError("loop store must be a JSON object");
131
+ if (parsed.version !== LOOP_STATE_VERSION) {
132
+ throw new UnsupportedLoopStoreVersionError(`unsupported loop store version: ${JSON.stringify(parsed.version)} (expected ${LOOP_STATE_VERSION})`);
133
+ }
134
+ if (typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0) {
135
+ throw new InvalidLoopStoreError("loop store is missing a sessionId");
136
+ }
137
+ if (parsed.sessionId !== ref.sessionId) {
138
+ throw new InvalidLoopStoreError(`loop store sessionId ${parsed.sessionId} does not match the session it was loaded for`);
139
+ }
140
+ if (!isEpochMs(parsed.updatedAt))
141
+ throw new InvalidLoopStoreError("loop store has an invalid updatedAt");
142
+ if (!isRecord(parsed.entries))
143
+ throw new InvalidLoopStoreError("loop store entries must be an object");
144
+ const entries = {};
145
+ for (const [id, value] of Object.entries(parsed.entries)) {
146
+ const entry = parseEntry(id, value);
147
+ entries[id] = entry;
148
+ }
149
+ const activeDynamicId = parsed.activeDynamicId;
150
+ if (activeDynamicId !== null) {
151
+ if (typeof activeDynamicId !== "string") {
152
+ throw new InvalidLoopStoreError("loop store activeDynamicId must be a string or null");
153
+ }
154
+ const active = entries[activeDynamicId];
155
+ if (active === undefined || active.kind !== "dynamic") {
156
+ throw new InvalidLoopStoreError(`loop store activeDynamicId ${activeDynamicId} does not name a dynamic loop`);
157
+ }
158
+ }
159
+ return {
160
+ version: LOOP_STATE_VERSION,
161
+ sessionId: parsed.sessionId,
162
+ entries,
163
+ activeDynamicId,
164
+ updatedAt: parsed.updatedAt,
165
+ };
166
+ }
167
+ function parseEntry(id, value) {
168
+ if (!isRecord(value))
169
+ throw new InvalidLoopStoreError(`loop entry ${id} is not an object`);
170
+ if (value.id !== id)
171
+ throw new InvalidLoopStoreError(`loop entry ${id} carries a mismatched id`);
172
+ const kind = value.kind;
173
+ if (kind !== "fixed" && kind !== "dynamic")
174
+ throw new InvalidLoopStoreError(`loop entry ${id} has an unknown kind`);
175
+ const base = parseEntryFields(id, value);
176
+ if (kind === "fixed") {
177
+ const fixed = {
178
+ ...base,
179
+ kind: "fixed",
180
+ requestedInterval: parseRequestedInterval(id, value.requestedInterval),
181
+ effectiveInterval: parseEffectiveInterval(id, value.effectiveInterval),
182
+ cronExpression: requireString(id, "cronExpression", value.cronExpression),
183
+ nextFireAt: requireEpochMs(id, "nextFireAt", value.nextFireAt),
184
+ intervalMs: requirePositiveInteger(id, "intervalMs", value.intervalMs),
185
+ };
186
+ return fixed;
187
+ }
188
+ const keepaliveCredit = value.keepaliveCredit;
189
+ if (keepaliveCredit !== 0 && keepaliveCredit !== 1) {
190
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid keepaliveCredit`);
191
+ }
192
+ const dynamic = {
193
+ ...base,
194
+ kind: "dynamic",
195
+ pendingWakeup: parsePendingWakeup(id, value.pendingWakeup),
196
+ keepaliveCredit,
197
+ };
198
+ return dynamic;
199
+ }
200
+ function parseEntryFields(id, value) {
201
+ const fields = {
202
+ id,
203
+ originalArgs: requireString(id, "originalArgs", value.originalArgs, { allowEmpty: true }),
204
+ reentryPrompt: requireString(id, "reentryPrompt", value.reentryPrompt),
205
+ payload: parsePayload(id, value.payload),
206
+ createdAt: requireEpochMs(id, "createdAt", value.createdAt),
207
+ lastFiredAt: parseNullableEpochMs(id, "lastFiredAt", value.lastFiredAt),
208
+ expiresAt: requireEpochMs(id, "expiresAt", value.expiresAt),
209
+ lastScheduledForAt: parseNullableEpochMs(id, "lastScheduledForAt", value.lastScheduledForAt),
210
+ coalescedFirePending: requireBoolean(id, "coalescedFirePending", value.coalescedFirePending),
211
+ queuedForAt: parseNullableEpochMs(id, "queuedForAt", value.queuedForAt),
212
+ noopStreak: requireNonNegativeInteger(id, "noopStreak", value.noopStreak),
213
+ tickCount: requireNonNegativeInteger(id, "tickCount", value.tickCount),
214
+ sentinelDelivery: parseSentinelDelivery(id, value.sentinelDelivery),
215
+ wakeSources: parseWakeSources(id, value.wakeSources),
216
+ };
217
+ return { ...fields, ...parseLifecycle(id, value) };
218
+ }
219
+ function parseLifecycle(id, value) {
220
+ const phase = value.phase;
221
+ if (typeof phase !== "string" || !isLoopPhase(phase)) {
222
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid phase`);
223
+ }
224
+ if (phase !== "ended") {
225
+ if (value.endedAt !== undefined || value.endReason !== undefined || value.endDetail !== undefined) {
226
+ throw new InvalidLoopStoreError(`loop entry ${id} carries terminal fields while not ended`);
227
+ }
228
+ return { phase };
229
+ }
230
+ const endReason = value.endReason;
231
+ if (typeof endReason !== "string" || !isLoopEndReason(endReason)) {
232
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid endReason`);
233
+ }
234
+ const endDetail = value.endDetail;
235
+ if (endDetail !== undefined && typeof endDetail !== "string") {
236
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid endDetail`);
237
+ }
238
+ return {
239
+ phase: "ended",
240
+ endedAt: requireEpochMs(id, "endedAt", value.endedAt),
241
+ endReason,
242
+ ...(endDetail === undefined ? {} : { endDetail }),
243
+ };
244
+ }
245
+ function parsePayload(id, value) {
246
+ if (!isRecord(value))
247
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid payload`);
248
+ if (value.type === "prompt") {
249
+ return { type: "prompt", prompt: requireString(id, "payload.prompt", value.prompt) };
250
+ }
251
+ if (value.type === "sentinel") {
252
+ const sentinel = value.sentinel;
253
+ if (typeof sentinel !== "string" || !isLoopSentinel(sentinel)) {
254
+ throw new InvalidLoopStoreError(`loop entry ${id} has an unknown payload sentinel`);
255
+ }
256
+ return { type: "sentinel", sentinel };
257
+ }
258
+ throw new InvalidLoopStoreError(`loop entry ${id} has an unknown payload type`);
259
+ }
260
+ function parseSentinelDelivery(id, value) {
261
+ if (!isRecord(value))
262
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid sentinelDelivery`);
263
+ return {
264
+ autonomousPreambleDelivered: requireBoolean(id, "sentinelDelivery.autonomousPreambleDelivered", value.autonomousPreambleDelivered),
265
+ lastLoopFileDelivered: parseFingerprint(id, value.lastLoopFileDelivered),
266
+ forceFullDelivery: requireBoolean(id, "sentinelDelivery.forceFullDelivery", value.forceFullDelivery),
267
+ };
268
+ }
269
+ function parseFingerprint(id, value) {
270
+ if (value === null)
271
+ return null;
272
+ if (!isRecord(value))
273
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid loop-file fingerprint`);
274
+ return {
275
+ path: requireString(id, "fingerprint.path", value.path),
276
+ mtimeMs: requireFiniteNumber(id, "fingerprint.mtimeMs", value.mtimeMs),
277
+ size: requireNonNegativeInteger(id, "fingerprint.size", value.size),
278
+ contentHash: requireString(id, "fingerprint.contentHash", value.contentHash),
279
+ anchorDeliveryId: requireString(id, "fingerprint.anchorDeliveryId", value.anchorDeliveryId),
280
+ };
281
+ }
282
+ function parseWakeSources(id, value) {
283
+ if (!Array.isArray(value))
284
+ throw new InvalidLoopStoreError(`loop entry ${id} has invalid wakeSources`);
285
+ return value.map((candidate) => {
286
+ if (!isRecord(candidate))
287
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid wake source`);
288
+ const source = candidate.source;
289
+ if (typeof source !== "string" || !isWakeSourceKind(source)) {
290
+ throw new InvalidLoopStoreError(`loop entry ${id} has an unknown wake source kind`);
291
+ }
292
+ const description = candidate.description;
293
+ if (description !== undefined && typeof description !== "string") {
294
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid wake source description`);
295
+ }
296
+ return {
297
+ source,
298
+ id: requireString(id, "wakeSource.id", candidate.id),
299
+ createdAt: requireEpochMs(id, "wakeSource.createdAt", candidate.createdAt),
300
+ ...(description === undefined ? {} : { description }),
301
+ };
302
+ });
303
+ }
304
+ function parsePendingWakeup(id, value) {
305
+ if (value === null || value === undefined)
306
+ return null;
307
+ if (!isRecord(value))
308
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid pendingWakeup`);
309
+ const source = value.source;
310
+ if (source !== "model" && source !== "keepalive") {
311
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid pendingWakeup source`);
312
+ }
313
+ if (value.kind !== "dynamic")
314
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid pendingWakeup kind`);
315
+ return {
316
+ id: requireString(id, "pendingWakeup.id", value.id),
317
+ loopId: requireString(id, "pendingWakeup.loopId", value.loopId),
318
+ kind: "dynamic",
319
+ source,
320
+ requestedDelaySeconds: requireFiniteNumber(id, "pendingWakeup.requestedDelaySeconds", value.requestedDelaySeconds),
321
+ delaySeconds: requirePositiveInteger(id, "pendingWakeup.delaySeconds", value.delaySeconds),
322
+ dueAt: requireEpochMs(id, "pendingWakeup.dueAt", value.dueAt),
323
+ reason: requireString(id, "pendingWakeup.reason", value.reason),
324
+ prompt: requireString(id, "pendingWakeup.prompt", value.prompt),
325
+ noop: requireBoolean(id, "pendingWakeup.noop", value.noop),
326
+ createdAt: requireEpochMs(id, "pendingWakeup.createdAt", value.createdAt),
327
+ };
328
+ }
329
+ function parseRequestedInterval(id, value) {
330
+ if (!isRecord(value))
331
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid requestedInterval`);
332
+ const unit = value.unit;
333
+ if (typeof unit !== "string" || !isRequestedIntervalUnit(unit)) {
334
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid requestedInterval unit`);
335
+ }
336
+ return {
337
+ value: requirePositiveInteger(id, "requestedInterval.value", value.value),
338
+ unit,
339
+ raw: requireString(id, "requestedInterval.raw", value.raw),
340
+ };
341
+ }
342
+ function parseEffectiveInterval(id, value) {
343
+ if (!isRecord(value))
344
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid effectiveInterval`);
345
+ const unit = value.unit;
346
+ if (typeof unit !== "string" || !isEffectiveIntervalUnit(unit)) {
347
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid effectiveInterval unit`);
348
+ }
349
+ const roundingNotice = value.roundingNotice;
350
+ if (roundingNotice !== undefined && typeof roundingNotice !== "string") {
351
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid effectiveInterval roundingNotice`);
352
+ }
353
+ return {
354
+ value: requirePositiveInteger(id, "effectiveInterval.value", value.value),
355
+ unit,
356
+ human: requireString(id, "effectiveInterval.human", value.human),
357
+ rounded: requireBoolean(id, "effectiveInterval.rounded", value.rounded),
358
+ ...(roundingNotice === undefined ? {} : { roundingNotice }),
359
+ };
360
+ }
361
+ function requireString(id, field, value, options) {
362
+ if (typeof value !== "string" || (options?.allowEmpty !== true && value.length === 0)) {
363
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);
364
+ }
365
+ return value;
366
+ }
367
+ function requireBoolean(id, field, value) {
368
+ if (typeof value !== "boolean")
369
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);
370
+ return value;
371
+ }
372
+ function requireFiniteNumber(id, field, value) {
373
+ if (typeof value !== "number" || !Number.isFinite(value)) {
374
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);
375
+ }
376
+ return value;
377
+ }
378
+ function requireNonNegativeInteger(id, field, value) {
379
+ if (!Number.isSafeInteger(value) || value < 0) {
380
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);
381
+ }
382
+ return value;
383
+ }
384
+ function requirePositiveInteger(id, field, value) {
385
+ if (!Number.isSafeInteger(value) || value <= 0) {
386
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);
387
+ }
388
+ return value;
389
+ }
390
+ function requireEpochMs(id, field, value) {
391
+ if (!isEpochMs(value))
392
+ throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);
393
+ return value;
394
+ }
395
+ function parseNullableEpochMs(id, field, value) {
396
+ if (value === null || value === undefined)
397
+ return null;
398
+ return requireEpochMs(id, field, value);
399
+ }
400
+ function isEpochMs(value) {
401
+ return Number.isSafeInteger(value) && value >= 0;
402
+ }
403
+ function isLoopPhase(value) {
404
+ return LOOP_PHASES.includes(value);
405
+ }
406
+ function isLoopEndReason(value) {
407
+ return LOOP_END_REASONS.includes(value);
408
+ }
409
+ function isLoopSentinel(value) {
410
+ return LOOP_SENTINELS.includes(value);
411
+ }
412
+ function isWakeSourceKind(value) {
413
+ return LOOP_WAKE_SOURCE_KINDS.includes(value);
414
+ }
415
+ function isRequestedIntervalUnit(value) {
416
+ return REQUESTED_INTERVAL_UNITS.includes(value);
417
+ }
418
+ function isEffectiveIntervalUnit(value) {
419
+ return EFFECTIVE_INTERVAL_UNITS.includes(value);
420
+ }
421
+ function isFileSystemError(error, code) {
422
+ return error instanceof Error && "code" in error && error.code === code;
423
+ }
424
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Atomic, versioned, per-session sidecar store for `/loop` state.\n *\n * Mirrors the goal store's persistence discipline (temp file + rename, per-file mutation\n * serialization, strict versioned parsing) because scheduler state must never be half\n * written or silently reset: a corrupt file arms nothing and surfaces a typed error so\n * the extension can end affected loops with `error` instead of guessing.\n *\n * Session custom entries are deliberately NOT the authoritative store: they are branch\n * nodes, so a globally scanned \"latest\" entry can come from an abandoned branch, while\n * loop schedules are session-scoped.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n\ttype CronEntry,\n\ttype DynamicCronEntry,\n\tEFFECTIVE_INTERVAL_UNITS,\n\ttype EffectiveInterval,\n\ttype EffectiveIntervalUnit,\n\ttype EpochMs,\n\ttype FixedCronEntry,\n\tisRecord,\n\tLOOP_END_REASONS,\n\tLOOP_PHASES,\n\tLOOP_SENTINELS,\n\tLOOP_STATE_VERSION,\n\tLOOP_WAKE_SOURCE_KINDS,\n\ttype LoopEndReason,\n\ttype LoopEntryFields,\n\ttype LoopFileFingerprint,\n\ttype LoopLifecycle,\n\ttype LoopPayload,\n\ttype LoopPhase,\n\ttype LoopSentinel,\n\ttype LoopState,\n\ttype LoopStoreRef,\n\ttype LoopWakeSource,\n\ttype LoopWakeSourceKind,\n\ttype PendingWakeup,\n\tREQUESTED_INTERVAL_UNITS,\n\ttype RequestedInterval,\n\ttype RequestedIntervalUnit,\n\ttype SentinelDeliveryState,\n} from \"./types.ts\";\n\nexport class InvalidLoopStoreError extends Error {\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t\tthis.name = \"InvalidLoopStoreError\";\n\t}\n}\n\nexport class UnsupportedLoopStoreVersionError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"UnsupportedLoopStoreVersionError\";\n\t}\n}\n\n/** Mutation callback. May be async; its result becomes the next persisted state. */\nexport type LoopStateMutation = (current: LoopState) => LoopState | Promise<LoopState>;\n\nconst mutationTails = new Map<string, Promise<void>>();\nconst snapshots = new Map<string, LoopState>();\n\nexport function encodedSessionId(ref: LoopStoreRef): string {\n\treturn encodeURIComponent(ref.sessionId);\n}\n\nexport function loopStateFilePath(ref: LoopStoreRef): string {\n\treturn join(ref.baseDir, `${encodedSessionId(ref)}.json`);\n}\n\nexport function emptyLoopState(sessionId: string): LoopState {\n\treturn { version: LOOP_STATE_VERSION, sessionId, entries: {}, activeDynamicId: null, updatedAt: 0 };\n}\n\n/**\n * Reads persisted state, or null when this session has never persisted any loop.\n * Throws {@link InvalidLoopStoreError} / {@link UnsupportedLoopStoreVersionError} on\n * unusable state so callers fail closed rather than resetting a user's schedule.\n */\nexport async function readLoopState(ref: LoopStoreRef): Promise<LoopState | null> {\n\tlet raw: string;\n\ttry {\n\t\traw = await readFile(loopStateFilePath(ref), \"utf8\");\n\t} catch (error) {\n\t\tif (isFileSystemError(error, \"ENOENT\")) return null;\n\t\tthrow error;\n\t}\n\tconst state = parseLoopState(raw, ref);\n\tsnapshots.set(loopStateFilePath(ref), state);\n\treturn state;\n}\n\n/** Like {@link readLoopState}, but returns an empty state when nothing is persisted. */\nexport async function loadLoopState(ref: LoopStoreRef): Promise<LoopState> {\n\treturn (await readLoopState(ref)) ?? emptyLoopState(ref.sessionId);\n}\n\n/**\n * Last successfully loaded or written state for this ref, for synchronous consumers\n * (status line, tick attribution). Undefined until the store has been touched.\n */\nexport function snapshotLoopState(ref: LoopStoreRef): LoopState | undefined {\n\treturn snapshots.get(loopStateFilePath(ref));\n}\n\n/**\n * Serializes read-modify-write cycles through a per-file promise tail so command,\n * timer, tool, and lifecycle writes cannot interleave and lose an update.\n */\nexport function mutateLoopState(ref: LoopStoreRef, mutation: LoopStateMutation): Promise<LoopState> {\n\treturn enqueue(ref, async () => {\n\t\tconst current = await loadLoopState(ref);\n\t\tconst next = await mutation(current);\n\t\tawait writeLoopState(ref, next);\n\t\treturn next;\n\t});\n}\n\n/** Persists a complete state atomically. Prefer {@link mutateLoopState} for updates. */\nexport async function writeLoopState(ref: LoopStoreRef, state: LoopState): Promise<void> {\n\tconst filePath = loopStateFilePath(ref);\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tawait writeAtomic(filePath, `${JSON.stringify(state, null, 2)}\\n`);\n\tsnapshots.set(filePath, state);\n}\n\n/** Drops cached snapshots; used when a session ends or tests reset global state. */\nexport function clearLoopStateSnapshot(ref: LoopStoreRef): void {\n\tsnapshots.delete(loopStateFilePath(ref));\n}\n\nfunction enqueue<T>(ref: LoopStoreRef, operation: () => Promise<T>): Promise<T> {\n\tconst key = loopStateFilePath(ref);\n\tconst previous = mutationTails.get(key) ?? Promise.resolve();\n\tconst run = previous.then(operation);\n\tconst tail = run.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\tmutationTails.set(key, tail);\n\tvoid tail.then(() => {\n\t\tif (mutationTails.get(key) === tail) mutationTails.delete(key);\n\t});\n\treturn run;\n}\n\nasync function writeAtomic(filePath: string, contents: string): Promise<void> {\n\tconst tempPath = join(dirname(filePath), `.loop-${randomUUID()}.tmp`);\n\ttry {\n\t\tawait writeFile(tempPath, contents, { encoding: \"utf8\", mode: 0o600 });\n\t\tawait rename(tempPath, filePath);\n\t} catch (error) {\n\t\ttry {\n\t\t\tawait rm(tempPath, { force: true });\n\t\t} catch (cleanupError) {\n\t\t\tthrow new AggregateError(\n\t\t\t\t[error, cleanupError],\n\t\t\t\t\"loop store write failed and its temporary file could not be removed\",\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nfunction parseLoopState(raw: string, ref: LoopStoreRef): LoopState {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (error) {\n\t\tthrow new InvalidLoopStoreError(\"loop store contains unparseable JSON\", { cause: error });\n\t}\n\tif (!isRecord(parsed)) throw new InvalidLoopStoreError(\"loop store must be a JSON object\");\n\tif (parsed.version !== LOOP_STATE_VERSION) {\n\t\tthrow new UnsupportedLoopStoreVersionError(\n\t\t\t`unsupported loop store version: ${JSON.stringify(parsed.version)} (expected ${LOOP_STATE_VERSION})`,\n\t\t);\n\t}\n\tif (typeof parsed.sessionId !== \"string\" || parsed.sessionId.length === 0) {\n\t\tthrow new InvalidLoopStoreError(\"loop store is missing a sessionId\");\n\t}\n\tif (parsed.sessionId !== ref.sessionId) {\n\t\tthrow new InvalidLoopStoreError(\n\t\t\t`loop store sessionId ${parsed.sessionId} does not match the session it was loaded for`,\n\t\t);\n\t}\n\tif (!isEpochMs(parsed.updatedAt)) throw new InvalidLoopStoreError(\"loop store has an invalid updatedAt\");\n\tif (!isRecord(parsed.entries)) throw new InvalidLoopStoreError(\"loop store entries must be an object\");\n\n\tconst entries: Record<string, CronEntry> = {};\n\tfor (const [id, value] of Object.entries(parsed.entries)) {\n\t\tconst entry = parseEntry(id, value);\n\t\tentries[id] = entry;\n\t}\n\n\tconst activeDynamicId = parsed.activeDynamicId;\n\tif (activeDynamicId !== null) {\n\t\tif (typeof activeDynamicId !== \"string\") {\n\t\t\tthrow new InvalidLoopStoreError(\"loop store activeDynamicId must be a string or null\");\n\t\t}\n\t\tconst active = entries[activeDynamicId];\n\t\tif (active === undefined || active.kind !== \"dynamic\") {\n\t\t\tthrow new InvalidLoopStoreError(`loop store activeDynamicId ${activeDynamicId} does not name a dynamic loop`);\n\t\t}\n\t}\n\n\treturn {\n\t\tversion: LOOP_STATE_VERSION,\n\t\tsessionId: parsed.sessionId,\n\t\tentries,\n\t\tactiveDynamicId,\n\t\tupdatedAt: parsed.updatedAt,\n\t};\n}\n\nfunction parseEntry(id: string, value: unknown): CronEntry {\n\tif (!isRecord(value)) throw new InvalidLoopStoreError(`loop entry ${id} is not an object`);\n\tif (value.id !== id) throw new InvalidLoopStoreError(`loop entry ${id} carries a mismatched id`);\n\tconst kind = value.kind;\n\tif (kind !== \"fixed\" && kind !== \"dynamic\") throw new InvalidLoopStoreError(`loop entry ${id} has an unknown kind`);\n\n\tconst base = parseEntryFields(id, value);\n\tif (kind === \"fixed\") {\n\t\tconst fixed: FixedCronEntry = {\n\t\t\t...base,\n\t\t\tkind: \"fixed\",\n\t\t\trequestedInterval: parseRequestedInterval(id, value.requestedInterval),\n\t\t\teffectiveInterval: parseEffectiveInterval(id, value.effectiveInterval),\n\t\t\tcronExpression: requireString(id, \"cronExpression\", value.cronExpression),\n\t\t\tnextFireAt: requireEpochMs(id, \"nextFireAt\", value.nextFireAt),\n\t\t\tintervalMs: requirePositiveInteger(id, \"intervalMs\", value.intervalMs),\n\t\t};\n\t\treturn fixed;\n\t}\n\n\tconst keepaliveCredit = value.keepaliveCredit;\n\tif (keepaliveCredit !== 0 && keepaliveCredit !== 1) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid keepaliveCredit`);\n\t}\n\tconst dynamic: DynamicCronEntry = {\n\t\t...base,\n\t\tkind: \"dynamic\",\n\t\tpendingWakeup: parsePendingWakeup(id, value.pendingWakeup),\n\t\tkeepaliveCredit,\n\t};\n\treturn dynamic;\n}\n\nfunction parseEntryFields(id: string, value: Record<string, unknown>): LoopEntryFields & LoopLifecycle {\n\tconst fields = {\n\t\tid,\n\t\toriginalArgs: requireString(id, \"originalArgs\", value.originalArgs, { allowEmpty: true }),\n\t\treentryPrompt: requireString(id, \"reentryPrompt\", value.reentryPrompt),\n\t\tpayload: parsePayload(id, value.payload),\n\t\tcreatedAt: requireEpochMs(id, \"createdAt\", value.createdAt),\n\t\tlastFiredAt: parseNullableEpochMs(id, \"lastFiredAt\", value.lastFiredAt),\n\t\texpiresAt: requireEpochMs(id, \"expiresAt\", value.expiresAt),\n\t\tlastScheduledForAt: parseNullableEpochMs(id, \"lastScheduledForAt\", value.lastScheduledForAt),\n\t\tcoalescedFirePending: requireBoolean(id, \"coalescedFirePending\", value.coalescedFirePending),\n\t\tqueuedForAt: parseNullableEpochMs(id, \"queuedForAt\", value.queuedForAt),\n\t\tnoopStreak: requireNonNegativeInteger(id, \"noopStreak\", value.noopStreak),\n\t\ttickCount: requireNonNegativeInteger(id, \"tickCount\", value.tickCount),\n\t\tsentinelDelivery: parseSentinelDelivery(id, value.sentinelDelivery),\n\t\twakeSources: parseWakeSources(id, value.wakeSources),\n\t};\n\treturn { ...fields, ...parseLifecycle(id, value) };\n}\n\ntype ParsedLifecycle =\n\t| { phase: Exclude<LoopPhase, \"ended\"> }\n\t| { phase: \"ended\"; endedAt: EpochMs; endReason: LoopEndReason; endDetail?: string };\n\nfunction parseLifecycle(id: string, value: Record<string, unknown>): ParsedLifecycle {\n\tconst phase = value.phase;\n\tif (typeof phase !== \"string\" || !isLoopPhase(phase)) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid phase`);\n\t}\n\tif (phase !== \"ended\") {\n\t\tif (value.endedAt !== undefined || value.endReason !== undefined || value.endDetail !== undefined) {\n\t\t\tthrow new InvalidLoopStoreError(`loop entry ${id} carries terminal fields while not ended`);\n\t\t}\n\t\treturn { phase };\n\t}\n\tconst endReason = value.endReason;\n\tif (typeof endReason !== \"string\" || !isLoopEndReason(endReason)) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid endReason`);\n\t}\n\tconst endDetail = value.endDetail;\n\tif (endDetail !== undefined && typeof endDetail !== \"string\") {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid endDetail`);\n\t}\n\treturn {\n\t\tphase: \"ended\",\n\t\tendedAt: requireEpochMs(id, \"endedAt\", value.endedAt),\n\t\tendReason,\n\t\t...(endDetail === undefined ? {} : { endDetail }),\n\t};\n}\n\nfunction parsePayload(id: string, value: unknown): LoopPayload {\n\tif (!isRecord(value)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid payload`);\n\tif (value.type === \"prompt\") {\n\t\treturn { type: \"prompt\", prompt: requireString(id, \"payload.prompt\", value.prompt) };\n\t}\n\tif (value.type === \"sentinel\") {\n\t\tconst sentinel = value.sentinel;\n\t\tif (typeof sentinel !== \"string\" || !isLoopSentinel(sentinel)) {\n\t\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an unknown payload sentinel`);\n\t\t}\n\t\treturn { type: \"sentinel\", sentinel };\n\t}\n\tthrow new InvalidLoopStoreError(`loop entry ${id} has an unknown payload type`);\n}\n\nfunction parseSentinelDelivery(id: string, value: unknown): SentinelDeliveryState {\n\tif (!isRecord(value)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid sentinelDelivery`);\n\treturn {\n\t\tautonomousPreambleDelivered: requireBoolean(\n\t\t\tid,\n\t\t\t\"sentinelDelivery.autonomousPreambleDelivered\",\n\t\t\tvalue.autonomousPreambleDelivered,\n\t\t),\n\t\tlastLoopFileDelivered: parseFingerprint(id, value.lastLoopFileDelivered),\n\t\tforceFullDelivery: requireBoolean(id, \"sentinelDelivery.forceFullDelivery\", value.forceFullDelivery),\n\t};\n}\n\nfunction parseFingerprint(id: string, value: unknown): LoopFileFingerprint | null {\n\tif (value === null) return null;\n\tif (!isRecord(value)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid loop-file fingerprint`);\n\treturn {\n\t\tpath: requireString(id, \"fingerprint.path\", value.path),\n\t\tmtimeMs: requireFiniteNumber(id, \"fingerprint.mtimeMs\", value.mtimeMs),\n\t\tsize: requireNonNegativeInteger(id, \"fingerprint.size\", value.size),\n\t\tcontentHash: requireString(id, \"fingerprint.contentHash\", value.contentHash),\n\t\tanchorDeliveryId: requireString(id, \"fingerprint.anchorDeliveryId\", value.anchorDeliveryId),\n\t};\n}\n\nfunction parseWakeSources(id: string, value: unknown): readonly LoopWakeSource[] {\n\tif (!Array.isArray(value)) throw new InvalidLoopStoreError(`loop entry ${id} has invalid wakeSources`);\n\treturn value.map((candidate): LoopWakeSource => {\n\t\tif (!isRecord(candidate)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid wake source`);\n\t\tconst source = candidate.source;\n\t\tif (typeof source !== \"string\" || !isWakeSourceKind(source)) {\n\t\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an unknown wake source kind`);\n\t\t}\n\t\tconst description = candidate.description;\n\t\tif (description !== undefined && typeof description !== \"string\") {\n\t\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid wake source description`);\n\t\t}\n\t\treturn {\n\t\t\tsource,\n\t\t\tid: requireString(id, \"wakeSource.id\", candidate.id),\n\t\t\tcreatedAt: requireEpochMs(id, \"wakeSource.createdAt\", candidate.createdAt),\n\t\t\t...(description === undefined ? {} : { description }),\n\t\t};\n\t});\n}\n\nfunction parsePendingWakeup(id: string, value: unknown): PendingWakeup | null {\n\tif (value === null || value === undefined) return null;\n\tif (!isRecord(value)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid pendingWakeup`);\n\tconst source = value.source;\n\tif (source !== \"model\" && source !== \"keepalive\") {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid pendingWakeup source`);\n\t}\n\tif (value.kind !== \"dynamic\") throw new InvalidLoopStoreError(`loop entry ${id} has an invalid pendingWakeup kind`);\n\treturn {\n\t\tid: requireString(id, \"pendingWakeup.id\", value.id),\n\t\tloopId: requireString(id, \"pendingWakeup.loopId\", value.loopId),\n\t\tkind: \"dynamic\",\n\t\tsource,\n\t\trequestedDelaySeconds: requireFiniteNumber(\n\t\t\tid,\n\t\t\t\"pendingWakeup.requestedDelaySeconds\",\n\t\t\tvalue.requestedDelaySeconds,\n\t\t),\n\t\tdelaySeconds: requirePositiveInteger(id, \"pendingWakeup.delaySeconds\", value.delaySeconds),\n\t\tdueAt: requireEpochMs(id, \"pendingWakeup.dueAt\", value.dueAt),\n\t\treason: requireString(id, \"pendingWakeup.reason\", value.reason),\n\t\tprompt: requireString(id, \"pendingWakeup.prompt\", value.prompt),\n\t\tnoop: requireBoolean(id, \"pendingWakeup.noop\", value.noop),\n\t\tcreatedAt: requireEpochMs(id, \"pendingWakeup.createdAt\", value.createdAt),\n\t};\n}\n\nfunction parseRequestedInterval(id: string, value: unknown): RequestedInterval {\n\tif (!isRecord(value)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid requestedInterval`);\n\tconst unit = value.unit;\n\tif (typeof unit !== \"string\" || !isRequestedIntervalUnit(unit)) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid requestedInterval unit`);\n\t}\n\treturn {\n\t\tvalue: requirePositiveInteger(id, \"requestedInterval.value\", value.value),\n\t\tunit,\n\t\traw: requireString(id, \"requestedInterval.raw\", value.raw),\n\t};\n}\n\nfunction parseEffectiveInterval(id: string, value: unknown): EffectiveInterval {\n\tif (!isRecord(value)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid effectiveInterval`);\n\tconst unit = value.unit;\n\tif (typeof unit !== \"string\" || !isEffectiveIntervalUnit(unit)) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid effectiveInterval unit`);\n\t}\n\tconst roundingNotice = value.roundingNotice;\n\tif (roundingNotice !== undefined && typeof roundingNotice !== \"string\") {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid effectiveInterval roundingNotice`);\n\t}\n\treturn {\n\t\tvalue: requirePositiveInteger(id, \"effectiveInterval.value\", value.value),\n\t\tunit,\n\t\thuman: requireString(id, \"effectiveInterval.human\", value.human),\n\t\trounded: requireBoolean(id, \"effectiveInterval.rounded\", value.rounded),\n\t\t...(roundingNotice === undefined ? {} : { roundingNotice }),\n\t};\n}\n\nfunction requireString(id: string, field: string, value: unknown, options?: { allowEmpty?: boolean }): string {\n\tif (typeof value !== \"string\" || (options?.allowEmpty !== true && value.length === 0)) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);\n\t}\n\treturn value;\n}\n\nfunction requireBoolean(id: string, field: string, value: unknown): boolean {\n\tif (typeof value !== \"boolean\") throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);\n\treturn value;\n}\n\nfunction requireFiniteNumber(id: string, field: string, value: unknown): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);\n\t}\n\treturn value;\n}\n\nfunction requireNonNegativeInteger(id: string, field: string, value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);\n\t}\n\treturn value as number;\n}\n\nfunction requirePositiveInteger(id: string, field: string, value: unknown): number {\n\tif (!Number.isSafeInteger(value) || (value as number) <= 0) {\n\t\tthrow new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);\n\t}\n\treturn value as number;\n}\n\nfunction requireEpochMs(id: string, field: string, value: unknown): EpochMs {\n\tif (!isEpochMs(value)) throw new InvalidLoopStoreError(`loop entry ${id} has an invalid ${field}`);\n\treturn value;\n}\n\nfunction parseNullableEpochMs(id: string, field: string, value: unknown): EpochMs | null {\n\tif (value === null || value === undefined) return null;\n\treturn requireEpochMs(id, field, value);\n}\n\nfunction isEpochMs(value: unknown): value is EpochMs {\n\treturn Number.isSafeInteger(value) && (value as number) >= 0;\n}\n\nfunction isLoopPhase(value: string): value is LoopPhase {\n\treturn (LOOP_PHASES as readonly string[]).includes(value);\n}\n\nfunction isLoopEndReason(value: string): value is LoopEndReason {\n\treturn (LOOP_END_REASONS as readonly string[]).includes(value);\n}\n\nfunction isLoopSentinel(value: string): value is LoopSentinel {\n\treturn (LOOP_SENTINELS as readonly string[]).includes(value);\n}\n\nfunction isWakeSourceKind(value: string): value is LoopWakeSourceKind {\n\treturn (LOOP_WAKE_SOURCE_KINDS as readonly string[]).includes(value);\n}\n\nfunction isRequestedIntervalUnit(value: string): value is RequestedIntervalUnit {\n\treturn (REQUESTED_INTERVAL_UNITS as readonly string[]).includes(value);\n}\n\nfunction isEffectiveIntervalUnit(value: string): value is EffectiveIntervalUnit {\n\treturn (EFFECTIVE_INTERVAL_UNITS as readonly string[]).includes(value);\n}\n\nfunction isFileSystemError(error: unknown, code: string): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === code;\n}\n"]}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Tick-prompt templates with cache-friendly sentinel expansion.
3
+ *
4
+ * `buildTickMessage` is pure: it reads no files, touches no clock, and mutates nothing.
5
+ * The caller resolves the loop file (see `loopfile.ts`) and hands the snapshot in; this
6
+ * module decides full-vs-reminder delivery, renders the tick text, and returns the
7
+ * updated sentinel delivery state to persist.
8
+ *
9
+ * Cache discipline: the long instruction blocks are delivered once (the "anchor") and
10
+ * later ticks send a short reminder that points back at that anchored message, so the
11
+ * cached message prefix stays stable across fires.
12
+ */
13
+ import type { DeliveryId, LoopFileFingerprint, LoopId, LoopPayload, LoopSentinel, SentinelDeliveryState } from "./types.ts";
14
+ export type LoopMode = "fixed" | "dynamic";
15
+ /** `prompt` covers a verbatim user prompt; sentinels alternate between full and reminder. */
16
+ export type TickDelivery = "full" | "reminder" | "prompt";
17
+ /** Resolved loop-file state for this fire, produced by the caller's loop-file reader. */
18
+ export type LoopFileSnapshot = {
19
+ readonly present: false;
20
+ } | {
21
+ readonly present: true;
22
+ readonly path: string;
23
+ /** Model-visible (already truncated) representation. */
24
+ readonly content: string;
25
+ readonly mtimeMs: number;
26
+ readonly size: number;
27
+ /** SHA-256 of the model-visible representation. */
28
+ readonly contentHash: string;
29
+ };
30
+ export interface TickPromptInput {
31
+ readonly loopId: LoopId;
32
+ readonly deliveryId: DeliveryId;
33
+ readonly mode: LoopMode;
34
+ readonly payload: LoopPayload;
35
+ /** Canonical re-entry text, e.g. `/loop check the deploy` or `/loop`. */
36
+ readonly reentryPrompt: string;
37
+ readonly deliveryState: SentinelDeliveryState;
38
+ readonly loopFile: LoopFileSnapshot;
39
+ }
40
+ export interface LoopTickDetails {
41
+ readonly loopId: LoopId;
42
+ readonly deliveryId: DeliveryId;
43
+ readonly mode: LoopMode;
44
+ readonly delivery: TickDelivery;
45
+ readonly sentinel?: LoopSentinel;
46
+ readonly loopFile?: LoopFileFingerprint;
47
+ }
48
+ export interface TickPromptResult {
49
+ readonly text: string;
50
+ readonly delivery: TickDelivery;
51
+ readonly details: LoopTickDetails;
52
+ /** Delivery state to persist after this tick is dispatched. */
53
+ readonly deliveryState: SentinelDeliveryState;
54
+ }
55
+ /**
56
+ * Render one tick message plus the delivery state to persist afterwards.
57
+ *
58
+ * Full delivery happens on the first sentinel delivery, when `forceFullDelivery` is set
59
+ * (accepted compaction or a missing anchor on resume), when the loop file's fingerprint
60
+ * changed, or when the file reappeared after absence. Otherwise the tick is a short
61
+ * reminder anchored to the last full message.
62
+ */
63
+ export declare function buildTickMessage(input: TickPromptInput): TickPromptResult;
64
+ //# sourceMappingURL=tick-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tick-prompt.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/loop/tick-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACX,UAAU,EACV,mBAAmB,EACnB,MAAM,EACN,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3C,6FAA6F;AAC7F,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE1D,yFAAyF;AACzF,MAAM,MAAM,gBAAgB,GACzB;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;CAAE,GAC3B;IACA,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEL,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC;CAC9C;AA4LD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CASzE"}